roullector
v1.1.2
Published
collect and generate map from file-based routers
Downloads
2
Maintainers
Readme
roullector: route collector
Collect and generate route data from a file-based router such as svelte-kit's
<!-- 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>
Table of Contents
Installation
npm install -D roullector
yarn add --dev roullector
pnpm install -D roullector
Usage
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.
Quick Start
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.
Example
If you have a directory that contains some routes like below...
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
{
"scripts": {
"codegen:routing": "roullector collect"
}
}
...the following files will be generated:
// 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
{
"compilerOptions": {
"resolveJsonModule": true
}
}
You can then use the route
helper to construct a complete path with typescript support built-in. For example:
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);
Options
Run npx roullector collect help
to for configurable options in command-line mode
| 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:
- 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. - The rationale for the current default is to enable reference to the mapping without having to do something like
AppRoutes['a-kebab-case']['[id']
. - To opt out completely, do
--keyTransform=none
. - See implementation for more details.
- only accepts these choices:
- for boolean options (default to
true
), the cli equivalent is--no-<option>
, meaning only add the flag if you want to negate the option.
Library Usage
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
],
}))