nuxt-typed-router 2.1.3 → 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.3"
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) {
23
- 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
- `;
68
- }
69
-
70
- function createRuntimeIndexFile() {
11
+ function createRoutesNamesListExport(routesList) {
71
12
  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
- `;
13
+ /**
14
+ * Exhaustive list of all the available route names in the app
15
+ * */
16
+ export type RoutesNamesList = ${routesList.map((m) => `'${m}'`).join("|\n")}`;
80
17
  }
81
18
 
82
- function createRuntimePluginFile(routesDeclTemplate) {
19
+ function createRoutesParamsRecordExport(routesParams) {
83
20
  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,166 +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}
154
-
155
- export const routesNames = ${routesObjectTemplate};
156
-
157
- ${createTypedRouteListExport(routesList)}
158
-
159
- export type RouteListDecl = ${routesDeclTemplate};
83
+ return (
84
+ /* typescript */
85
+ `
86
+ ${createRoutesNamesListExport(routesList)}
160
87
 
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)}
90
+ ${createRoutesNamedLocationsExport(routesParams)}
171
91
 
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
- }
210
-
211
-
212
- `;
213
- }
214
-
215
- function createRuntimeUseTypedRouterFile(routesDeclTemplate) {
216
- return `
217
- ${watermarkTemplate}
218
- import { useRouter as defaultRouter } from '#app';
219
- import type { TypedRouter } from './__router';
92
+ ${createRoutesNamedLocationsResolvedExport(routesParams)}
220
93
 
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();
94
+
231
95
 
232
- return router;
233
- };
96
+ export type RoutesNamesListRecord = ${routesDeclTemplate};
234
97
 
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
- type TypedRouteLocationRaw = RouteQueryAndHash & TypedRouteNamedMapper & RouteLocationOptions;
281
-
282
- type _TypedRoute = Omit<RouteLocationNormalizedLoaded, 'name' | 'params'> &
283
- ResolvedTypedRouteNamedMapper;
284
- type _TypedNamedRoute<T extends TypedRouteList> = Omit<
285
- RouteLocationNormalizedLoaded,
286
- 'name' | 'params'
287
- > &
288
- ResolvedTypedLocationAsRelativeRaw<T>;
151
+ ? { params?: RoutesParamsRecord[T] }
152
+ : { params: RoutesParamsRecord[T] });
153
+
289
154
 
290
- /** Augmented Router interface */
291
- interface _TypedRouter
155
+ /** Augmented Router with typed methods
156
+ * {@link Router}
157
+ */
158
+ export interface TypedRouter
292
159
  extends Omit<Router, 'removeRoute' | 'hasRoute' | 'resolve' | 'push' | 'replace' | 'currentRoute'> {
293
- readonly currentRoute: _TypedRoute;
160
+ readonly currentRoute: Ref<TypedRoute>;
294
161
  /**
295
162
  * Remove an existing route by its name.
296
163
  *
297
164
  * @param name - Name of the route to remove
298
165
  */
299
- removeRoute(name: TypedRouteList): void;
166
+ removeRoute(name: RoutesNamesList): void;
300
167
  /**
301
168
  * Checks if a route with a given name exists
302
169
  *
303
170
  * @param name - Name of the route to check
304
171
  */
305
- hasRoute(name: TypedRouteList): boolean;
172
+ hasRoute(name: RoutesNamesList): boolean;
306
173
  /**
307
174
  * Returns the {@link RouteLocation | normalized version} of a
308
175
  * {@link RouteLocationRaw | route location}. Also includes an \`href\` property
@@ -312,12 +179,10 @@ function createRuntimeRouterTypes() {
312
179
  * @param to - Raw route location to resolve
313
180
  * @param currentLocation - Optional current location to resolve against
314
181
  */
315
- resolve(
316
- to: TypedRouteLocationRaw,
317
- currentLocation?: RouteLocationNormalizedLoaded
318
- ): RouteLocation & {
319
- href: string;
320
- };
182
+ resolve<T extends RoutesNamesList>(
183
+ to: TypedRouteLocationRawFromName<T>,
184
+ currentLocation?: TypedRoute
185
+ ): TypedRouteLocationFromName<T>;
321
186
  /**
322
187
  * Programmatically navigate to a new URL by pushing an entry in the history
323
188
  * stack.
@@ -333,28 +198,298 @@ function createRuntimeRouterTypes() {
333
198
  */
334
199
  replace(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
335
200
  }
201
+
202
+
203
+
204
+ // - Resolved normalized routes for current Location (ex: useRoute and currentRoute)
336
205
 
337
- export interface TypedRouter extends _TypedRouter {}
338
- export type TypedRoute = _TypedRoute;
339
- export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
340
- `;
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
+ );
341
239
  }
342
240
 
343
- function createRuntimeNavigateToFunction() {
344
- return `
345
- ${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
+ `
346
438
  import { navigateTo as defaultNavigateTo } from '#app';
347
- import { NavigateToOptions } from 'nuxt/dist/app/composables/router';
348
- import { NavigationFailure } from 'vue-router';
349
- import type { ResolvedTypedLocationAsRelativeRaw, TypedNamedRoute } from './__router';
350
- 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';
351
443
 
352
- export const navigateTo: <T extends TypedRouteList>(
353
- to: ResolvedTypedLocationAsRelativeRaw<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>,
354
455
  options?: NavigateToOptions
355
- ) => Promise<void | NavigationFailure | TypedNamedRoute<T>> = defaultNavigateTo as any;
456
+ ) => Promise<void | NavigationFailure | TypedRouteFromName<T>> = defaultNavigateTo as any;
356
457
 
357
- `;
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
+ });
358
493
  }
359
494
 
360
495
  const { resolveConfig, format } = prettier;
@@ -418,32 +553,28 @@ async function writeFile(path, content) {
418
553
  }
419
554
  }
420
555
 
421
- function handlePluginFileSave({ nuxt, routesDeclTemplate }) {
422
- const pluginName = "__typed-router.ts";
423
- const srcDir = nuxt.options.srcDir;
424
- async function savePlugin() {
425
- const pluginFolder = `${srcDir}/plugins`;
426
- await processPathAndWriteFile({
427
- outDir: pluginFolder,
428
- rootDir: srcDir,
429
- fileName: pluginName,
430
- content: createRuntimePluginFile(routesDeclTemplate)
431
- });
432
- }
433
- nuxt.hook("build:done", savePlugin);
434
- nuxt.hook("prepare:types", savePlugin);
435
- }
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
+ `;
436
566
 
437
567
  let previousGeneratedRoutes = "";
438
568
  async function saveGeneratedFiles({
439
569
  rootDir,
440
570
  autoImport,
571
+ plugin,
441
572
  outputData: { routesDeclTemplate, routesList, routesObjectTemplate, routesParams }
442
573
  }) {
443
574
  const filesMap = [
444
575
  {
445
576
  fileName: "__useTypedRouter.ts",
446
- content: createRuntimeUseTypedRouterFile()
577
+ content: createUseTypedRouterFile()
447
578
  },
448
579
  {
449
580
  fileName: "__useTypedRoute.ts",
@@ -451,7 +582,7 @@ async function saveGeneratedFiles({
451
582
  },
452
583
  {
453
584
  fileName: `__routes.ts`,
454
- content: createRuntimeRoutesFile({
585
+ content: createRoutesTypesFile({
455
586
  routesList,
456
587
  routesObjectTemplate,
457
588
  routesDeclTemplate,
@@ -459,24 +590,35 @@ async function saveGeneratedFiles({
459
590
  })
460
591
  },
461
592
  {
462
- fileName: "__utils.ts",
463
- content: createRuntimeNavigateToFunction()
593
+ fileName: "__navigateTo.ts",
594
+ content: createNavigateToFile()
464
595
  },
465
596
  {
466
597
  fileName: `__router.d.ts`,
467
- content: createRuntimeRouterTypes()
598
+ content: createTypedRouterFile()
599
+ },
600
+ {
601
+ fileName: `__types_utils.d.ts`,
602
+ content: createTypeUtilsRuntimeFile()
468
603
  },
469
604
  {
470
605
  fileName: `typed-router.d.ts`,
471
- content: createDeclarationRoutesFile(autoImport)
606
+ content: createTypedRouterDefinitionFile({ autoImport, plugin })
472
607
  },
473
608
  {
474
609
  fileName: "index.ts",
475
- content: createRuntimeIndexFile()
610
+ content: createIndexFile()
476
611
  }
477
612
  ];
478
613
  await Promise.all(
479
- 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
+ })
480
622
  );
481
623
  if (previousGeneratedRoutes !== routesList.join(",")) {
482
624
  previousGeneratedRoutes = routesList.join(",");
@@ -662,21 +804,19 @@ async function createTypedRouter({
662
804
  nuxt.hook("modules:done", () => {
663
805
  createTypedRouter({ nuxt, plugin, isHookCall: true });
664
806
  });
807
+ if (plugin) {
808
+ await handleAddPlugin();
809
+ }
665
810
  return;
666
811
  }
667
812
  extendPages(async (routes) => {
668
813
  hasRoutesDefined = true;
669
814
  const outputData = constructRouteMap(routes);
670
- if (plugin) {
671
- handlePluginFileSave({
672
- nuxt,
673
- routesDeclTemplate: outputData.routesDeclTemplate
674
- });
675
- }
676
815
  await saveGeneratedFiles({
677
816
  autoImport,
678
817
  rootDir,
679
- outputData
818
+ outputData,
819
+ plugin
680
820
  });
681
821
  });
682
822
  setTimeout(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-typed-router",
3
- "version": "2.1.3",
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
  }