nuxt-typed-router 2.0.3 → 2.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,11 +18,11 @@
18
18
 
19
19
  ## Provide a type safe router to Nuxt with auto-generated typed definitions for route names and autocompletion for route params
20
20
 
21
- - Automaticaly provides types check and autocomplete to `NuxtLink`
22
- - Exports a `useTypedRouter` and `useTypedRoute` composable
23
- - Supports routes defined in `config.extendRoutes`
24
- - Infer route params based on route name
25
- - Provide global `$typedRouter` util
21
+ - Provides types check and autocomplete to `NuxtLink`
22
+ - Provides types check for `useRouter` and `useRoute`
23
+ - Supports routes defined in `config.extendRoutes`
24
+ - Supports optional params and catchAll routes
25
+ - Infer route params based on route name
26
26
 
27
27
  <br/>
28
28
 
package/dist/module.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "nuxt": "^3.0.0-rc.1",
6
6
  "bridge": false
7
7
  },
8
- "version": "2.0.3"
8
+ "version": "2.1.0-beta.1"
9
9
  }
package/dist/module.mjs CHANGED
@@ -26,125 +26,8 @@ import type {
26
26
  TypedRouteParams,
27
27
  ResolvedTypedRouteNamedMapper,
28
28
  } from './__routes';
29
- `;
30
-
31
- const staticTypeUtils = `
32
- // Type utils
33
- type ExtractRequiredParameters<T extends Record<string, any>> = Pick<
34
- T,
35
- { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]
36
- >;
37
-
38
- type HasOneRequiredParameter<T extends TypedRouteList> = [TypedRouteParams[T]] extends [never]
39
- ? false
40
- : [keyof ExtractRequiredParameters<TypedRouteParams[T]>] extends [undefined]
41
- ? false
42
- : true;
43
-
44
- type TypedLocationAsRelativeRaw<T extends TypedRouteList> = {
45
- name?: T;
46
- } & ([TypedRouteParams[T]] extends [never]
47
- ? {}
48
- : HasOneRequiredParameter<T> extends false
49
- ? { params?: TypedRouteParams[T] }
50
- : { params: TypedRouteParams[T] });
51
-
52
- type ResolvedTypedLocationAsRelativeRaw<T extends TypedRouteList> = {
53
- name?: T;
54
- } & ([TypedRouteParams[T]] extends [never] ? {} : { params: Required<TypedRouteParams[T]> });
55
-
56
- type TypedRouteLocationRaw = RouteQueryAndHash & TypedRouteNamedMapper & RouteLocationOptions;
57
-
58
- type _TypedRoute = Omit<RouteLocationNormalizedLoaded, 'name' | 'params'> &
59
- ResolvedTypedRouteNamedMapper;
60
- type _TypedNamedRoute<T extends TypedRouteList> = Omit<
61
- RouteLocationNormalizedLoaded,
62
- 'name' | 'params'
63
- > &
64
- ResolvedTypedLocationAsRelativeRaw<T>;
65
-
66
- /** Augmented Router interface */
67
- interface _TypedRouter
68
- extends Omit<Router, 'removeRoute' | 'hasRoute' | 'resolve' | 'push' | 'replace'> {
69
- /**
70
- * Remove an existing route by its name.
71
- *
72
- * @param name - Name of the route to remove
73
- */
74
- removeRoute(name: TypedRouteList): void;
75
- /**
76
- * Checks if a route with a given name exists
77
- *
78
- * @param name - Name of the route to check
79
- */
80
- hasRoute(name: TypedRouteList): boolean;
81
- /**
82
- * Returns the {@link RouteLocation | normalized version} of a
83
- * {@link RouteLocationRaw | route location}. Also includes an \`href\` property
84
- * that includes any existing \`base\`. By default the \`currentLocation\` used is
85
- * \`route.currentRoute\` and should only be overriden in advanced use cases.
86
- *
87
- * @param to - Raw route location to resolve
88
- * @param currentLocation - Optional current location to resolve against
89
- */
90
- resolve(
91
- to: TypedRouteLocationRaw,
92
- currentLocation?: RouteLocationNormalizedLoaded
93
- ): RouteLocation & {
94
- href: string;
95
- };
96
- /**
97
- * Programmatically navigate to a new URL by pushing an entry in the history
98
- * stack.
99
- *
100
- * @param to - Route location to navigate to
101
- */
102
- push(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
103
- /**
104
- * Programmatically navigate to a new URL by replacing the current entry in
105
- * the history stack.
106
- *
107
- * @param to - Route location to navigate to
108
- */
109
- replace(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
110
- }
111
-
112
- export interface TypedRouter extends _TypedRouter {}
113
- export type TypedRoute = _TypedRoute;
114
- export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
115
-
116
- declare global {
117
- export interface TypedRouter extends _TypedRouter {}
118
- export type TypedRoute = _TypedRoute;
119
- export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
120
- }
121
-
122
- type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
123
- to: string | Omit<Exclude<RouteLocationRaw, string>, 'name'> & TypedRouteNamedMapper;
124
- };
125
-
126
- type _NuxtLink = DefineComponent<
127
- TypedNuxtLinkProps,
128
- {},
129
- {},
130
- import('vue').ComputedOptions,
131
- import('vue').MethodOptions,
132
- import('vue').ComponentOptionsMixin,
133
- import('vue').ComponentOptionsMixin,
134
- {},
135
- string,
136
- import('vue').VNodeProps &
137
- import('vue').AllowedComponentProps &
138
- import('vue').ComponentCustomProps,
139
- Readonly<TypedNuxtLinkProps>,
140
- {}
141
- >;
142
-
143
- declare module '@vue/runtime-core' {
144
- export interface GlobalComponents {
145
- NuxtLink: _NuxtLink;
146
- }
147
- }
29
+ import {useTypedRoute} from './__useTypedRoute';
30
+ import {useTypedRouter} from './__useTypedRouter';
148
31
  `;
149
32
 
150
33
  const watermarkTemplate = `
@@ -158,13 +41,138 @@ const watermarkTemplate = `
158
41
 
159
42
  `;
160
43
 
161
- function createDeclarationRoutesFile() {
44
+ function createRuntimeTypeUtils(autoImport) {
45
+ return `
46
+ // Type utils
47
+ type ExtractRequiredParameters<T extends Record<string, any>> = Pick<
48
+ T,
49
+ { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]
50
+ >;
51
+
52
+ type HasOneRequiredParameter<T extends TypedRouteList> = [TypedRouteParams[T]] extends [never]
53
+ ? false
54
+ : [keyof ExtractRequiredParameters<TypedRouteParams[T]>] extends [undefined]
55
+ ? false
56
+ : true;
57
+
58
+ type TypedLocationAsRelativeRaw<T extends TypedRouteList> = {
59
+ name?: T;
60
+ } & ([TypedRouteParams[T]] extends [never]
61
+ ? {}
62
+ : HasOneRequiredParameter<T> extends false
63
+ ? { params?: TypedRouteParams[T] }
64
+ : { params: TypedRouteParams[T] });
65
+
66
+ type ResolvedTypedLocationAsRelativeRaw<T extends TypedRouteList> = {
67
+ name?: T;
68
+ } & ([TypedRouteParams[T]] extends [never] ? {} : { params: TypedRouteParams[T] });
69
+
70
+ type TypedRouteLocationRaw = RouteQueryAndHash & TypedRouteNamedMapper & RouteLocationOptions;
71
+
72
+ type _TypedRoute = Omit<RouteLocationNormalizedLoaded, 'name' | 'params'> &
73
+ ResolvedTypedRouteNamedMapper;
74
+ type _TypedNamedRoute<T extends TypedRouteList> = Omit<
75
+ RouteLocationNormalizedLoaded,
76
+ 'name' | 'params'
77
+ > &
78
+ ResolvedTypedLocationAsRelativeRaw<T>;
79
+
80
+ /** Augmented Router interface */
81
+ interface _TypedRouter
82
+ extends Omit<Router, 'removeRoute' | 'hasRoute' | 'resolve' | 'push' | 'replace', 'currentRoute'> {
83
+ readonly currentRoute: _TypedRoute;
84
+ /**
85
+ * Remove an existing route by its name.
86
+ *
87
+ * @param name - Name of the route to remove
88
+ */
89
+ removeRoute(name: TypedRouteList): void;
90
+ /**
91
+ * Checks if a route with a given name exists
92
+ *
93
+ * @param name - Name of the route to check
94
+ */
95
+ hasRoute(name: TypedRouteList): boolean;
96
+ /**
97
+ * Returns the {@link RouteLocation | normalized version} of a
98
+ * {@link RouteLocationRaw | route location}. Also includes an \`href\` property
99
+ * that includes any existing \`base\`. By default the \`currentLocation\` used is
100
+ * \`route.currentRoute\` and should only be overriden in advanced use cases.
101
+ *
102
+ * @param to - Raw route location to resolve
103
+ * @param currentLocation - Optional current location to resolve against
104
+ */
105
+ resolve(
106
+ to: TypedRouteLocationRaw,
107
+ currentLocation?: RouteLocationNormalizedLoaded
108
+ ): RouteLocation & {
109
+ href: string;
110
+ };
111
+ /**
112
+ * Programmatically navigate to a new URL by pushing an entry in the history
113
+ * stack.
114
+ *
115
+ * @param to - Route location to navigate to
116
+ */
117
+ push(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
118
+ /**
119
+ * Programmatically navigate to a new URL by replacing the current entry in
120
+ * the history stack.
121
+ *
122
+ * @param to - Route location to navigate to
123
+ */
124
+ replace(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
125
+ }
126
+
127
+ export interface TypedRouter extends _TypedRouter {}
128
+ export type TypedRoute = _TypedRoute;
129
+ export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
130
+
131
+ declare global {
132
+ export interface TypedRouter extends _TypedRouter {}
133
+ export type TypedRoute = _TypedRoute;
134
+ export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
135
+
136
+ ${autoImport ? `const useRoute: typeof useTypedRoute;
137
+ const useRouter: typeof useTypedRouter;` : ""}
138
+ }
139
+
140
+ type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
141
+ to: string | Omit<Exclude<RouteLocationRaw, string>, 'name'> & TypedRouteNamedMapper;
142
+ };
143
+
144
+ type _NuxtLink = DefineComponent<
145
+ TypedNuxtLinkProps,
146
+ {},
147
+ {},
148
+ import('vue').ComputedOptions,
149
+ import('vue').MethodOptions,
150
+ import('vue').ComponentOptionsMixin,
151
+ import('vue').ComponentOptionsMixin,
152
+ {},
153
+ string,
154
+ import('vue').VNodeProps &
155
+ import('vue').AllowedComponentProps &
156
+ import('vue').ComponentCustomProps,
157
+ Readonly<TypedNuxtLinkProps>,
158
+ {}
159
+ >;
160
+
161
+ declare module '@vue/runtime-core' {
162
+ export interface GlobalComponents {
163
+ NuxtLink: _NuxtLink;
164
+ }
165
+ }
166
+ `;
167
+ }
168
+
169
+ function createDeclarationRoutesFile(autoImport) {
162
170
  return `
163
171
  ${watermarkTemplate}
164
172
 
165
173
  ${staticTypesImports}
166
174
 
167
- ${staticTypeUtils}
175
+ ${createRuntimeTypeUtils(autoImport)}
168
176
  `;
169
177
  }
170
178
 
@@ -173,8 +181,8 @@ function createRuntimeIndexFile() {
173
181
  ${watermarkTemplate}
174
182
  export { routesNames } from './__routes';
175
183
  export type { TypedRouteList } from './__routes';
176
- export * from './__useTypedRouter';
177
- export * from './__useTypedRoute';
184
+ export {useRouter} from './__useTypedRouter';
185
+ export {useRoute} from './__useTypedRoute';
178
186
  `;
179
187
  }
180
188
 
@@ -215,7 +223,9 @@ function createTypedRouteNamedMapperExport(routesParams) {
215
223
  return `export type TypedRouteNamedMapper =
216
224
  ${routesParams.map(
217
225
  ({ name, params }) => `{name: "${name}" ${params.length ? `, params${params.some((s) => s.required) ? "" : "?"}: {
218
- ${params.map(({ key, required }) => `"${key}"${required ? "" : "?"}: string | number`).join(",\n")}
226
+ ${params.map(
227
+ ({ key, required, catchAll }) => `"${key}"${required ? "" : "?"}: (string | number)${catchAll ? "[]" : ""}`
228
+ ).join(",\n")}
219
229
  }` : ""}}`
220
230
  ).join("|\n")}
221
231
  `;
@@ -229,7 +239,7 @@ function createResolvedTypedRouteNamedMapperExport(routesParams) {
229
239
  ${routesParams.map(
230
240
  ({ name, params }) => `{name: "${name}" ${params.length ? `, params: {
231
241
  ${params.map(
232
- ({ key, notRequiredOnPage }) => `"${key}"${notRequiredOnPage ? "?" : ""}: string`
242
+ ({ key, notRequiredOnPage, catchAll }) => `"${key}"${notRequiredOnPage ? "?" : ""}: string${catchAll ? "[]" : ""}`
233
243
  ).join(",\n")}
234
244
  }` : ""}}`
235
245
  ).join("|\n")}
@@ -302,7 +312,9 @@ export function useTypedRoute<T extends TypedRouteList = never>(
302
312
  return route as any;
303
313
  }
304
314
 
305
- `;
315
+ export const useRoute = useTypedRoute;
316
+
317
+ `;
306
318
  }
307
319
 
308
320
  function createRuntimeUseTypedRouterFile(routesDeclTemplate) {
@@ -325,6 +337,7 @@ function createRuntimeUseTypedRouterFile(routesDeclTemplate) {
325
337
  return router;
326
338
  };
327
339
 
340
+ export const useRouter = useTypedRouter;
328
341
  `;
329
342
  }
330
343
 
@@ -409,6 +422,7 @@ function handlePluginFileSave({
409
422
  let previousGeneratedRoutes = "";
410
423
  async function saveGeneratedFiles({
411
424
  rootDir,
425
+ autoImport,
412
426
  outputData: { routesDeclTemplate, routesList, routesObjectTemplate, routesParams }
413
427
  }) {
414
428
  const filesMap = [
@@ -431,7 +445,7 @@ async function saveGeneratedFiles({
431
445
  },
432
446
  {
433
447
  fileName: `typed-router.d.ts`,
434
- content: createDeclarationRoutesFile()
448
+ content: createDeclarationRoutesFile(autoImport)
435
449
  },
436
450
  {
437
451
  fileName: "index.ts",
@@ -443,7 +457,7 @@ async function saveGeneratedFiles({
443
457
  );
444
458
  if (previousGeneratedRoutes !== routesList.join(",")) {
445
459
  previousGeneratedRoutes = routesList.join(",");
446
- console.log(logSymbols.success, `[typed-router] Routes definitions generated`);
460
+ console.log(logSymbols.success, `Router autocompletions generated \u{1F6A6}`);
447
461
  }
448
462
  }
449
463
 
@@ -451,24 +465,25 @@ function isItemLast(array, index) {
451
465
  return array ? index === array.length - 1 : false;
452
466
  }
453
467
 
454
- const routeParamExtractRegxp = /(:(\w+)(\?)?)+/g;
468
+ const routeParamExtractRegxp = /(:(\w+)(\(.+\)[*+]?)?(\?)?)+/g;
455
469
  function extractRouteParamsFromPath(path, isIndexFileForRouting, previousParams) {
456
470
  let params = [];
457
471
  let matches;
458
472
  do {
459
473
  matches = routeParamExtractRegxp.exec(path);
460
474
  if (matches) {
461
- const [_, mtch, key, optional] = matches;
475
+ const [_, mtch, key, catchAll, optional] = matches;
462
476
  if (mtch) {
463
- params.push({ name: key, optional: !!optional });
477
+ params.push({ name: key, optional: !!optional, catchAll: !!catchAll });
464
478
  }
465
479
  }
466
480
  } while (matches);
467
481
  let allMergedParams = params.map(
468
- ({ name, optional }) => ({
482
+ ({ name, optional, catchAll }) => ({
469
483
  key: name,
470
484
  required: !optional,
471
- notRequiredOnPage: optional
485
+ notRequiredOnPage: optional,
486
+ catchAll
472
487
  })
473
488
  );
474
489
  if (previousParams?.length) {
@@ -602,8 +617,10 @@ function startGenerator({ output, routesConfig }) {
602
617
  output.routesDeclTemplate += "}";
603
618
  }
604
619
 
605
- function createTypedRouter({ rootDir, plugin, nuxt }) {
620
+ function createTypedRouter({ plugin, nuxt }) {
606
621
  try {
622
+ const rootDir = nuxt.options.rootDir;
623
+ const autoImport = nuxt.options.imports.autoImport ?? true;
607
624
  extendPages(async (routes) => {
608
625
  if (routes.length) {
609
626
  const outputData = constructRouteMap(routes);
@@ -615,6 +632,7 @@ function createTypedRouter({ rootDir, plugin, nuxt }) {
615
632
  });
616
633
  }
617
634
  await saveGeneratedFiles({
635
+ autoImport,
618
636
  rootDir,
619
637
  outputData
620
638
  });
@@ -651,8 +669,12 @@ const module = defineNuxtModule({
651
669
  ...nuxt.options.alias,
652
670
  "@typed-router": resolve(`${rootDir}/.nuxt/typed-router`)
653
671
  };
654
- nuxt.hook("pages:extend", () => createTypedRouter({ rootDir, nuxt, plugin }));
655
- createTypedRouter({ rootDir, nuxt, plugin });
672
+ nuxt.options.typescript.tsConfig = {
673
+ include: ["./typed-router/typed-router.d.ts"]
674
+ };
675
+ const typedRouterOptions = { nuxt, plugin };
676
+ nuxt.hook("pages:extend", () => createTypedRouter(typedRouterOptions));
677
+ createTypedRouter(typedRouterOptions);
656
678
  }
657
679
  });
658
680
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-typed-router",
3
- "version": "2.0.3",
3
+ "version": "2.1.0-beta.1",
4
4
  "description": "Provide autocompletion for pages route names generated by Nuxt router",
5
5
  "type": "module",
6
6
  "main": "./dist/module.cjs",
@@ -59,10 +59,11 @@
59
59
  "log-symbols": "^5.1.0",
60
60
  "mkdirp": "^1.0.4",
61
61
  "pathe": "1.0.0",
62
- "prettier": "2.8.1"
62
+ "prettier": "2.8.3"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@nuxt/module-builder": "^0.2.1",
66
+ "@nuxt/test-utils": "^3.0.0",
66
67
  "@nuxt/types": "^2.15.8",
67
68
  "@nuxtjs/eslint-config-typescript": "^12.0.0",
68
69
  "@types/lodash-es": "^4.17.6",
@@ -70,11 +71,12 @@
70
71
  "@types/node": "^17.0.23",
71
72
  "@types/prettier": "^2.7.2",
72
73
  "cross-env": "^7.0.3",
73
- "eslint": "8.31.0",
74
+ "eslint": "8.32.0",
74
75
  "eslint-config-prettier": "^8.6.0",
76
+ "eslint-plugin-vue": "^9.9.0",
75
77
  "nuxt": "3.0.0",
76
78
  "typescript": "^4.9.4",
77
- "vitest": "^0.27.0",
79
+ "vitest": "^0.27.2",
78
80
  "vue-router": "^4.1.6"
79
81
  }
80
82
  }