From 3324bfb572a6508169c472f320b0c5248274a828 Mon Sep 17 00:00:00 2001 From: Wei Zhu Date: Fri, 20 Oct 2023 22:23:46 +1030 Subject: [PATCH] feat: introduce strict mode (#66) address #57 --- .changeset/gentle-ears-peel.md | 5 ++++ README.md | 1 + .../__tests__/__snapshots__/cli.test.ts.snap | 8 ++++-- .../remix-routes/src/__tests__/cli.test.ts | 4 +-- packages/remix-routes/src/cli.ts | 26 ++++++++++++------- packages/remix-routes/src/template.ts | 6 ++++- 6 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 .changeset/gentle-ears-peel.md diff --git a/.changeset/gentle-ears-peel.md b/.changeset/gentle-ears-peel.md new file mode 100644 index 00000000..edea1ef7 --- /dev/null +++ b/.changeset/gentle-ears-peel.md @@ -0,0 +1,5 @@ +--- +"remix-routes": minor +--- + +feat: introduce strict mode diff --git a/README.md b/README.md index 5657cb92..8307834e 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ export default function Component() { ## Command Line Options - `-w`: Watch for changes and automatically rebuild. +- `-s`: Enale strict mode. In strict mode only routes that define `SearchParams` type are allowed to have query string. - `-o`: Specify the output path for `remix-routes.d.ts`. Defaults to `./node_modules` if arg is not given. ## TypeScript Integration diff --git a/packages/remix-routes/src/__tests__/__snapshots__/cli.test.ts.snap b/packages/remix-routes/src/__tests__/__snapshots__/cli.test.ts.snap index 19e92626..0f7f3c21 100644 --- a/packages/remix-routes/src/__tests__/__snapshots__/cli.test.ts.snap +++ b/packages/remix-routes/src/__tests__/__snapshots__/cli.test.ts.snap @@ -5,7 +5,9 @@ exports[`build v1 routes 1`] = ` type URLSearchParamsInit = string | string[][] | Record | URLSearchParams; // symbol won't be a key of SearchParams type IsSearchParams = symbol extends keyof T ? false : true; - type ExportedQuery = IsSearchParams extends true ? T : URLSearchParamsInit; + + type ExportedQuery = IsSearchParams extends true ? T : URLSearchParamsInit; + export interface Routes { @@ -229,7 +231,9 @@ exports[`build v2 routes 1`] = ` type URLSearchParamsInit = string | string[][] | Record | URLSearchParams; // symbol won't be a key of SearchParams type IsSearchParams = symbol extends keyof T ? false : true; - type ExportedQuery = IsSearchParams extends true ? T : URLSearchParamsInit; + + type ExportedQuery = IsSearchParams extends true ? T : URLSearchParamsInit; + export interface Routes { diff --git a/packages/remix-routes/src/__tests__/cli.test.ts b/packages/remix-routes/src/__tests__/cli.test.ts index 8b3ba6ba..fa5e8fc7 100644 --- a/packages/remix-routes/src/__tests__/cli.test.ts +++ b/packages/remix-routes/src/__tests__/cli.test.ts @@ -4,14 +4,14 @@ import * as path from 'path'; import { build } from '../cli'; test('build v1 routes', async () => { - await build(path.resolve(__dirname, '../../fixture/v1'), './node_modules'); + await build(path.resolve(__dirname, '../../fixture/v1'), { outputDirPath: './node_modules', watch: false, strict: false }); expect( fs.readFileSync(path.resolve(__dirname, '../../fixture/v1/node_modules/remix-routes.d.ts'), 'utf8'), ).toMatchSnapshot(); }); test('build v2 routes', async () => { - await build(path.resolve(__dirname, '../../fixture/v2'), './node_modules'); + await build(path.resolve(__dirname, '../../fixture/v2'), { outputDirPath: './node_modules', watch: false, strict: false }); expect( fs.readFileSync(path.resolve(__dirname, '../../fixture/v2/node_modules/remix-routes.d.ts'), 'utf8'), ).toMatchSnapshot(); diff --git a/packages/remix-routes/src/cli.ts b/packages/remix-routes/src/cli.ts index c882f350..e0c5d75b 100644 --- a/packages/remix-routes/src/cli.ts +++ b/packages/remix-routes/src/cli.ts @@ -23,6 +23,7 @@ $ remix-routes Options --watch, -w Watch for routes changes +--strict, -s Enable strict mode --outputDirPath, -o Specify the output path for "remix-routes.d.ts". Defaults to "./node_modules" if arg is not given. `; @@ -34,6 +35,10 @@ const cli = meow(helpText, { type: 'boolean', alias: 'w', }, + strict: { + type: 'boolean', + alias: 's', + }, outputDirPath: { type: 'string', alias: 'o', @@ -83,26 +88,27 @@ async function buildHelpers(remixRoot: string): Promise { return routesInfo; } -export async function build(remixRoot: string, outputDirPath: string) { +export async function build(remixRoot: string, flags: typeof cli.flags) { const routesInfo = await buildHelpers(remixRoot); - generate(routesInfo, remixRoot, outputDirPath); + generate(routesInfo, remixRoot, flags); } -function watch(remixRoot: string, outputDirPath: string) { - build(remixRoot, outputDirPath); +function watch(remixRoot: string, flags: typeof cli.flags) { + build(remixRoot, flags); chokidar .watch([ path.join(remixRoot, 'app/routes/**/*.{ts,tsx}'), path.join(remixRoot, 'remix.config.js'), ]) .on('change', () => { - build(remixRoot, outputDirPath); + build(remixRoot, flags); }); console.log('Watching for routes changes...'); } -function generate(routesInfo: RoutesInfo, remixRoot: string, outputDirPath: string) { +function generate(routesInfo: RoutesInfo, remixRoot: string, flags: typeof cli.flags) { const tsCode = ejs.render(template, { + strictMode: flags.strict, routes: Object.entries(routesInfo).map(([route, { fileName, params }]) => ({ route, params, @@ -112,7 +118,7 @@ function generate(routesInfo: RoutesInfo, remixRoot: string, outputDirPath: stri const outputPath = path.join( remixRoot, - outputDirPath, + flags.outputDirPath, ); if (!fs.existsSync(outputPath)) { @@ -141,10 +147,12 @@ if (require.main === module) { (async function () { const remixRoot = process.env.REMIX_ROOT || process.cwd(); + console.log(cli.flags); + if (cli.flags.watch) { - watch(remixRoot, cli.flags.outputDirPath); + watch(remixRoot, cli.flags); } else { - build(remixRoot, cli.flags.outputDirPath); + build(remixRoot, cli.flags); } })(); } diff --git a/packages/remix-routes/src/template.ts b/packages/remix-routes/src/template.ts index 91ebf25b..712881f1 100644 --- a/packages/remix-routes/src/template.ts +++ b/packages/remix-routes/src/template.ts @@ -2,7 +2,11 @@ export const template = `declare module "remix-routes" { type URLSearchParamsInit = string | string[][] | Record | URLSearchParams; // symbol won't be a key of SearchParams type IsSearchParams = symbol extends keyof T ? false : true; - type ExportedQuery = IsSearchParams extends true ? T : URLSearchParamsInit; + <% if (strictMode) { %> + type ExportedQuery = IsSearchParams extends true ? T : never; + <% } else { %> + type ExportedQuery = IsSearchParams extends true ? T : URLSearchParamsInit; + <% } %> export interface Routes { <% routes.forEach(({ route, params, fileName }) => { %>