Collect and generate route data from a file-based router such as svelte-kit's
What this does: show / hide
<!-- before -->
<script lang="ts">
import { goto } from '$app/navigation';
goto('/organiztion/org-abc/members/member-123/posts/prefix-too-much-literals-no-safety');
// => 404 because organiztion is a typo
</script>
<!-- after -->
<script lang="ts">
import { goto } from '$app/navigation';
import { AppRoutes, route } from '$generated/routing';
goto(
route(
AppRoutes.organization.$orgId.members.$memberId.posts.prefix$slug,
'org-abc',
'member-123',
'type-support-no-typo',
),
);
</script>
Show / hide
npm install -D roullector
yarn add -D roullector
pnpm install -D roullector
This package was initially built and tested for svelte-kit. But I expect it to work in other cases using the configurable options. Examples used in this docs will use svelte-kit common route setup.
If you are using svelte-kit and want a no lock-in jump start, do, at project root,...
npx roullector collect
...and inspect the src/generated/routing
directory for the generated goodies.
If you have a directory that contains some routes like below...
directory: show / hide
src/routes/
├── __components/
| ├── Navbar.svelte
| └── Footer.svelte
├── __layout.svelte
├── __error.svelte
├── me.svelte
├── sign-in.svelte
├── sign-up.svelte
├── index.svelte
└── admin/
├── __components/
| ├── Admin.layout.svelte
| ├── types.ts
└── users/
├── [id]/
| ├── __layout.svelte
| ├── index.svelte
| ├── types.ts
| └── posts/
| ├── [post_id].svelte
| ├── s-[slug].svelte
| └── l-[short-link]-l.svelte
└── index.svelte
...and a npm script that run roullector
with default options,...
package.json: show/hide
// package.json
{
"scripts": {
"codegen:routing": "roullector collect"
}
}
...the following files will be generated:
Generated: show / hide
// src/generated/routing/routes.json
{
"admin": {
"users": {
"$id": {
"index": "/admin/users/[id]",
"posts": {
"$postId": "/admin/users/[id]/posts/[post_id]",
"l$shortLink$L": "/admin/users/[id]/posts/l-[short-link]-l",
"s$slug": "/admin/users/[id]/posts/s-[slug]",
"__dir": "/admin/users/[id]/posts"
}
},
"index": "/admin/users"
},
"__dir": "/admin"
},
"index": "/",
"me": "/me",
"signIn": "/sign-in",
"signUp": "/sign-up"
}
// src/generated/routing/index.ts
export { default as AppRoutes } from './routes.json';
/**
* build a complete path with injected arguments
* @param path {string} based path
* @param args {string[]} arguments to inject
*/
export function route(path: string, ...args: string[]): string {
const params = path.match(/\[[a-zA-Z_-]+\]/g) ?? [];
for (const i in params) {
path = path.replace(params[i], args[i]);
}
return path;
}
Notice that index.ts
will import json, you need to configure your tsconfig.json
to enable resolveJsonModule
:
tsconfig.json: show / hide
// tsconfig.json
{
"compilerOptions": {
"resolveJsonModule": true
}
}
You can then use the route
helper to construct a complete path with typescript support built-in. For example:
path contruct: show / hide
import { AppRoutes } from '$generated/routing'; // or use relative path
const path = route(AppRoutes.admin.users.$id.posts.s$slug, 'user-id-123', 'slug');
// path = '/admin/users/user-id-123/posts/s-slug'
// ... later
// navigate(path);
Run npx roullector collect help
to for configurable options in command-line mode
Table of Options
name | default | description | cli equivalent |
---|---|---|---|
inDir |
'src/routes' |
input directory path to collect route data from | -i, --inDir |
extensions |
['.svelte'] |
file extensions to accept | -e, --extensions (comma-separated) |
ignorePatterns |
[/^_/] |
patterns to ignore filenames | -x, --ignorePatterns (comma-separated) |
output |
true |
write generated files to disk?, if false will print to console | --no-output |
outDir |
'src/generated/routing' |
output directory | -o, --outDir |
depth |
Infinity |
depth of directory traversal | -d, --depth |
dirkey |
__dir |
key to save path for directories with no index file | -k, --dirkey |
keyTransform |
[dollarArgify(), camelCasify()] |
how to transform route key in mapping | -t, --keyTransform (allows multiple) |
utils |
true |
generate utils for building path? | --no-utils |
typescript |
true |
generate files in typescript? | --no-typescript |
verbose |
false |
print more info during operation (for cli only) | --verbose |
Notes:
- for boolean options (default to
true
), the cli equivalent is--no-<option>
, meaning only add the flag if you want to negate the option - for
ingorePatterns
in cli, provide the regex without/
. Ex:--ignorePatterns=^_
(default)
The rationale for the current default is to enable reference to the mapping without having to do something like AppRoutes['a-kebab-case']['[id']
.
In command-line mode, keyTransform
:
- only accepts these choices:
dollarArg | camelCase | none
, - if you want to specify multiple transforms, provide multiple arguments:
--keyTransform=dollarArg --keyTransform=camelCase
. The transforms will be applied in the order they are provided. - To opt out completely, do
--keyTransform=none
. - See implementation for more details.
show / hide
import {
collect,
defaultCollectOptions,
camelCasify,
dollarArgify,
compose,
} from `roullector`;
console.log('These are default options:', defaultCollectOptions);
let { json, route } = collect(); // use default options
// json = './src/generated/routing/routes.json'
// route = './src/generated/routing/index.ts'
({ json, route } = collect({ output: false; }); // helpful for testing
// json = generated route mapping content
// route = generated index source
const transformers = [dollarArgify, camelCasify];
({ json, route } = collect({
keyTransform: [
(key, original) => compose(key, ...defaultTransformers),
// key = the current key in the transformation pipeline
// original = the original key before all transformation
],
}))