nuxt-typed-router 2.1.0-beta.2 → 2.1.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/dist/module.d.ts +2 -13
- package/dist/module.json +1 -1
- package/dist/module.mjs +4 -679
- package/dist/types.d.ts +5 -2
- package/package.json +10 -7
package/dist/module.d.ts
CHANGED
|
@@ -1,13 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
interface ModuleOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Set to false if you don't want a plugin generated
|
|
6
|
-
* @default false
|
|
7
|
-
*/
|
|
8
|
-
plugin?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
12
|
-
|
|
13
|
-
export { ModuleOptions, _default as default };
|
|
1
|
+
export * from "/Users/victorgarcia/Desktop/projects/nuxt-typed-router/src/module";
|
|
2
|
+
export { default } from "/Users/victorgarcia/Desktop/projects/nuxt-typed-router/src/module";
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,681 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import logSymbols from 'log-symbols';
|
|
4
|
-
import prettier from 'prettier';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
import { dirname, resolve } from 'pathe';
|
|
8
|
-
import mkdirp from 'mkdirp';
|
|
9
|
-
import { camelCase } from 'lodash-es';
|
|
1
|
+
import jiti from "file:///Users/victorgarcia/Desktop/projects/nuxt-typed-router/node_modules/.pnpm/jiti@1.16.2/node_modules/jiti/lib/index.js";
|
|
10
2
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
NavigationFailure,
|
|
14
|
-
RouteLocation,
|
|
15
|
-
RouteLocationNormalizedLoaded,
|
|
16
|
-
RouteLocationOptions,
|
|
17
|
-
RouteQueryAndHash,
|
|
18
|
-
RouteLocationRaw,
|
|
19
|
-
Router,
|
|
20
|
-
} from 'vue-router';
|
|
21
|
-
import type { DefineComponent } from 'vue';
|
|
22
|
-
import type { NuxtLinkProps } from '#app';
|
|
23
|
-
import type {
|
|
24
|
-
TypedRouteList,
|
|
25
|
-
TypedRouteNamedMapper,
|
|
26
|
-
TypedRouteParams,
|
|
27
|
-
ResolvedTypedRouteNamedMapper,
|
|
28
|
-
} from './__routes';
|
|
29
|
-
import {useTypedRoute} from './__useTypedRoute';
|
|
30
|
-
import {useTypedRouter} from './__useTypedRouter';
|
|
31
|
-
`;
|
|
3
|
+
/** @type {import("/Users/victorgarcia/Desktop/projects/nuxt-typed-router/src/module")} */
|
|
4
|
+
const _module = jiti(null, { interopDefault: true, esmResolve: true })("/Users/victorgarcia/Desktop/projects/nuxt-typed-router/src/module.ts");
|
|
32
5
|
|
|
33
|
-
|
|
34
|
-
// @ts-nocheck
|
|
35
|
-
// eslint-disable
|
|
36
|
-
/**
|
|
37
|
-
* ---------------------------------------------------
|
|
38
|
-
* \u{1F697}\u{1F6A6} Generated by nuxt-typed-router. Do not modify !
|
|
39
|
-
* ---------------------------------------------------
|
|
40
|
-
* */
|
|
41
|
-
|
|
42
|
-
`;
|
|
43
|
-
|
|
44
|
-
function createRuntimeTypeUtils(autoImport) {
|
|
45
|
-
return `
|
|
46
|
-
// Type utils
|
|
47
|
-
type ExtractRequiredParameters<T extends Record<string, any>> = Pick<
|
|
48
|
-
T,
|
|
49
|
-
{ [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]
|
|
50
|
-
>;
|
|
51
|
-
|
|
52
|
-
type HasOneRequiredParameter<T extends TypedRouteList> = [TypedRouteParams[T]] extends [never]
|
|
53
|
-
? false
|
|
54
|
-
: [keyof ExtractRequiredParameters<TypedRouteParams[T]>] extends [undefined]
|
|
55
|
-
? false
|
|
56
|
-
: true;
|
|
57
|
-
|
|
58
|
-
type TypedLocationAsRelativeRaw<T extends TypedRouteList> = {
|
|
59
|
-
name?: T;
|
|
60
|
-
} & ([TypedRouteParams[T]] extends [never]
|
|
61
|
-
? {}
|
|
62
|
-
: HasOneRequiredParameter<T> extends false
|
|
63
|
-
? { params?: TypedRouteParams[T] }
|
|
64
|
-
: { params: TypedRouteParams[T] });
|
|
65
|
-
|
|
66
|
-
type ResolvedTypedLocationAsRelativeRaw<T extends TypedRouteList> = {
|
|
67
|
-
name?: T;
|
|
68
|
-
} & ([TypedRouteParams[T]] extends [never] ? {} : { params: TypedRouteParams[T] });
|
|
69
|
-
|
|
70
|
-
type TypedRouteLocationRaw = RouteQueryAndHash & TypedRouteNamedMapper & RouteLocationOptions;
|
|
71
|
-
|
|
72
|
-
type _TypedRoute = Omit<RouteLocationNormalizedLoaded, 'name' | 'params'> &
|
|
73
|
-
ResolvedTypedRouteNamedMapper;
|
|
74
|
-
type _TypedNamedRoute<T extends TypedRouteList> = Omit<
|
|
75
|
-
RouteLocationNormalizedLoaded,
|
|
76
|
-
'name' | 'params'
|
|
77
|
-
> &
|
|
78
|
-
ResolvedTypedLocationAsRelativeRaw<T>;
|
|
79
|
-
|
|
80
|
-
/** Augmented Router interface */
|
|
81
|
-
interface _TypedRouter
|
|
82
|
-
extends Omit<Router, 'removeRoute' | 'hasRoute' | 'resolve' | 'push' | 'replace', 'currentRoute'> {
|
|
83
|
-
readonly currentRoute: _TypedRoute;
|
|
84
|
-
/**
|
|
85
|
-
* Remove an existing route by its name.
|
|
86
|
-
*
|
|
87
|
-
* @param name - Name of the route to remove
|
|
88
|
-
*/
|
|
89
|
-
removeRoute(name: TypedRouteList): void;
|
|
90
|
-
/**
|
|
91
|
-
* Checks if a route with a given name exists
|
|
92
|
-
*
|
|
93
|
-
* @param name - Name of the route to check
|
|
94
|
-
*/
|
|
95
|
-
hasRoute(name: TypedRouteList): boolean;
|
|
96
|
-
/**
|
|
97
|
-
* Returns the {@link RouteLocation | normalized version} of a
|
|
98
|
-
* {@link RouteLocationRaw | route location}. Also includes an \`href\` property
|
|
99
|
-
* that includes any existing \`base\`. By default the \`currentLocation\` used is
|
|
100
|
-
* \`route.currentRoute\` and should only be overriden in advanced use cases.
|
|
101
|
-
*
|
|
102
|
-
* @param to - Raw route location to resolve
|
|
103
|
-
* @param currentLocation - Optional current location to resolve against
|
|
104
|
-
*/
|
|
105
|
-
resolve(
|
|
106
|
-
to: TypedRouteLocationRaw,
|
|
107
|
-
currentLocation?: RouteLocationNormalizedLoaded
|
|
108
|
-
): RouteLocation & {
|
|
109
|
-
href: string;
|
|
110
|
-
};
|
|
111
|
-
/**
|
|
112
|
-
* Programmatically navigate to a new URL by pushing an entry in the history
|
|
113
|
-
* stack.
|
|
114
|
-
*
|
|
115
|
-
* @param to - Route location to navigate to
|
|
116
|
-
*/
|
|
117
|
-
push(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
|
|
118
|
-
/**
|
|
119
|
-
* Programmatically navigate to a new URL by replacing the current entry in
|
|
120
|
-
* the history stack.
|
|
121
|
-
*
|
|
122
|
-
* @param to - Route location to navigate to
|
|
123
|
-
*/
|
|
124
|
-
replace(to: TypedRouteLocationRaw): Promise<NavigationFailure | void | undefined>;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface TypedRouter extends _TypedRouter {}
|
|
128
|
-
export type TypedRoute = _TypedRoute;
|
|
129
|
-
export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
|
|
130
|
-
|
|
131
|
-
declare global {
|
|
132
|
-
export interface TypedRouter extends _TypedRouter {}
|
|
133
|
-
export type TypedRoute = _TypedRoute;
|
|
134
|
-
export type TypedNamedRoute<T extends TypedRouteList> = _TypedNamedRoute<T>;
|
|
135
|
-
|
|
136
|
-
${autoImport ? `const useRoute: typeof useTypedRoute;
|
|
137
|
-
const useRouter: typeof useTypedRouter;` : ""}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
|
|
141
|
-
to: string | Omit<Exclude<RouteLocationRaw, string>, 'name'> & TypedRouteNamedMapper;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
type _NuxtLink = DefineComponent<
|
|
145
|
-
TypedNuxtLinkProps,
|
|
146
|
-
{},
|
|
147
|
-
{},
|
|
148
|
-
import('vue').ComputedOptions,
|
|
149
|
-
import('vue').MethodOptions,
|
|
150
|
-
import('vue').ComponentOptionsMixin,
|
|
151
|
-
import('vue').ComponentOptionsMixin,
|
|
152
|
-
{},
|
|
153
|
-
string,
|
|
154
|
-
import('vue').VNodeProps &
|
|
155
|
-
import('vue').AllowedComponentProps &
|
|
156
|
-
import('vue').ComponentCustomProps,
|
|
157
|
-
Readonly<TypedNuxtLinkProps>,
|
|
158
|
-
{}
|
|
159
|
-
>;
|
|
160
|
-
|
|
161
|
-
declare module '@vue/runtime-core' {
|
|
162
|
-
export interface GlobalComponents {
|
|
163
|
-
NuxtLink: _NuxtLink;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
`;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function createDeclarationRoutesFile(autoImport) {
|
|
170
|
-
return `
|
|
171
|
-
${watermarkTemplate}
|
|
172
|
-
|
|
173
|
-
${staticTypesImports}
|
|
174
|
-
|
|
175
|
-
${createRuntimeTypeUtils(autoImport)}
|
|
176
|
-
`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function createRuntimeIndexFile() {
|
|
180
|
-
return `
|
|
181
|
-
${watermarkTemplate}
|
|
182
|
-
export { routesNames } from './__routes';
|
|
183
|
-
export type { TypedRouteList } from './__routes';
|
|
184
|
-
export {useRouter} from './__useTypedRouter';
|
|
185
|
-
export {useRoute} from './__useTypedRoute';
|
|
186
|
-
`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function createRuntimePluginFile(routesDeclTemplate) {
|
|
190
|
-
return `
|
|
191
|
-
${watermarkTemplate}
|
|
192
|
-
import { defineNuxtPlugin } from '#app';
|
|
193
|
-
|
|
194
|
-
export default defineNuxtPlugin(() => {
|
|
195
|
-
const router = useRouter();
|
|
196
|
-
const route = useRoute();
|
|
197
|
-
const routesNames = ${routesDeclTemplate};
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
provide: {
|
|
201
|
-
typedRouter: router as TypedRouter,
|
|
202
|
-
typedRoute: route as TypedRoute,
|
|
203
|
-
routesNames,
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
`;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function createTypedRouteListExport(routesList) {
|
|
211
|
-
return `export type TypedRouteList = ${routesList.map((m) => `'${m}'`).join("|\n")}`;
|
|
212
|
-
}
|
|
213
|
-
function createTypedRouteParamsExport(routesParams) {
|
|
214
|
-
return `export type TypedRouteParams = {
|
|
215
|
-
${routesParams.map(
|
|
216
|
-
({ name, params }) => `"${name}": ${params.length ? `{
|
|
217
|
-
${params.map(({ key, required }) => `"${key}"${required ? "" : "?"}: string | number`).join(",\n")}
|
|
218
|
-
}` : "never"}`
|
|
219
|
-
).join(",\n")}
|
|
220
|
-
}`;
|
|
221
|
-
}
|
|
222
|
-
function createTypedRouteNamedMapperExport(routesParams) {
|
|
223
|
-
return `export type TypedRouteNamedMapper =
|
|
224
|
-
${routesParams.map(
|
|
225
|
-
({ name, params }) => `{name: "${name}" ${params.length ? `, params${params.some((s) => s.required) ? "" : "?"}: {
|
|
226
|
-
${params.map(
|
|
227
|
-
({ key, required, catchAll }) => `"${key}"${required ? "" : "?"}: (string | number)${catchAll ? "[]" : ""}`
|
|
228
|
-
).join(",\n")}
|
|
229
|
-
}` : ""}}`
|
|
230
|
-
).join("|\n")}
|
|
231
|
-
`;
|
|
232
|
-
}
|
|
233
|
-
function createResolvedTypedRouteNamedMapperExport(routesParams) {
|
|
234
|
-
return `export type ResolvedTypedRouteNamedMapper =
|
|
235
|
-
{
|
|
236
|
-
name: TypedRouteList;
|
|
237
|
-
params: unknown;
|
|
238
|
-
} & (
|
|
239
|
-
${routesParams.map(
|
|
240
|
-
({ name, params }) => `{name: "${name}" ${params.length ? `, params: {
|
|
241
|
-
${params.map(
|
|
242
|
-
({ key, notRequiredOnPage, catchAll }) => `"${key}"${notRequiredOnPage ? "?" : ""}: string${catchAll ? "[]" : ""}`
|
|
243
|
-
).join(",\n")}
|
|
244
|
-
}` : ""}}`
|
|
245
|
-
).join("|\n")}
|
|
246
|
-
)
|
|
247
|
-
`;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function createRuntimeRoutesFile({
|
|
251
|
-
routesList,
|
|
252
|
-
routesObjectTemplate,
|
|
253
|
-
routesDeclTemplate,
|
|
254
|
-
routesParams
|
|
255
|
-
}) {
|
|
256
|
-
return `
|
|
257
|
-
${watermarkTemplate}
|
|
258
|
-
|
|
259
|
-
export const routesNames = ${routesObjectTemplate};
|
|
260
|
-
|
|
261
|
-
${createTypedRouteListExport(routesList)}
|
|
262
|
-
|
|
263
|
-
export type RouteListDecl = ${routesDeclTemplate};
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Routes params are only required for the exact targeted route name,
|
|
267
|
-
* vue-router behaviour allow to navigate between children routes without the need to provide all the params every time.
|
|
268
|
-
* So we can't enforce params when navigating between routes, only a \`[xxx].vue\` page will have required params in the type definition
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* */
|
|
272
|
-
|
|
273
|
-
${createTypedRouteParamsExport(routesParams)}
|
|
274
|
-
|
|
275
|
-
${createTypedRouteNamedMapperExport(routesParams)}
|
|
276
|
-
|
|
277
|
-
${createResolvedTypedRouteNamedMapperExport(routesParams)}
|
|
278
|
-
`;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function createUseTypedRouteFile(routesDeclTemplate) {
|
|
282
|
-
return `
|
|
283
|
-
${watermarkTemplate}
|
|
284
|
-
import { useRoute } from '#app';
|
|
285
|
-
import type { TypedRouteList } from './__routes';
|
|
286
|
-
|
|
287
|
-
/** Acts the same as \`useRoute\`, but typed.
|
|
288
|
-
*
|
|
289
|
-
* @exemple
|
|
290
|
-
*
|
|
291
|
-
* \`\`\`ts
|
|
292
|
-
* const route = useTypedRoute();
|
|
293
|
-
* \`\`\`
|
|
294
|
-
*
|
|
295
|
-
* \`\`\`ts
|
|
296
|
-
* const route = useTypedRoute('my-route-with-param-id');
|
|
297
|
-
* route.params.id // autocompletes!
|
|
298
|
-
* \`\`\`
|
|
299
|
-
*
|
|
300
|
-
* \`\`\`ts
|
|
301
|
-
* const route = useTypedRoute();
|
|
302
|
-
* if (route.name === 'my-route-with-param-id') {
|
|
303
|
-
* route.params.id // autocompletes!
|
|
304
|
-
* }
|
|
305
|
-
* \`\`\`
|
|
306
|
-
*/
|
|
307
|
-
export function useTypedRoute<T extends TypedRouteList = never>(
|
|
308
|
-
name?: T
|
|
309
|
-
): [T] extends [never] ? TypedRoute : TypedNamedRoute<T> {
|
|
310
|
-
const route = useRoute();
|
|
311
|
-
|
|
312
|
-
return route as any;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
export const useRoute = useTypedRoute;
|
|
316
|
-
|
|
317
|
-
`;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function createRuntimeUseTypedRouterFile(routesDeclTemplate) {
|
|
321
|
-
return `
|
|
322
|
-
${watermarkTemplate}
|
|
323
|
-
import { useRouter } from '#app';
|
|
324
|
-
import type { TypedRouter } from './typed-router';
|
|
325
|
-
|
|
326
|
-
/** Returns instances of $typedRouter and $routesList fully typed to use in your components or your Vuex/Pinia store
|
|
327
|
-
*
|
|
328
|
-
* @exemple
|
|
329
|
-
*
|
|
330
|
-
* \`\`\`ts
|
|
331
|
-
* const { router, routes } = useTypedRouter();
|
|
332
|
-
* \`\`\`
|
|
333
|
-
*/
|
|
334
|
-
export function useTypedRouter(): TypedRouter {
|
|
335
|
-
const router = useRouter();
|
|
336
|
-
|
|
337
|
-
return router;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
export const useRouter = useTypedRouter;
|
|
341
|
-
`;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const { resolveConfig, format } = prettier;
|
|
345
|
-
const defaultPrettierOptions = {
|
|
346
|
-
printWidth: 100,
|
|
347
|
-
tabWidth: 2,
|
|
348
|
-
trailingComma: "es5",
|
|
349
|
-
singleQuote: true,
|
|
350
|
-
semi: true,
|
|
351
|
-
bracketSpacing: true,
|
|
352
|
-
htmlWhitespaceSensitivity: "strict"
|
|
353
|
-
};
|
|
354
|
-
async function formatOutputWithPrettier(template) {
|
|
355
|
-
try {
|
|
356
|
-
let prettierFoundOptions = await resolveConfig(process.cwd());
|
|
357
|
-
if (!prettierFoundOptions) {
|
|
358
|
-
prettierFoundOptions = defaultPrettierOptions;
|
|
359
|
-
}
|
|
360
|
-
const formatedTemplate = format(template, {
|
|
361
|
-
...prettierFoundOptions,
|
|
362
|
-
parser: "typescript"
|
|
363
|
-
});
|
|
364
|
-
return formatedTemplate;
|
|
365
|
-
} catch (e) {
|
|
366
|
-
console.error(logSymbols.error, chalk.red("Error while formatting the output"), "\n" + e);
|
|
367
|
-
return Promise.reject(e);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
dirname(fileURLToPath(import.meta.url));
|
|
372
|
-
async function processPathAndWriteFile({
|
|
373
|
-
content,
|
|
374
|
-
fileName,
|
|
375
|
-
rootDir,
|
|
376
|
-
outDir
|
|
377
|
-
}) {
|
|
378
|
-
try {
|
|
379
|
-
const finalOutDir = outDir ?? `.nuxt/typed-router`;
|
|
380
|
-
const processedOutDir = resolve(rootDir, finalOutDir);
|
|
381
|
-
const outputFile = resolve(process.cwd(), `${processedOutDir}/${fileName}`);
|
|
382
|
-
const formatedContent = await formatOutputWithPrettier(content);
|
|
383
|
-
if (fs.existsSync(outputFile)) {
|
|
384
|
-
await writeFile(outputFile, formatedContent);
|
|
385
|
-
} else {
|
|
386
|
-
let dirList = outputFile.split("/");
|
|
387
|
-
dirList.pop();
|
|
388
|
-
const dirPath = dirList.join("/");
|
|
389
|
-
await mkdirp(dirPath);
|
|
390
|
-
await writeFile(outputFile, formatedContent);
|
|
391
|
-
}
|
|
392
|
-
} catch (e) {
|
|
393
|
-
return Promise.reject(e);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
async function writeFile(path, content) {
|
|
397
|
-
try {
|
|
398
|
-
await fs.writeFileSync(path, content);
|
|
399
|
-
} catch (e) {
|
|
400
|
-
console.log(logSymbols.error, chalk.red(`Error while saving file at ${path}`, e));
|
|
401
|
-
return Promise.reject(e);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function handlePluginFileSave({
|
|
406
|
-
nuxt,
|
|
407
|
-
rootDir,
|
|
408
|
-
routesDeclTemplate
|
|
409
|
-
}) {
|
|
410
|
-
const pluginName = "__typed-router.ts";
|
|
411
|
-
nuxt.hook("build:done", async () => {
|
|
412
|
-
const pluginFolder = `${rootDir}/plugins`;
|
|
413
|
-
await processPathAndWriteFile({
|
|
414
|
-
outDir: pluginFolder,
|
|
415
|
-
rootDir,
|
|
416
|
-
fileName: pluginName,
|
|
417
|
-
content: createRuntimePluginFile(routesDeclTemplate)
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
let previousGeneratedRoutes = "";
|
|
423
|
-
async function saveGeneratedFiles({
|
|
424
|
-
rootDir,
|
|
425
|
-
autoImport,
|
|
426
|
-
outputData: { routesDeclTemplate, routesList, routesObjectTemplate, routesParams }
|
|
427
|
-
}) {
|
|
428
|
-
const filesMap = [
|
|
429
|
-
{
|
|
430
|
-
fileName: "__useTypedRouter.ts",
|
|
431
|
-
content: createRuntimeUseTypedRouterFile()
|
|
432
|
-
},
|
|
433
|
-
{
|
|
434
|
-
fileName: "__useTypedRoute.ts",
|
|
435
|
-
content: createUseTypedRouteFile()
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
fileName: `__routes.ts`,
|
|
439
|
-
content: createRuntimeRoutesFile({
|
|
440
|
-
routesList,
|
|
441
|
-
routesObjectTemplate,
|
|
442
|
-
routesDeclTemplate,
|
|
443
|
-
routesParams
|
|
444
|
-
})
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
fileName: `typed-router.d.ts`,
|
|
448
|
-
content: createDeclarationRoutesFile(autoImport)
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
fileName: "index.ts",
|
|
452
|
-
content: createRuntimeIndexFile()
|
|
453
|
-
}
|
|
454
|
-
];
|
|
455
|
-
await Promise.all(
|
|
456
|
-
filesMap.map(({ content, fileName }) => processPathAndWriteFile({ rootDir, content, fileName }))
|
|
457
|
-
);
|
|
458
|
-
if (previousGeneratedRoutes !== routesList.join(",")) {
|
|
459
|
-
previousGeneratedRoutes = routesList.join(",");
|
|
460
|
-
console.log(logSymbols.success, `Router autocompletions generated \u{1F6A6}`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function isItemLast(array, index) {
|
|
465
|
-
return array ? index === array.length - 1 : false;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const routeParamExtractRegxp = /(:(\w+)(\(.+\)[*+]?)?(\?)?)+/g;
|
|
469
|
-
function extractRouteParamsFromPath(path, isIndexFileForRouting, previousParams) {
|
|
470
|
-
let params = [];
|
|
471
|
-
let matches;
|
|
472
|
-
do {
|
|
473
|
-
matches = routeParamExtractRegxp.exec(path);
|
|
474
|
-
if (matches) {
|
|
475
|
-
const [_, mtch, key, catchAll, optional] = matches;
|
|
476
|
-
if (mtch) {
|
|
477
|
-
params.push({ name: key, optional: !!optional, catchAll: !!catchAll });
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
} while (matches);
|
|
481
|
-
let allMergedParams = params.map(
|
|
482
|
-
({ name, optional, catchAll }) => ({
|
|
483
|
-
key: name,
|
|
484
|
-
required: !optional,
|
|
485
|
-
notRequiredOnPage: optional,
|
|
486
|
-
catchAll
|
|
487
|
-
})
|
|
488
|
-
);
|
|
489
|
-
if (previousParams?.length) {
|
|
490
|
-
allMergedParams = previousParams.map((m) => ({ ...m, required: false })).concat(allMergedParams);
|
|
491
|
-
}
|
|
492
|
-
if (!params.length && isIndexFileForRouting) {
|
|
493
|
-
const lastItem = allMergedParams[allMergedParams.length - 1];
|
|
494
|
-
if (lastItem) {
|
|
495
|
-
lastItem.required = true;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return allMergedParams;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function extractMatchingSiblings(mainRoute, siblingRoutes) {
|
|
502
|
-
return siblingRoutes?.filter((s) => {
|
|
503
|
-
const chunkName = extractChunkMain(mainRoute.file);
|
|
504
|
-
if (chunkName && s.name) {
|
|
505
|
-
const siblingChunkName = extractChunkMain(s.file);
|
|
506
|
-
if (!siblingChunkName)
|
|
507
|
-
return false;
|
|
508
|
-
return chunkName === siblingChunkName;
|
|
509
|
-
}
|
|
510
|
-
return false;
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
function extractUnMatchingSiblings(mainRoute, siblingRoutes) {
|
|
514
|
-
return siblingRoutes?.filter((s) => {
|
|
515
|
-
const chunkName = extractChunkMain(mainRoute.file);
|
|
516
|
-
if (chunkName) {
|
|
517
|
-
const siblingChunkName = extractChunkMain(s.file);
|
|
518
|
-
if (!siblingChunkName)
|
|
519
|
-
return false;
|
|
520
|
-
return chunkName !== siblingChunkName;
|
|
521
|
-
}
|
|
522
|
-
return false;
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
function extractChunkMain(chunkName) {
|
|
526
|
-
let chunkArray = chunkName?.split("/");
|
|
527
|
-
return chunkArray?.join("/");
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function walkThoughRoutes({
|
|
531
|
-
route,
|
|
532
|
-
level,
|
|
533
|
-
siblings,
|
|
534
|
-
parentName,
|
|
535
|
-
previousParams,
|
|
536
|
-
output,
|
|
537
|
-
isLast
|
|
538
|
-
}) {
|
|
539
|
-
const matchingSiblings = extractMatchingSiblings(route, siblings);
|
|
540
|
-
const haveMatchingSiblings = !!matchingSiblings?.length && route.path !== "/";
|
|
541
|
-
const chunkArray = route.file?.split("/") ?? [];
|
|
542
|
-
const lastChunkArray = chunkArray[chunkArray?.length - 1].split(".vue")[0];
|
|
543
|
-
const isRootSibling = lastChunkArray === "index";
|
|
544
|
-
if (route.children?.length && !haveMatchingSiblings || !route.children?.length && haveMatchingSiblings && isRootSibling) {
|
|
545
|
-
let childrenChunks = haveMatchingSiblings ? matchingSiblings : route.children;
|
|
546
|
-
const splittedPaths = route.path.split("/");
|
|
547
|
-
const parentPath = splittedPaths[splittedPaths.length - 1];
|
|
548
|
-
const nameKey = camelCase(parentPath || "index");
|
|
549
|
-
output.routesObjectTemplate += `${nameKey}:{`;
|
|
550
|
-
output.routesDeclTemplate += `"${nameKey}":{`;
|
|
551
|
-
const allRouteParams = extractRouteParamsFromPath(route.path, false, previousParams);
|
|
552
|
-
childrenChunks?.map(
|
|
553
|
-
(routeConfig, index) => walkThoughRoutes({
|
|
554
|
-
route: routeConfig,
|
|
555
|
-
level: level + 1,
|
|
556
|
-
siblings: extractUnMatchingSiblings(route, siblings),
|
|
557
|
-
parentName: nameKey,
|
|
558
|
-
previousParams: allRouteParams,
|
|
559
|
-
output,
|
|
560
|
-
isLast: isItemLast(childrenChunks, index)
|
|
561
|
-
})
|
|
562
|
-
);
|
|
563
|
-
output.routesObjectTemplate += "},";
|
|
564
|
-
output.routesDeclTemplate += `}${isLast ? "" : ","}`;
|
|
565
|
-
} else if (route.name) {
|
|
566
|
-
let splitted = [];
|
|
567
|
-
splitted = route.name.split("-");
|
|
568
|
-
splitted = splitted.slice(level, splitted.length);
|
|
569
|
-
if (splitted[0] === parentName) {
|
|
570
|
-
splitted.splice(0, 1);
|
|
571
|
-
}
|
|
572
|
-
const keyName = route.path === "" ? "index" : camelCase(splitted.join("-")) || "index";
|
|
573
|
-
output.routesObjectTemplate += `'${keyName}': '${route.name}' as const,`;
|
|
574
|
-
output.routesDeclTemplate += `"${keyName}": "${route.name}"${isLast ? "" : ","}`;
|
|
575
|
-
output.routesList.push(route.name);
|
|
576
|
-
const isIndexFileForRouting = route.path === "";
|
|
577
|
-
const allRouteParams = extractRouteParamsFromPath(
|
|
578
|
-
route.path,
|
|
579
|
-
isIndexFileForRouting,
|
|
580
|
-
previousParams
|
|
581
|
-
);
|
|
582
|
-
output.routesParams.push({
|
|
583
|
-
name: route.name,
|
|
584
|
-
params: allRouteParams
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
function constructRouteMap(routesConfig) {
|
|
590
|
-
try {
|
|
591
|
-
let routesObjectTemplate = "{";
|
|
592
|
-
let routesDeclTemplate = "{";
|
|
593
|
-
let routesList = [];
|
|
594
|
-
let routesParams = [];
|
|
595
|
-
const output = { routesObjectTemplate, routesDeclTemplate, routesList, routesParams };
|
|
596
|
-
startGenerator({
|
|
597
|
-
output,
|
|
598
|
-
routesConfig
|
|
599
|
-
});
|
|
600
|
-
return output;
|
|
601
|
-
} catch (e) {
|
|
602
|
-
throw new Error("Generation failed");
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
function startGenerator({ output, routesConfig }) {
|
|
606
|
-
routesConfig.forEach((route, index) => {
|
|
607
|
-
const rootSiblingsRoutes = routesConfig.filter((rt) => rt.chunkName !== route.chunkName);
|
|
608
|
-
walkThoughRoutes({
|
|
609
|
-
route,
|
|
610
|
-
level: 0,
|
|
611
|
-
output,
|
|
612
|
-
siblings: rootSiblingsRoutes,
|
|
613
|
-
isLast: isItemLast(routesConfig, index)
|
|
614
|
-
});
|
|
615
|
-
});
|
|
616
|
-
output.routesObjectTemplate += "}";
|
|
617
|
-
output.routesDeclTemplate += "}";
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
function createTypedRouter({ plugin, nuxt }) {
|
|
621
|
-
try {
|
|
622
|
-
const rootDir = nuxt.options.rootDir;
|
|
623
|
-
const autoImport = nuxt.options.imports.autoImport ?? true;
|
|
624
|
-
extendPages(async (routes) => {
|
|
625
|
-
if (routes.length) {
|
|
626
|
-
const outputData = constructRouteMap(routes);
|
|
627
|
-
if (plugin) {
|
|
628
|
-
handlePluginFileSave({
|
|
629
|
-
nuxt,
|
|
630
|
-
routesDeclTemplate: outputData.routesDeclTemplate,
|
|
631
|
-
rootDir
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
await saveGeneratedFiles({
|
|
635
|
-
autoImport,
|
|
636
|
-
rootDir,
|
|
637
|
-
outputData
|
|
638
|
-
});
|
|
639
|
-
} else {
|
|
640
|
-
console.log(
|
|
641
|
-
logSymbols.warning,
|
|
642
|
-
chalk.yellow(
|
|
643
|
-
`[typed-router] No routes defined. Check if your ${chalk.underline(
|
|
644
|
-
chalk.bold("pages")
|
|
645
|
-
)} folder exists and remove ${chalk.underline(chalk.bold("app.vue"))}`
|
|
646
|
-
)
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
} catch (e) {
|
|
651
|
-
console.error(chalk.red("Error while generating routes definitions model"), "\n" + e);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
const module = defineNuxtModule({
|
|
656
|
-
meta: {
|
|
657
|
-
name: "nuxt-typed-router",
|
|
658
|
-
configKey: "nuxtTypedRouter",
|
|
659
|
-
compatibility: { nuxt: "^3.0.0-rc.1", bridge: false }
|
|
660
|
-
},
|
|
661
|
-
defaults: {
|
|
662
|
-
plugin: false
|
|
663
|
-
},
|
|
664
|
-
setup(moduleOptions, nuxt) {
|
|
665
|
-
const rootDir = nuxt.options.rootDir;
|
|
666
|
-
const { plugin } = moduleOptions;
|
|
667
|
-
const { resolve } = createResolver(import.meta.url);
|
|
668
|
-
nuxt.options.alias = {
|
|
669
|
-
...nuxt.options.alias,
|
|
670
|
-
"@typed-router": resolve(`${rootDir}/.nuxt/typed-router`)
|
|
671
|
-
};
|
|
672
|
-
nuxt.options.typescript.tsConfig = {
|
|
673
|
-
include: ["./typed-router/typed-router.d.ts"]
|
|
674
|
-
};
|
|
675
|
-
const typedRouterOptions = { nuxt, plugin };
|
|
676
|
-
nuxt.hook("pages:extend", () => createTypedRouter(typedRouterOptions));
|
|
677
|
-
createTypedRouter(typedRouterOptions);
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
export { module as default };
|
|
6
|
+
export default _module;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
|
|
2
|
-
import { ModuleOptions } from './module'
|
|
2
|
+
import { ModuleOptions, ModuleHooks, ModuleRuntimeConfig, ModulePublicRuntimeConfig } from './module'
|
|
3
3
|
|
|
4
4
|
declare module '@nuxt/schema' {
|
|
5
5
|
interface NuxtConfig { ['nuxtTypedRouter']?: Partial<ModuleOptions> }
|
|
6
6
|
interface NuxtOptions { ['nuxtTypedRouter']?: ModuleOptions }
|
|
7
|
+
interface NuxtHooks extends ModuleHooks {}
|
|
8
|
+
interface RuntimeConfig extends ModuleRuntimeConfig {}
|
|
9
|
+
interface PublicRuntimeConfig extends ModulePublicRuntimeConfig {}
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
export {
|
|
13
|
+
export { default } from './module'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-typed-router",
|
|
3
|
-
"version": "2.1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Provide autocompletion for pages route names generated by Nuxt router",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/module.cjs",
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
"dev": "nuxi dev playground",
|
|
21
21
|
"dev:build": "nuxi build playground",
|
|
22
22
|
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
|
|
23
|
-
"build:test": "cross-env NUXT_BUILD_TYPE=stub
|
|
24
|
-
"test": "
|
|
25
|
-
"test:
|
|
26
|
-
"
|
|
27
|
-
"docs:
|
|
23
|
+
"build:test": "cross-env NUXT_BUILD_TYPE=stub pnpm run prepack && pnpm run dev:build",
|
|
24
|
+
"test:fixtures": "nuxt-module-build --stub && nuxi prepare test/fixtures/simple && vitest run --dir test",
|
|
25
|
+
"test:types": "nuxi prepare test/fixtures/simple && cd test/fixtures/simple && vue-tsc --noEmit",
|
|
26
|
+
"test": "pnpm run test:fixtures && pnpm run test:types",
|
|
27
|
+
"docs:dev": "cd docs && pnpm run dev",
|
|
28
|
+
"docs:build": "npm run dev:prepare && cd docs && nuxi generate",
|
|
29
|
+
"prepare": "pnpm run dev:prepare"
|
|
28
30
|
},
|
|
29
31
|
"publishConfig": {
|
|
30
32
|
"access": "public"
|
|
@@ -77,6 +79,7 @@
|
|
|
77
79
|
"nuxt": "3.0.0",
|
|
78
80
|
"typescript": "^4.9.4",
|
|
79
81
|
"vitest": "^0.27.2",
|
|
80
|
-
"vue-router": "^4.1.6"
|
|
82
|
+
"vue-router": "^4.1.6",
|
|
83
|
+
"vue-tsc": "^1.0.24"
|
|
81
84
|
}
|
|
82
85
|
}
|