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 +7 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +413 -279
- package/package.json +6 -6
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
|
|
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
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
19
|
+
function createRoutesParamsRecordExport(routesParams) {
|
|
71
20
|
return `
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
export
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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:
|
|
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
|
|
77
|
+
function createRoutesTypesFile({
|
|
147
78
|
routesList,
|
|
148
79
|
routesObjectTemplate,
|
|
149
80
|
routesDeclTemplate,
|
|
150
81
|
routesParams
|
|
151
82
|
}) {
|
|
152
|
-
return
|
|
153
|
-
|
|
83
|
+
return (
|
|
84
|
+
/* typescript */
|
|
85
|
+
`
|
|
86
|
+
${createRoutesNamesListExport(routesList)}
|
|
154
87
|
|
|
155
|
-
|
|
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
|
-
${
|
|
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
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
117
|
+
RoutesNamedLocations,
|
|
118
|
+
RoutesNamedLocationsResolved,
|
|
119
|
+
RoutesNamesList,
|
|
120
|
+
RoutesParamsRecord,
|
|
254
121
|
} from './__routes';
|
|
122
|
+
import type { HasOneRequiredParameter } from './__types_utils';
|
|
255
123
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
} & ([
|
|
148
|
+
} & ([RoutesParamsRecord[T]] extends [never]
|
|
271
149
|
? {}
|
|
272
150
|
: HasOneRequiredParameter<T> extends false
|
|
273
|
-
? { params?:
|
|
274
|
-
: { params:
|
|
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
|
|
297
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
323
|
-
currentLocation?:
|
|
324
|
-
):
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
|
350
|
-
return
|
|
351
|
-
|
|
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 {
|
|
356
|
-
import {
|
|
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
|
-
|
|
359
|
-
|
|
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 |
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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:
|
|
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:
|
|
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: "
|
|
469
|
-
content:
|
|
593
|
+
fileName: "__navigateTo.ts",
|
|
594
|
+
content: createNavigateToFile()
|
|
470
595
|
},
|
|
471
596
|
{
|
|
472
597
|
fileName: `__router.d.ts`,
|
|
473
|
-
content:
|
|
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:
|
|
606
|
+
content: createTypedRouterDefinitionFile({ autoImport, plugin })
|
|
478
607
|
},
|
|
479
608
|
{
|
|
480
609
|
fileName: "index.ts",
|
|
481
|
-
content:
|
|
610
|
+
content: createIndexFile()
|
|
482
611
|
}
|
|
483
612
|
];
|
|
484
613
|
await Promise.all(
|
|
485
|
-
filesMap.map(({ 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.
|
|
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.
|
|
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.
|
|
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.
|
|
82
|
-
"playwright": "1.
|
|
81
|
+
"nuxt": "3.1.1",
|
|
82
|
+
"playwright": "1.30.0",
|
|
83
83
|
"typescript": "^4.9.4",
|
|
84
|
-
"vitest": "^0.28.
|
|
84
|
+
"vitest": "^0.28.2",
|
|
85
85
|
"vue-router": "^4.1.6",
|
|
86
86
|
"vue-tsc": "^1.0.24"
|
|
87
87
|
}
|