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 +7 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +413 -273
- 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) {
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
export {
|
|
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
|
|
19
|
+
function createRoutesParamsRecordExport(routesParams) {
|
|
83
20
|
return `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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,166 +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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
${
|
|
90
|
+
${createRoutesNamedLocationsExport(routesParams)}
|
|
171
91
|
|
|
172
|
-
${
|
|
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
|
-
|
|
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
|
-
|
|
233
|
-
};
|
|
96
|
+
export type RoutesNamesListRecord = ${routesDeclTemplate};
|
|
234
97
|
|
|
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
|
-
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
|
|
291
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
317
|
-
currentLocation?:
|
|
318
|
-
):
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
|
344
|
-
return
|
|
345
|
-
|
|
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 {
|
|
350
|
-
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';
|
|
351
443
|
|
|
352
|
-
|
|
353
|
-
|
|
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 |
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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:
|
|
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:
|
|
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: "
|
|
463
|
-
content:
|
|
593
|
+
fileName: "__navigateTo.ts",
|
|
594
|
+
content: createNavigateToFile()
|
|
464
595
|
},
|
|
465
596
|
{
|
|
466
597
|
fileName: `__router.d.ts`,
|
|
467
|
-
content:
|
|
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:
|
|
606
|
+
content: createTypedRouterDefinitionFile({ autoImport, plugin })
|
|
472
607
|
},
|
|
473
608
|
{
|
|
474
609
|
fileName: "index.ts",
|
|
475
|
-
content:
|
|
610
|
+
content: createIndexFile()
|
|
476
611
|
}
|
|
477
612
|
];
|
|
478
613
|
await Promise.all(
|
|
479
|
-
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
|
+
})
|
|
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.
|
|
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
|
}
|