nuxt-typed-router 2.1.4 → 2.2.0-beta.0

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
@@ -22,7 +22,7 @@
22
22
  - `useRouter`, `useRoute` and `navigateTo` route autocomplete and params type-check
23
23
  - Supports optional params and catchAll routes
24
24
  - Infer route params based on route name
25
- - Supports routes defined in `config.extendRoutes`
25
+ - Supports routes extended by config and modules
26
26
 
27
27
  > ⚠️ Since `v2.1.x`, `useTypedRouter` and `useTypedRoute` are no longer exported.
28
28
  The package can now override types from `useRouter`, `useRoute` and `navigateTo`
@@ -89,6 +89,12 @@ export default defineNuxtConfig({
89
89
  ```
90
90
 
91
91
 
92
+ # Roadmap
93
+
94
+ - [ ] Add `path` autocomplete with TS string templates
95
+ - [ ] Enforce strong params typing depending of origin route
96
+
97
+
92
98
  ## Development
93
99
 
94
100
  1. Clone this repository
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.1.4"
8
+ "version": "2.2.0-beta.0"
9
9
  }
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { extendPages, defineNuxtModule, createResolver } from '@nuxt/kit';
1
+ import { addPluginTemplate, extendPages, defineNuxtModule, createResolver } from '@nuxt/kit';
2
2
  import chalk from 'chalk';
3
3
  import logSymbols from 'log-symbols';
4
4
  import prettier from 'prettier';
@@ -8,104 +8,23 @@ import { dirname, resolve } from 'pathe';
8
8
  import mkdirp from 'mkdirp';
9
9
  import { camelCase } from 'lodash-es';
10
10
 
11
- const watermarkTemplate = `
12
- // @ts-nocheck
13
- // eslint-disable
14
- /**
15
- * ---------------------------------------------------
16
- * \u{1F697}\u{1F6A6} Generated by nuxt-typed-router. Do not modify !
17
- * ---------------------------------------------------
18
- * */
19
-
20
- `;
21
-
22
- function createDeclarationRoutesFile(autoImport) {
11
+ function createRoutesNamesListExport(routesList) {
23
12
  return `
24
- ${watermarkTemplate}
25
-
26
- import type { NuxtLinkProps } from '#app';
27
- import type { DefineComponent } from 'vue';
28
- import type { RouteLocationRaw } from 'vue-router';
29
- import type { TypedRouteNamedMapper } from './__routes';
30
- import { useRoute as _useRoute } from './__useTypedRoute';
31
- import { useRouter as _useRouter } from './__useTypedRouter';
32
- import { navigateTo as _navigateTo } from './__utils';
33
-
34
- declare global {
35
-
36
- ${autoImport ? `const useRoute: typeof _useRoute;
37
- const useRouter: typeof _useRouter;
38
- const navigateTo: typeof _navigateTo;` : ""}
39
- }
40
-
41
- type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
42
- to: string | Omit<Exclude<RouteLocationRaw, string>, 'name'> & TypedRouteNamedMapper;
43
- };
44
-
45
- export type _NuxtLink = DefineComponent<
46
- TypedNuxtLinkProps,
47
- {},
48
- {},
49
- import('vue').ComputedOptions,
50
- import('vue').MethodOptions,
51
- import('vue').ComponentOptionsMixin,
52
- import('vue').ComponentOptionsMixin,
53
- {},
54
- string,
55
- import('vue').VNodeProps &
56
- import('vue').AllowedComponentProps &
57
- import('vue').ComponentCustomProps,
58
- Readonly<TypedNuxtLinkProps>,
59
- {}
60
- >;
61
-
62
- declare module '@vue/runtime-core' {
63
- export interface GlobalComponents {
64
- NuxtLink: _NuxtLink;
65
- }
66
- }
67
- `;
13
+ /**
14
+ * Exhaustive list of all the available route names in the app
15
+ * */
16
+ export type RoutesNamesList = ${routesList.map((m) => `'${m}'`).join("|\n")}`;
68
17
  }
69
18
 
70
- function createRuntimeIndexFile() {
19
+ function createRoutesParamsRecordExport(routesParams) {
71
20
  return `
72
- ${watermarkTemplate}
73
- export { routesNames } from './__routes';
74
- export type { TypedRouteList, TypedRouteNamedMapper, TypedRouteParams } from './__routes';
75
- export {useRouter} from './__useTypedRouter';
76
- export {useRoute} from './__useTypedRoute';
77
- export type {TypedRoute, TypedRouter, TypedNamedRoute } from './__router';
78
- export * from './__utils';
79
- `;
80
- }
81
-
82
- function createRuntimePluginFile(routesDeclTemplate) {
83
- return `
84
- ${watermarkTemplate}
85
- import { defineNuxtPlugin, useRouter, useRoute } from '#app';
86
- import {TypedRouter, TypedRoute} from '@typed-router';
87
-
88
- export default defineNuxtPlugin(() => {
89
- const router = useRouter();
90
- const route = useRoute();
91
- const routesNames = ${routesDeclTemplate};
92
-
93
- return {
94
- provide: {
95
- typedRouter: router as TypedRouter,
96
- typedRoute: route as TypedRoute,
97
- routesNames,
98
- },
99
- };
100
- });
101
- `;
102
- }
103
-
104
- function createTypedRouteListExport(routesList) {
105
- return `export type TypedRouteList = ${routesList.map((m) => `'${m}'`).join("|\n")}`;
106
- }
107
- function createTypedRouteParamsExport(routesParams) {
108
- return `export type TypedRouteParams = {
21
+ /**
22
+ * Routes params are only required for the exact targeted route name,
23
+ * vue-router behaviour allow to navigate between children routes without the need to provide all the params every time.
24
+ * So we can't enforce params when navigating between routes, only a \`[xxx].vue\` page will have required params in the type definition
25
+ *
26
+ * */
27
+ export type RoutesParamsRecord = {
109
28
  ${routesParams.map(
110
29
  ({ name, params }) => `"${name}": ${params.length ? `{
111
30
  ${params.map(
@@ -115,8 +34,14 @@ function createTypedRouteParamsExport(routesParams) {
115
34
  ).join(",\n")}
116
35
  }`;
117
36
  }
118
- function createTypedRouteNamedMapperExport(routesParams) {
119
- return `export type TypedRouteNamedMapper =
37
+
38
+ function createRoutesNamedLocationsExport(routesParams) {
39
+ return `
40
+ /**
41
+ * Discriminated union that will allow to infer params based on route name
42
+ * It's used for programmatic navigation like router.push or <NuxtLink/>
43
+ * */
44
+ export type RoutesNamedLocations =
120
45
  ${routesParams.map(
121
46
  ({ name, params }) => `{name: "${name}" ${params.length ? `, params${params.some((s) => s.required) ? "" : "?"}: {
122
47
  ${params.map(
@@ -126,10 +51,16 @@ function createTypedRouteNamedMapperExport(routesParams) {
126
51
  ).join("|\n")}
127
52
  `;
128
53
  }
129
- function createResolvedTypedRouteNamedMapperExport(routesParams) {
130
- return `export type ResolvedTypedRouteNamedMapper =
54
+
55
+ function createRoutesNamedLocationsResolvedExport(routesParams) {
56
+ return `
57
+ /**
58
+ * Type returned by a resolved Route that will allow to type guard the route name.
59
+ * By default the params are unknown
60
+ * */
61
+ export type RoutesNamedLocationsResolved =
131
62
  {
132
- name: TypedRouteList;
63
+ name: RoutesNamesList;
133
64
  params: unknown;
134
65
  } & (
135
66
  ${routesParams.map(
@@ -143,172 +74,102 @@ function createResolvedTypedRouteNamedMapperExport(routesParams) {
143
74
  `;
144
75
  }
145
76
 
146
- function createRuntimeRoutesFile({
77
+ function createRoutesTypesFile({
147
78
  routesList,
148
79
  routesObjectTemplate,
149
80
  routesDeclTemplate,
150
81
  routesParams
151
82
  }) {
152
- return `
153
- ${watermarkTemplate}
83
+ return (
84
+ /* typescript */
85
+ `
86
+ ${createRoutesNamesListExport(routesList)}
154
87
 
155
- export const routesNames = ${routesObjectTemplate};
156
-
157
- ${createTypedRouteListExport(routesList)}
158
-
159
- export type RouteListDecl = ${routesDeclTemplate};
160
-
161
- /**
162
- * Routes params are only required for the exact targeted route name,
163
- * vue-router behaviour allow to navigate between children routes without the need to provide all the params every time.
164
- * So we can't enforce params when navigating between routes, only a \`[xxx].vue\` page will have required params in the type definition
165
- *
166
- *
167
- * */
168
- ${createTypedRouteParamsExport(routesParams)}
88
+ ${createRoutesParamsRecordExport(routesParams)}
169
89
 
170
- ${createTypedRouteNamedMapperExport(routesParams)}
171
-
172
- ${createResolvedTypedRouteNamedMapperExport(routesParams)}
173
- `;
174
- }
175
-
176
- function createUseTypedRouteFile(routesDeclTemplate) {
177
- return `
178
- ${watermarkTemplate}
179
- import { useRoute as defaultRoute } from '#app';
180
- import type { TypedRouteList } from './__routes';
181
- import type {TypedRoute, TypedNamedRoute} from './__router'
182
-
183
- /** Acts the same as \`useRoute\`, but typed.
184
- *
185
- * @exemple
186
- *
187
- * \`\`\`ts
188
- * const route = useRoute();
189
- * \`\`\`
190
- *
191
- * \`\`\`ts
192
- * const route = useRoute('my-route-with-param-id');
193
- * route.params.id // autocompletes!
194
- * \`\`\`
195
- *
196
- * \`\`\`ts
197
- * const route = useRoute();
198
- * if (route.name === 'my-route-with-param-id') {
199
- * route.params.id // autocompletes!
200
- * }
201
- * \`\`\`
202
- */
203
- export function useRoute<T extends TypedRouteList = never>(
204
- name?: T
205
- ): [T] extends [never] ? TypedRoute : TypedNamedRoute<T> {
206
- const route = defaultRoute();
207
-
208
- return route as any;
209
- }
90
+ ${createRoutesNamedLocationsExport(routesParams)}
210
91
 
92
+ ${createRoutesNamedLocationsResolvedExport(routesParams)}
211
93
 
212
- `;
213
- }
94
+
214
95
 
215
- function createRuntimeUseTypedRouterFile(routesDeclTemplate) {
216
- return `
217
- ${watermarkTemplate}
218
- import { useRouter as defaultRouter } from '#app';
219
- import type { TypedRouter } from './__router';
96
+ export type RoutesNamesListRecord = ${routesDeclTemplate};
220
97
 
221
- /** Returns instances of $typedRouter and $routesList fully typed to use in your components or your Vuex/Pinia store
222
- *
223
- * @exemple
224
- *
225
- * \`\`\`ts
226
- * const router = useRouter();
227
- * \`\`\`
228
- */
229
- export function useRouter(): TypedRouter {
230
- const router = defaultRouter();
231
-
232
- return router;
233
- };
234
-
235
- `;
98
+ export const routesNames = ${routesObjectTemplate};
99
+ `
100
+ );
236
101
  }
237
102
 
238
- function createRuntimeRouterTypes() {
239
- return `
103
+ function createTypedRouterFile() {
104
+ return (
105
+ /* typescript */
106
+ `
107
+
108
+ import type { Ref } from 'vue';
240
109
  import type {
241
110
  NavigationFailure,
242
111
  RouteLocation,
243
112
  RouteLocationNormalizedLoaded,
244
- RouteLocationOptions,
245
113
  RouteLocationRaw,
246
- RouteQueryAndHash,
247
114
  Router,
248
115
  } from 'vue-router';
249
116
  import type {
250
- ResolvedTypedRouteNamedMapper,
251
- TypedRouteList,
252
- TypedRouteNamedMapper,
253
- TypedRouteParams,
117
+ RoutesNamedLocations,
118
+ RoutesNamedLocationsResolved,
119
+ RoutesNamesList,
120
+ RoutesParamsRecord,
254
121
  } from './__routes';
122
+ import type { HasOneRequiredParameter } from './__types_utils';
255
123
 
256
- // Type utils
257
- type ExtractRequiredParameters<T extends Record<string, any>> = Pick<
258
- T,
259
- { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]
260
- >;
261
-
262
- type HasOneRequiredParameter<T extends TypedRouteList> = [TypedRouteParams[T]] extends [never]
263
- ? false
264
- : [keyof ExtractRequiredParameters<TypedRouteParams[T]>] extends [undefined]
265
- ? false
266
- : true;
267
-
268
- type TypedLocationAsRelativeRaw<T extends TypedRouteList> = {
124
+
125
+ // - Routes location for navigation types (ex: router.push or navigateTo)
126
+
127
+ /**
128
+ * RouteLocationRaw with discrimanated name and params properties
129
+ * {@link RouteLocationRaw}
130
+ * */
131
+ export type TypedRouteLocationRaw =
132
+ | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & RoutesNamedLocations)
133
+ | string;
134
+
135
+ /**
136
+ * Alternative version of {@link TypedRouteLocationRaw} but with a name generic
137
+ */
138
+ export type TypedRouteLocationRawFromName<T extends RoutesNamesList> =
139
+ | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & TypedLocationAsRelativeRaw<T>)
140
+ | string;
141
+
142
+ /**
143
+ * Generic providing inference and dynamic inclusion of \`params\` property
144
+ * {@link import('vue-router').LocationAsRelativeRaw}
145
+ * */
146
+ export type TypedLocationAsRelativeRaw<T extends RoutesNamesList> = {
269
147
  name?: T;
270
- } & ([TypedRouteParams[T]] extends [never]
148
+ } & ([RoutesParamsRecord[T]] extends [never]
271
149
  ? {}
272
150
  : HasOneRequiredParameter<T> extends false
273
- ? { params?: TypedRouteParams[T] }
274
- : { params: TypedRouteParams[T] });
275
-
276
- type ResolvedTypedLocationAsRelativeRaw<T extends TypedRouteList> = {
277
- name?: T;
278
- } & ([TypedRouteParams[T]] extends [never] ? {} : { params: TypedRouteParams[T] });
279
-
280
- export type TypedNamedRouteLocation<T extends TypedRouteList> =
281
- | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & TypedLocationAsRelativeRaw<T>)
282
- | string;
283
-
284
- type TypedRouteLocationRaw =
285
- | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & TypedRouteNamedMapper)
286
- | string;
287
-
288
- type _TypedRoute = Omit<RouteLocationNormalizedLoaded, 'name' | 'params'> &
289
- ResolvedTypedRouteNamedMapper;
290
- type _TypedNamedRoute<T extends TypedRouteList> = Omit<
291
- RouteLocationNormalizedLoaded,
292
- 'name' | 'params'
293
- > &
294
- ResolvedTypedLocationAsRelativeRaw<T>;
151
+ ? { params?: RoutesParamsRecord[T] }
152
+ : { params: RoutesParamsRecord[T] });
153
+
295
154
 
296
- /** Augmented Router interface */
297
- interface _TypedRouter
155
+ /** Augmented Router with typed methods
156
+ * {@link Router}
157
+ */
158
+ export interface TypedRouter
298
159
  extends Omit<Router, 'removeRoute' | 'hasRoute' | 'resolve' | 'push' | 'replace' | 'currentRoute'> {
299
- readonly currentRoute: _TypedRoute;
160
+ readonly currentRoute: Ref<TypedRoute>;
300
161
  /**
301
162
  * Remove an existing route by its name.
302
163
  *
303
164
  * @param name - Name of the route to remove
304
165
  */
305
- removeRoute(name: TypedRouteList): void;
166
+ removeRoute(name: RoutesNamesList): void;
306
167
  /**
307
168
  * Checks if a route with a given name exists
308
169
  *
309
170
  * @param name - Name of the route to check
310
171
  */
311
- hasRoute(name: TypedRouteList): boolean;
172
+ hasRoute(name: RoutesNamesList): boolean;
312
173
  /**
313
174
  * Returns the {@link RouteLocation | normalized version} of a
314
175
  * {@link RouteLocationRaw | route location}. Also includes an \`href\` property
@@ -318,12 +179,10 @@ function createRuntimeRouterTypes() {
318
179
  * @param to - Raw route location to resolve
319
180
  * @param currentLocation - Optional current location to resolve against
320
181
  */
321
- resolve(
322
- to: TypedRouteLocationRaw,
323
- currentLocation?: RouteLocationNormalizedLoaded
324
- ): RouteLocation & {
325
- href: string;
326
- };
182
+ resolve<T extends RoutesNamesList>(
183
+ to: TypedRouteLocationRawFromName<T>,
184
+ currentLocation?: TypedRoute
185
+ ): TypedRouteLocationFromName<T>;
327
186
  /**
328
187
  * Programmatically navigate to a new URL by pushing an entry in the history
329
188
  * stack.
@@ -339,28 +198,298 @@ function createRuntimeRouterTypes() {
339
198
  */
340
199
  replace(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
341
200
  }
201
+
202
+
203
+
204
+ // - Resolved normalized routes for current Location (ex: useRoute and currentRoute)
342
205
 
343
- export interface TypedRouter extends _TypedRouter {}
344
- export type TypedRoute = _TypedRoute;
345
- export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
346
- `;
206
+
207
+
208
+ /**
209
+ * Clone of {@link RouteLocationNormalizedLoaded} with a discriminated union for name and params
210
+ */
211
+ export type TypedRoute = Omit<RouteLocationNormalizedLoaded, 'name' | 'params'> &
212
+ RoutesNamedLocationsResolved;
213
+
214
+ /**
215
+ * Clone of {@link TypedRoute} with generic param for route name that can dynamicaly add params property
216
+ */
217
+ export type TypedRouteFromName<T extends RoutesNamesList> = Omit<
218
+ RouteLocationNormalizedLoaded,
219
+ 'name' | 'params'
220
+ > &
221
+ TypedResolvedMatcherLocation<T>;
222
+
223
+ /**
224
+ * Generic providing inference and dynamic inclusion of \`params\` property
225
+ * {@link import('vue-router').LocationAsRelativeRaw}
226
+ * */
227
+ export type TypedResolvedMatcherLocation<T extends RoutesNamesList> = {
228
+ name: T;
229
+ } & ([RoutesParamsRecord[T]] extends [never] ? {} : { params: RoutesParamsRecord[T] });
230
+
231
+
232
+ /**
233
+ * Clone of {@link RouteLocation} with generic param for route name that can dynamicaly add params property
234
+ * Used by Router.resolve
235
+ * */
236
+ export type TypedRouteLocationFromName<T extends RoutesNamesList> = TypedRouteFromName<T> & {href: string};
237
+ `
238
+ );
347
239
  }
348
240
 
349
- function createRuntimeNavigateToFunction() {
350
- return `
351
- ${watermarkTemplate}
241
+ function createTypedRouterDefinitionFile({ autoImport, plugin }) {
242
+ return (
243
+ /* typescript */
244
+ `
245
+
246
+ import type { NuxtLinkProps } from '#app';
247
+ import type { DefineComponent } from 'vue';
248
+ import type { RouteLocationRaw } from 'vue-router';
249
+ import type { RoutesNamedLocations, RoutesNamesListRecord } from './__routes';
250
+ import type {TypedRouter, TypedRoute} from './__router';
251
+ import { useRoute as _useRoute } from './__useTypedRoute';
252
+ import { useRouter as _useRouter } from './__useTypedRouter';
253
+ import { navigateTo as _navigateTo } from './__navigateTo';
254
+
255
+ declare global {
256
+
257
+ ${autoImport ? (
258
+ /* typescript */
259
+ `
260
+ const useRoute: typeof _useRoute;
261
+ const useRouter: typeof _useRouter;
262
+ const navigateTo: typeof _navigateTo;`
263
+ ) : ""}
264
+ }
265
+
266
+ type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
267
+ to: string | Omit<Exclude<RouteLocationRaw, string>, 'name'> & RoutesNamedLocations;
268
+ };
269
+
270
+ export type TypedNuxtLink = DefineComponent<
271
+ TypedNuxtLinkProps,
272
+ {},
273
+ {},
274
+ import('vue').ComputedOptions,
275
+ import('vue').MethodOptions,
276
+ import('vue').ComponentOptionsMixin,
277
+ import('vue').ComponentOptionsMixin,
278
+ {},
279
+ string,
280
+ import('vue').VNodeProps &
281
+ import('vue').AllowedComponentProps &
282
+ import('vue').ComponentCustomProps,
283
+ Readonly<TypedNuxtLinkProps>,
284
+ {}
285
+ >;
286
+
287
+ declare module '@vue/runtime-core' {
288
+ export interface GlobalComponents {
289
+ NuxtLink: TypedNuxtLink;
290
+ }
291
+ }
292
+
293
+ ${plugin ? (
294
+ /* typescript */
295
+ `
296
+ interface CustomPluginProperties {
297
+ $typedRouter: TypedRouter,
298
+ $typedRoute: TypedRoute,
299
+ $routesNames: RoutesNamesListRecord
300
+ }
301
+ declare module '#app' {
302
+ interface NuxtApp extends CustomPluginProperties {}
303
+ }
304
+ declare module 'vue' {
305
+ interface ComponentCustomProperties extends CustomPluginProperties {}
306
+ }
307
+ `
308
+ ) : ""}
309
+ `
310
+ );
311
+ }
312
+
313
+ function createIndexFile() {
314
+ return (
315
+ /* typescript */
316
+ `
317
+
318
+ export type {
319
+ TypedLocationAsRelativeRaw,
320
+ TypedResolvedMatcherLocation,
321
+ TypedRoute,
322
+ TypedRouteFromName,
323
+ TypedRouteLocationFromName,
324
+ TypedRouteLocationRaw,
325
+ TypedRouteLocationRawFromName,
326
+ TypedRouter,
327
+ } from './__router';
328
+ export { routesNames } from './__routes';
329
+ export type {
330
+ RoutesNamedLocations,
331
+ RoutesNamedLocationsResolved,
332
+ RoutesNamesList,
333
+ RoutesNamesListRecord,
334
+ RoutesParamsRecord,
335
+ } from './__routes';
336
+ export { useRoute } from './__useTypedRoute';
337
+ export { useRouter } from './__useTypedRouter';
338
+ export { navigateTo } from './__navigateTo';
339
+ `
340
+ );
341
+ }
342
+
343
+ function createPluginFile() {
344
+ return (
345
+ /* typescript */
346
+ `
347
+
348
+ import { defineNuxtPlugin, useRouter, useRoute } from '#app';
349
+ import {TypedRouter, TypedRoute, routesNames} from '@typed-router';
350
+
351
+ export default defineNuxtPlugin(() => {
352
+ const router = useRouter();
353
+ const route = useRoute();
354
+
355
+ return {
356
+ provide: {
357
+ typedRouter: router as TypedRouter,
358
+ typedRoute: route as TypedRoute,
359
+ routesNames,
360
+ },
361
+ };
362
+ });
363
+ `
364
+ );
365
+ }
366
+
367
+ function createUseTypedRouteFile(routesDeclTemplate) {
368
+ return (
369
+ /* typescript */
370
+ `
371
+ import { useRoute as defaultRoute } from '#app';
372
+ import type { RoutesNamesList } from './__routes';
373
+ import type {TypedRoute, TypedRouteFromName} from './__router'
374
+
375
+ /**
376
+ * Typed clone of \`useRoute\`
377
+ *
378
+ * @exemple
379
+ *
380
+ * \`\`\`ts
381
+ * const route = useRoute();
382
+ * \`\`\`
383
+ *
384
+ * \`\`\`ts
385
+ * const route = useRoute('my-route-with-param-id');
386
+ * route.params.id // autocompletes!
387
+ * \`\`\`
388
+ *
389
+ * \`\`\`ts
390
+ * const route = useRoute();
391
+ * if (route.name === 'my-route-with-param-id') {
392
+ * route.params.id // autocompletes!
393
+ * }
394
+ * \`\`\`
395
+ */
396
+ export function useRoute<T extends RoutesNamesList = never>(
397
+ name?: T
398
+ ): [T] extends [never] ? TypedRoute : TypedRouteFromName<T> {
399
+ const route = defaultRoute();
400
+
401
+ return route as any;
402
+ }
403
+ `
404
+ );
405
+ }
406
+
407
+ function createUseTypedRouterFile(routesDeclTemplate) {
408
+ return (
409
+ /* typescript */
410
+ `
411
+
412
+ import { useRouter as defaultRouter } from '#app';
413
+ import type { TypedRouter } from './__router';
414
+
415
+ /**
416
+ * Typed clone of \`useRouter\`
417
+ *
418
+ * @exemple
419
+ *
420
+ * \`\`\`ts
421
+ * const router = useRouter();
422
+ * \`\`\`
423
+ */
424
+ export function useRouter(): TypedRouter {
425
+ const router = defaultRouter();
426
+
427
+ return router;
428
+ };
429
+
430
+ `
431
+ );
432
+ }
433
+
434
+ function createNavigateToFile() {
435
+ return (
436
+ /* typescript */
437
+ `
352
438
  import { navigateTo as defaultNavigateTo } from '#app';
353
- import { NavigateToOptions } from 'nuxt/dist/app/composables/router';
354
- import { NavigationFailure } from 'vue-router';
355
- import type { TypedNamedRouteLocation } from './__router';
356
- import { TypedRouteList } from './__routes';
439
+ import type { NavigateToOptions } from 'nuxt/dist/app/composables/router';
440
+ import type { NavigationFailure } from 'vue-router';
441
+ import type { TypedRouteLocationRawFromName, TypedRouteFromName } from './__router';
442
+ import type { RoutesNamesList } from './__routes';
357
443
 
358
- export const navigateTo: <T extends TypedRouteList>(
359
- to: TypedNamedRouteLocation<T>,
444
+ /**
445
+ * Typed clone of \`navigateTo\`
446
+ *
447
+ * @exemple
448
+ *
449
+ * \`\`\`ts
450
+ * const resolved = navigateTo({name: 'foo', params: {foo: 'bar'}});
451
+ * \`\`\`
452
+ */
453
+ export const navigateTo: <T extends RoutesNamesList>(
454
+ to: TypedRouteLocationRawFromName<T>,
360
455
  options?: NavigateToOptions
361
- ) => Promise<void | NavigationFailure | TypedNamedRoute<T>> = defaultNavigateTo as any;
456
+ ) => Promise<void | NavigationFailure | TypedRouteFromName<T>> = defaultNavigateTo as any;
362
457
 
363
- `;
458
+ `
459
+ );
460
+ }
461
+
462
+ function createTypeUtilsRuntimeFile() {
463
+ return (
464
+ /* typescript */
465
+ `
466
+
467
+ import { RoutesNamesList, RoutesParamsRecord } from './__routes';
468
+
469
+ // - Type utils
470
+ export type ExtractRequiredParameters<T extends Record<string, any>> = Pick<
471
+ T,
472
+ { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]
473
+ >;
474
+
475
+ export type HasOneRequiredParameter<T extends RoutesNamesList> = [RoutesParamsRecord[T]] extends [
476
+ never
477
+ ]
478
+ ? false
479
+ : [keyof ExtractRequiredParameters<RoutesParamsRecord[T]>] extends [undefined]
480
+ ? false
481
+ : true;
482
+ `
483
+ );
484
+ }
485
+
486
+ async function handleAddPlugin() {
487
+ const pluginName = "__typed-router.plugin.ts";
488
+ addPluginTemplate({
489
+ filename: pluginName,
490
+ getContents: createPluginFile,
491
+ mode: "all"
492
+ });
364
493
  }
365
494
 
366
495
  const { resolveConfig, format } = prettier;
@@ -424,32 +553,28 @@ async function writeFile(path, content) {
424
553
  }
425
554
  }
426
555
 
427
- function handlePluginFileSave({ nuxt, routesDeclTemplate }) {
428
- const pluginName = "__typed-router.ts";
429
- const srcDir = nuxt.options.srcDir;
430
- async function savePlugin() {
431
- const pluginFolder = `${srcDir}/plugins`;
432
- await processPathAndWriteFile({
433
- outDir: pluginFolder,
434
- rootDir: srcDir,
435
- fileName: pluginName,
436
- content: createRuntimePluginFile(routesDeclTemplate)
437
- });
438
- }
439
- nuxt.hook("build:done", savePlugin);
440
- nuxt.hook("prepare:types", savePlugin);
441
- }
556
+ const watermarkTemplate = `
557
+ // @ts-nocheck
558
+ // eslint-disable
559
+ /**
560
+ * ---------------------------------------------------
561
+ * \u{1F697}\u{1F6A6} Generated by nuxt-typed-router. Do not modify !
562
+ * ---------------------------------------------------
563
+ * */
564
+
565
+ `;
442
566
 
443
567
  let previousGeneratedRoutes = "";
444
568
  async function saveGeneratedFiles({
445
569
  rootDir,
446
570
  autoImport,
571
+ plugin,
447
572
  outputData: { routesDeclTemplate, routesList, routesObjectTemplate, routesParams }
448
573
  }) {
449
574
  const filesMap = [
450
575
  {
451
576
  fileName: "__useTypedRouter.ts",
452
- content: createRuntimeUseTypedRouterFile()
577
+ content: createUseTypedRouterFile()
453
578
  },
454
579
  {
455
580
  fileName: "__useTypedRoute.ts",
@@ -457,7 +582,7 @@ async function saveGeneratedFiles({
457
582
  },
458
583
  {
459
584
  fileName: `__routes.ts`,
460
- content: createRuntimeRoutesFile({
585
+ content: createRoutesTypesFile({
461
586
  routesList,
462
587
  routesObjectTemplate,
463
588
  routesDeclTemplate,
@@ -465,24 +590,35 @@ async function saveGeneratedFiles({
465
590
  })
466
591
  },
467
592
  {
468
- fileName: "__utils.ts",
469
- content: createRuntimeNavigateToFunction()
593
+ fileName: "__navigateTo.ts",
594
+ content: createNavigateToFile()
470
595
  },
471
596
  {
472
597
  fileName: `__router.d.ts`,
473
- content: createRuntimeRouterTypes()
598
+ content: createTypedRouterFile()
599
+ },
600
+ {
601
+ fileName: `__types_utils.d.ts`,
602
+ content: createTypeUtilsRuntimeFile()
474
603
  },
475
604
  {
476
605
  fileName: `typed-router.d.ts`,
477
- content: createDeclarationRoutesFile(autoImport)
606
+ content: createTypedRouterDefinitionFile({ autoImport, plugin })
478
607
  },
479
608
  {
480
609
  fileName: "index.ts",
481
- content: createRuntimeIndexFile()
610
+ content: createIndexFile()
482
611
  }
483
612
  ];
484
613
  await Promise.all(
485
- filesMap.map(({ content, fileName }) => processPathAndWriteFile({ rootDir, content, fileName }))
614
+ filesMap.map(({ content, fileName }) => {
615
+ const waterMakeredContent = `
616
+ ${watermarkTemplate}
617
+
618
+ ${content}
619
+ `;
620
+ return processPathAndWriteFile({ rootDir, content: waterMakeredContent, fileName });
621
+ })
486
622
  );
487
623
  if (previousGeneratedRoutes !== routesList.join(",")) {
488
624
  previousGeneratedRoutes = routesList.join(",");
@@ -668,21 +804,19 @@ async function createTypedRouter({
668
804
  nuxt.hook("modules:done", () => {
669
805
  createTypedRouter({ nuxt, plugin, isHookCall: true });
670
806
  });
807
+ if (plugin) {
808
+ await handleAddPlugin();
809
+ }
671
810
  return;
672
811
  }
673
812
  extendPages(async (routes) => {
674
813
  hasRoutesDefined = true;
675
814
  const outputData = constructRouteMap(routes);
676
- if (plugin) {
677
- handlePluginFileSave({
678
- nuxt,
679
- routesDeclTemplate: outputData.routesDeclTemplate
680
- });
681
- }
682
815
  await saveGeneratedFiles({
683
816
  autoImport,
684
817
  rootDir,
685
- outputData
818
+ outputData,
819
+ plugin
686
820
  });
687
821
  });
688
822
  setTimeout(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-typed-router",
3
- "version": "2.1.4",
3
+ "version": "2.2.0-beta.0",
4
4
  "description": "Provide autocompletion for pages route names generated by Nuxt router",
5
5
  "type": "module",
6
6
  "main": "./dist/module.cjs",
@@ -57,7 +57,7 @@
57
57
  "url": "https://github.com/victorgarciaesgi/nuxt-typed-router/issues"
58
58
  },
59
59
  "dependencies": {
60
- "@nuxt/kit": "3.0.0",
60
+ "@nuxt/kit": "3.1.1",
61
61
  "chalk": "^5.2.0",
62
62
  "lodash-es": "^4.17.21",
63
63
  "log-symbols": "^5.1.0",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@nuxt/module-builder": "^0.2.1",
70
- "@nuxt/test-utils": "^3.0.0",
70
+ "@nuxt/test-utils": "^3.1.1",
71
71
  "@nuxt/types": "^2.15.8",
72
72
  "@nuxtjs/eslint-config-typescript": "^12.0.0",
73
73
  "@types/lodash-es": "^4.17.6",
@@ -78,10 +78,10 @@
78
78
  "eslint": "8.32.0",
79
79
  "eslint-config-prettier": "^8.6.0",
80
80
  "eslint-plugin-vue": "^9.9.0",
81
- "nuxt": "3.0.0",
82
- "playwright": "1.29.2",
81
+ "nuxt": "3.1.1",
82
+ "playwright": "1.30.0",
83
83
  "typescript": "^4.9.4",
84
- "vitest": "^0.28.1",
84
+ "vitest": "^0.28.2",
85
85
  "vue-router": "^4.1.6",
86
86
  "vue-tsc": "^1.0.24"
87
87
  }