Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Commit

Permalink
feat(command.collect): use custom parser to unify lib & cli options t…
Browse files Browse the repository at this point in the history
…ype, allow array of transformer, and fix type exports (package.json)
  • Loading branch information
vnphanquang committed Mar 10, 2022
1 parent ac9ec40 commit 4855c76
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 92 deletions.
21 changes: 13 additions & 8 deletions README.md
Expand Up @@ -67,7 +67,7 @@ If you have a directory that contains some routes like below...
| └── posts/
| ├── [post_id].svelte
| ├── s-[slug].svelte
| └── l-[short-link].svelte
| └── l-[short-link]-l.svelte
└── index.svelte
```

Expand Down Expand Up @@ -101,12 +101,12 @@ If you have a directory that contains some routes like below...
{
"admin": {
"users": {
"id": {
"$id": {
"index": "/admin/users/[id]",
"posts": {
"postId": "/admin/users/[id]/posts/[post_id]",
"lShortLink": "/admin/users/[id]/posts/l-[short-link]",
"sSlug": "/admin/users/[id]/posts/s-[slug]",
"$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"
}
},
Expand Down Expand Up @@ -166,7 +166,7 @@ You can then use the `route` helper to construct a complete path with typescript
```typescript
import { AppRoutes } from '$generated/routing'; // or use relative path

const path = route(AppRoutes.admin.users.id.posts.sSlug, 'user-id-123', 'slug');
const path = route(AppRoutes.admin.users.$id.posts.s$slug, 'user-id-123', 'slug');
// path = '/admin/users/user-id-123/posts/s-slug'

// ... later
Expand All @@ -192,7 +192,7 @@ Run `npx roullector collect help` to for [configurable options](#options) in com
| `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` | `camelCasify` | how to transform route key in mapping | -t, --keyTransform |
| `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 |
Expand All @@ -202,7 +202,12 @@ Run `npx roullector collect help` to for [configurable options](#options) in com

Notes:

- in command-line mode, `keyTransform` only accepts one of `camelCase | none`. See [implementation for more details][roullector.collect.constants].
- in command-line mode, `keyTransform`
- only accepts these choices: `dollarArg | camelCase | none`,
- if you want to specify multiple transforms, provide multiple argument: `--keyTransform=dollarArg --keyTransform=camelCase`,
- The rationale for the current default is to enable reference 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][roullector.collect.constants].
- 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
Expand Down
2 changes: 1 addition & 1 deletion __tests__/fixtures/memfs.ts
Expand Up @@ -22,7 +22,7 @@ export const FILES = {
'posts': {
'[post_id].svelte': '',
's-[slug].svelte': '',
'l-[short-link].svelte': '',
'l-[short-link]-l.svelte': '',
},
},
'index.svelte': '',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -35,7 +35,7 @@
"lint:es": "eslint src --ext .ts",
"prettier": "prettier --check 'src/*.{ts,js,json}'",
"prebuild": "run-p lint:es prettier",
"build": "pnpm prebuild && rollup -c",
"build": "rollup -c",
"test": "jest",
"test:ci": "jest --ci --json --coverage --testLocationInResults --outputFile=coverage/report.json",
"semantic-release": "semantic-release"
Expand Down
11 changes: 6 additions & 5 deletions rollup.config.js
Expand Up @@ -12,12 +12,13 @@ import typescript from 'rollup-plugin-typescript2';

import pkg from './package.json';

const plugins = [
const plugins = (declaration = false) => ([
typescript({
typescript: require('ttypescript'),
tsconfig: resolve(__dirname, 'tsconfig.build.json'),
tsconfigDefaults: {
compilerOptions: {
declaration,
plugins: [
{ transform: 'typescript-transform-paths' },
{ transform: 'typescript-transform-paths', afterDeclarations: true },
Expand All @@ -29,7 +30,7 @@ const plugins = [
commonjs(),
json(),
filesize(),
];
]);

const external = [];

Expand All @@ -45,7 +46,7 @@ const config = [
},
external,
plugins: [
...plugins,
...plugins(),
copy({
targets: [
{
Expand All @@ -65,7 +66,7 @@ const config = [
},
external,
plugins: [
...plugins,
...plugins(true),
copy({
targets: [
{
Expand All @@ -85,7 +86,7 @@ const config = [
sourcemap: true,
},
external,
plugins,
plugins: plugins(),
},
];

Expand Down
8 changes: 4 additions & 4 deletions src/commands/collect/__snapshots__/collect.cli.test.ts.snap
Expand Up @@ -5,12 +5,12 @@ Object {
"json": "{
\\"admin\\": {
\\"users\\": {
\\"id\\": {
\\"$id\\": {
\\"index\\": \\"/admin/users/[id]\\",
\\"posts\\": {
\\"postId\\": \\"/admin/users/[id]/posts/[post_id]\\",
\\"lShortLink\\": \\"/admin/users/[id]/posts/l-[short-link]\\",
\\"sSlug\\": \\"/admin/users/[id]/posts/s-[slug]\\",
\\"$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\\"
}
},
Expand Down
58 changes: 35 additions & 23 deletions src/commands/collect/collect.cli.ts
@@ -1,88 +1,100 @@
import {
Command,
InvalidArgumentError,
Option,
} from 'commander';

import { collect } from '$commands/collect/collect';
import {
defaultCliCollectOptions,
keyTransformCliToFunc,
defaultCollectOptions,
defaultKeyTransformCli,
} from '$commands/collect/collect.constants';
import type {
CollectOptions,
CliCollectOptions,
KeyTransformFn,
} from '$commands/collect/collect.types';
import { KeyTransformCli } from '$commands/collect/collect.types';
import {
commaSeparatedList,
number,
} from '$utils/parser';

export function collectCli() {
return new Command('collect')
.description('collect route data')
.option(
'-i, --inDir <path>',
'directory path to collect',
defaultCliCollectOptions.inDir
defaultCollectOptions.inDir
)
.option(
'-e, --extensions <list>',
'comma-separated list of file extensions to collect',
defaultCliCollectOptions.extensions,
commaSeparatedList,
defaultCollectOptions.extensions,
)
.option(
'-x, --ignorePatterns <list>',
'comma-separated list of filename patterns (string literal or regex) to ignore',
defaultCliCollectOptions.ignorePatterns,
commaSeparatedList,
defaultCollectOptions.ignorePatterns,
)
.option(
'--no-output',
'don\'t write to disk but print to console instead',
defaultCliCollectOptions.output,
defaultCollectOptions.output,
)
.option(
'-o, --outDir <path>',
'outputs generated files to this folder',
defaultCliCollectOptions.outDir,
defaultCollectOptions.outDir,
)
.option(
'-d, --depth <number>',
'depth of inDir to collect',
defaultCliCollectOptions.depth,
number,
defaultCollectOptions.depth,
)
.addOption(
new Option( '-t, --keyTransform <variant>', 'how to transform route key')
.choices(['none', 'camelCase'])
.default(defaultCliCollectOptions.keyTransform)
.default([])
.argParser<KeyTransformFn[]>((value, previous) => {
if (!(value in KeyTransformCli)) {
throw new InvalidArgumentError(`Must be one of: ${Object.keys(KeyTransformCli).join(', ')}`);
}
const valued = keyTransformCliToFunc[KeyTransformCli[value] as KeyTransformCli];
return [...previous, valued];
}),
)
.option(
'-k, --dirkey <name>',
'how to transform route key',
defaultCliCollectOptions.dirkey,
defaultCollectOptions.dirkey,
)
.option(
'--no-utils',
'don\'t generate utils for building path with arguments',
defaultCliCollectOptions.utils,
defaultCollectOptions.utils,
)
.option(
'--no-typescript',
'outputs files in javascript instead of typescript',
defaultCliCollectOptions.typescript,
defaultCollectOptions.typescript,
)
.option(
'--verbose',
'prints more info during operation',
defaultCliCollectOptions.verbose,
defaultCollectOptions.verbose,
)
.action((rawOptions: CliCollectOptions) => {
const options: CollectOptions = {
...rawOptions,
extensions: rawOptions.extensions?.split(',') ?? [],
ignorePatterns: rawOptions.ignorePatterns?.split(',') ?? [],
depth: Number(rawOptions.depth) || Infinity,
keyTransform: keyTransformCliToFunc[rawOptions.keyTransform],
};
.action((options: CollectOptions) => {
if (options.keyTransform.length === 0) {
options.keyTransform = defaultKeyTransformCli.map((k) => keyTransformCliToFunc[k]);
}
if (options.verbose) {
console.info('Running "collect" with options:');
console.table(Object.fromEntries(
Object.entries(rawOptions).map(
Object.entries(options).map(
([key, value]) => ([
key,
{ value },
Expand Down
39 changes: 14 additions & 25 deletions src/commands/collect/collect.constants.ts
@@ -1,48 +1,37 @@
import type {
CliCollectOptions,
CollectOptions,
KeyTransformFn
} from '$commands/collect/collect.types';
import {KeyTransformCli} from '$commands/collect/collect.types';
import {
camelCasify,
dollarArgify,
} from '$utils/transformer';

export const MAPPING_FILENAME = 'routes.json';
export const UTIL_ROUTE_FILENAME = 'route';

export function camelCasify(str: string) {
return str
.trim()
.replace(/\[([a-zA-Z_-]+)\]/g, function(match, p1) {
return match.replace(/\[[a-zA-Z_-]+\]/g, `-${p1}`);
})
.replace(/[-_\s]+./g, function(match) {
return match.replace(/[-_\s]/g, '').toUpperCase();
})
.replace(/^(.)/, function(match) {
return match.toLowerCase();
});
}

export const defaultCollectOptions: CollectOptions = {
inDir: 'src/routes',
extensions: ['.svelte'],
ignorePatterns: [/^_/],
output: true,
outDir: 'src/generated/routing',
typescript: true,
keyTransform: camelCasify,
keyTransform: [dollarArgify, camelCasify],
verbose: false,
depth: Infinity,
dirkey: '__dir',
utils: true,
};

export const defaultCliCollectOptions: CliCollectOptions = {
...defaultCollectOptions,
extensions: defaultCollectOptions.extensions.join(','),
ignorePatterns: defaultCollectOptions.ignorePatterns.map((pattern) => (pattern instanceof RegExp) ? pattern.source : pattern).join(','),
depth: 'Infinity',
keyTransform: 'camelCase',
};
export const defaultKeyTransformCli: KeyTransformCli[] = [
KeyTransformCli.camelCase,
KeyTransformCli.dollarArg,
];

export const keyTransformCliToFunc: Record<CliCollectOptions['keyTransform'], (str) => string> = {
camelCase: camelCasify,
export const keyTransformCliToFunc: Record<KeyTransformCli, KeyTransformFn> = {
none: (str) => str,
camelCase: camelCasify,
dollarArg: dollarArgify,
};
17 changes: 8 additions & 9 deletions src/commands/collect/collect.types.ts
@@ -1,5 +1,3 @@
type Modify<T, R> = Omit<T, keyof R> & R;

export type CollectOptions = {
/** input directory path to collect route data from */
inDir: string;
Expand All @@ -14,7 +12,7 @@ export type CollectOptions = {
/** prints more info during operation */
verbose: boolean;
/** how to transform route key */
keyTransform?: (key: string) => string;
keyTransform: KeyTransformFn[];
/** depth of inDir to collect */
depth: number;
/** key to save path for directories with no index file */
Expand All @@ -32,9 +30,10 @@ export type CollectOutput = {
route: null|string;
};

export type CliCollectOptions = Modify<CollectOptions, {
depth: string;
extensions?: string;
ignorePatterns?: string;
keyTransform: 'none' | 'camelCase';
}>;
export enum KeyTransformCli {
none = 'none',
camelCase = 'camelCase',
dollarArg = 'dollarArg',
}

export type KeyTransformFn = (key: string, original: string) => string;

0 comments on commit 4855c76

Please sign in to comment.