nuxt-typed-router 2.2.2-beta.1 → 2.3.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/dist/module.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "name": "nuxt-typed-router",
3
3
  "configKey": "nuxtTypedRouter",
4
4
  "compatibility": {
5
- "nuxt": "^3.0.0-rc.1",
5
+ "nuxt": "^3.0.0",
6
6
  "bridge": false
7
7
  },
8
- "version": "2.2.2-beta.1"
8
+ "version": "2.3.0-beta.0"
9
9
  }
package/dist/module.mjs CHANGED
@@ -15,6 +15,8 @@ class ModuleOptionsStore {
15
15
  this.strict = false;
16
16
  this.autoImport = false;
17
17
  this.rootDir = "";
18
+ this.i18n = false;
19
+ this.i18nLocales = [];
18
20
  }
19
21
  updateOptions(options) {
20
22
  if (options.plugin != null)
@@ -25,6 +27,10 @@ class ModuleOptionsStore {
25
27
  this.autoImport = options.autoImport;
26
28
  if (options.rootDir != null)
27
29
  this.rootDir = options.rootDir;
30
+ if (options.i18n != null)
31
+ this.i18n = options.i18n;
32
+ if (options.i18nLocales != null)
33
+ this.i18nLocales = options.i18nLocales;
28
34
  }
29
35
  getResolvedStrictOptions() {
30
36
  let resolved;
@@ -171,8 +177,6 @@ function createRoutesTypesFile({
171
177
 
172
178
  ${createRoutesNamedLocationsResolvedExport(routesParams)}
173
179
 
174
-
175
-
176
180
  export type RoutesNamesListRecord = ${routesDeclTemplate};
177
181
 
178
182
  export const routesNames = ${routesObjectTemplate};
@@ -209,6 +213,7 @@ function createTypedRouterFile() {
209
213
  RouteLocationNormalizedLoaded,
210
214
  RouteLocationRaw,
211
215
  Router,
216
+ RouteLocationPathRaw
212
217
  } from 'vue-router';
213
218
  import type {
214
219
  RoutesNamedLocations,
@@ -227,22 +232,26 @@ function createTypedRouterFile() {
227
232
  * {@link RouteLocationRaw}
228
233
  * */
229
234
  export type TypedRouteLocationRaw =
230
- | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params' ${returnIfTrue(
235
+ | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & RoutesNamedLocations)
236
+ ${returnIfFalse(strictOptions.router.strictToArgument, "| string")}
237
+ ${returnIfTrue(
231
238
  strictOptions.router.strictRouteLocation,
232
- `| 'path'`
233
- )}> & RoutesNamedLocations)
234
- ${returnIfFalse(strictOptions.router.strictToArgument, "| string")};
239
+ `| Omit<RouteLocationPathRaw, 'path'>`,
240
+ "| RouteLocationPathRaw"
241
+ )};
235
242
 
236
243
 
237
244
  /**
238
245
  * Alternative version of {@link TypedRouteLocationRaw} but with a name generic
239
246
  */
240
247
  export type TypedRouteLocationRawFromName<T extends RoutesNamesList> =
241
- | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params' ${returnIfTrue(
248
+ | (Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & TypedLocationAsRelativeRaw<T>)
249
+ ${returnIfFalse(strictOptions.router.strictToArgument, "| string")}
250
+ ${returnIfTrue(
242
251
  strictOptions.router.strictRouteLocation,
243
- `| 'path'`
244
- )}> & TypedLocationAsRelativeRaw<T>)
245
- ${returnIfFalse(strictOptions.router.strictToArgument, "| string")};
252
+ `| Omit<RouteLocationPathRaw, 'path'>`,
253
+ "| RouteLocationPathRaw"
254
+ )}
246
255
 
247
256
  /**
248
257
  * Generic providing inference and dynamic inclusion of \`params\` property
@@ -344,7 +353,7 @@ function createTypedRouterFile() {
344
353
  }
345
354
 
346
355
  function createTypedRouterDefinitionFile() {
347
- const { plugin, autoImport } = moduleOptionStore;
356
+ const { plugin, autoImport, i18n } = moduleOptionStore;
348
357
  const strictOptions = moduleOptionStore.getResolvedStrictOptions();
349
358
  return (
350
359
  /* typescript */
@@ -352,12 +361,13 @@ function createTypedRouterDefinitionFile() {
352
361
 
353
362
  import type { NuxtLinkProps } from '#app';
354
363
  import type { DefineComponent } from 'vue';
355
- import type { RouteLocationRaw } from 'vue-router';
364
+ import type { RouteLocationRaw, RouteLocationPathRaw } from 'vue-router';
356
365
  import type { RoutesNamedLocations, RoutesNamesListRecord } from './__routes';
357
366
  import type {TypedRouter, TypedRoute} from './__router';
358
367
  import { useRoute as _useRoute } from './__useTypedRoute';
359
368
  import { useRouter as _useRouter } from './__useTypedRouter';
360
369
  import { navigateTo as _navigateTo } from './__navigateTo';
370
+ import { useLocalePath as _useLocalePath, useLocaleRoute as _useLocaleRoute} from './__i18n-router';
361
371
 
362
372
  declare global {
363
373
 
@@ -367,18 +377,28 @@ function createTypedRouterDefinitionFile() {
367
377
  `
368
378
  const useRoute: typeof _useRoute;
369
379
  const useRouter: typeof _useRouter;
370
- const navigateTo: typeof _navigateTo;`
380
+ const navigateTo: typeof _navigateTo;
381
+ ${returnIfTrue(
382
+ i18n,
383
+ /* typescript */
384
+ `
385
+ const useLocalePath: typeof _useLocalePath;
386
+ const useLocaleRoute: typeof _useLocaleRoute;
387
+ `
388
+ )}
389
+ `
371
390
  )}
372
391
  }
373
392
 
374
393
  type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
375
- to: ${returnIfFalse(
376
- strictOptions.NuxtLink.strictToArgument,
377
- "string |"
378
- )} Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params' ${returnIfTrue(
394
+ to:
395
+ Omit<Exclude<RouteLocationRaw, string>, 'name' | 'params'> & RoutesNamedLocations
396
+ ${returnIfFalse(strictOptions.NuxtLink.strictToArgument, "| string")}
397
+ ${returnIfTrue(
379
398
  strictOptions.NuxtLink.strictRouteLocation,
380
- `| 'path'`
381
- )}> & RoutesNamedLocations;
399
+ `| Omit<RouteLocationPathRaw, 'path'>`,
400
+ "| RouteLocationPathRaw"
401
+ )}
382
402
  };
383
403
 
384
404
  export type TypedNuxtLink = DefineComponent<
@@ -426,31 +446,33 @@ function createTypedRouterDefinitionFile() {
426
446
  }
427
447
 
428
448
  function createIndexFile() {
449
+ const { i18n } = moduleOptionStore;
429
450
  return (
430
451
  /* typescript */
431
452
  `
432
453
 
433
- export type {
434
- TypedLocationAsRelativeRaw,
435
- TypedResolvedMatcherLocation,
436
- TypedRoute,
437
- TypedRouteFromName,
438
- TypedRouteLocationFromName,
439
- TypedRouteLocationRaw,
440
- TypedRouteLocationRawFromName,
441
- TypedRouter,
442
- } from './__router';
443
- export { routesNames } from './__routes';
444
- export type {
445
- RoutesNamedLocations,
446
- RoutesNamedLocationsResolved,
447
- RoutesNamesList,
448
- RoutesNamesListRecord,
449
- RoutesParamsRecord,
450
- } from './__routes';
451
- export { useRoute } from './__useTypedRoute';
452
- export { useRouter } from './__useTypedRouter';
453
- export { navigateTo } from './__navigateTo';
454
+ export type {
455
+ TypedLocationAsRelativeRaw,
456
+ TypedResolvedMatcherLocation,
457
+ TypedRoute,
458
+ TypedRouteFromName,
459
+ TypedRouteLocationFromName,
460
+ TypedRouteLocationRaw,
461
+ TypedRouteLocationRawFromName,
462
+ TypedRouter,
463
+ } from './__router';
464
+ export { routesNames } from './__routes';
465
+ export type {
466
+ RoutesNamedLocations,
467
+ RoutesNamedLocationsResolved,
468
+ RoutesNamesList,
469
+ RoutesNamesListRecord,
470
+ RoutesParamsRecord,
471
+ } from './__routes';
472
+ export { useRoute } from './__useTypedRoute';
473
+ export { useRouter } from './__useTypedRouter';
474
+ export { navigateTo } from './__navigateTo';
475
+ ${returnIfTrue(i18n, `export {useLocalePath, useLocaleRoute} from './__i18n-router.ts';`)}
454
476
  `
455
477
  );
456
478
  }
@@ -598,6 +620,35 @@ function createTypeUtilsRuntimeFile() {
598
620
  );
599
621
  }
600
622
 
623
+ function createi18nRouterFile() {
624
+ const { i18nLocales } = moduleOptionStore;
625
+ return (
626
+ /* typescript */
627
+ `
628
+
629
+ import { useLocalePath as _useLocalePath, useLocaleRoute as _useLocaleRoute} from 'vue-i18n-routing';
630
+ import type {TypedRouteLocationRawFromName, TypedRouteFromName} from './__router';
631
+ import type {RoutesNamesList} from './__routes';
632
+
633
+ export type I18nLocales = ${i18nLocales.map((loc) => `"${loc}"`).join("|")};
634
+
635
+ export type TypedToLocalePath = <T extends RoutesNamesList>(to: TypedRouteLocationRawFromName<T>, locale?: I18nLocales | undefined) => TypedRouteLocationRawFromName<T>;
636
+
637
+ export function useLocalePath(options?: Pick<NonNullable<Parameters<typeof _useLocalePath>[0]>, 'i18n'>): TypedToLocalePath {
638
+ return _useLocalePath(options) as any;
639
+ }
640
+
641
+ export type TypedLocaleRoute = <T extends RoutesNamesList>(to: TypedRouteLocationRawFromName<T>, locale?: I18nLocales | undefined) => TypedRouteFromName<T>;
642
+
643
+
644
+ export function useLocaleRoute(options?: Pick<NonNullable<Parameters<typeof _useLocaleRoute>[0]>, 'i18n'>): TypedLocaleRoute {
645
+ return _useLocaleRoute(options) as any;
646
+ }
647
+
648
+ `
649
+ );
650
+ }
651
+
601
652
  async function handleAddPlugin() {
602
653
  const pluginName = "__typed-router.plugin.ts";
603
654
  addPluginTemplate({
@@ -683,6 +734,7 @@ let previousGeneratedRoutes = "";
683
734
  async function saveGeneratedFiles({
684
735
  outputData: { routesDeclTemplate, routesList, routesObjectTemplate, routesParams }
685
736
  }) {
737
+ const { i18n } = moduleOptionStore;
686
738
  const filesMap = [
687
739
  {
688
740
  fileName: "__useTypedRouter.ts",
@@ -722,6 +774,12 @@ async function saveGeneratedFiles({
722
774
  content: createIndexFile()
723
775
  }
724
776
  ];
777
+ if (i18n) {
778
+ filesMap.push({
779
+ fileName: "__i18n-router.ts",
780
+ content: createi18nRouterFile()
781
+ });
782
+ }
725
783
  await Promise.all(
726
784
  filesMap.map(({ content, fileName }) => {
727
785
  const waterMakeredContent = `
@@ -800,6 +858,22 @@ function extractChunkMain(chunkName) {
800
858
  return chunkArray?.join("/");
801
859
  }
802
860
 
861
+ function createKeyedName(route) {
862
+ const splittedPaths = route.path.split("/");
863
+ const parentPath = splittedPaths[splittedPaths.length - 1];
864
+ const nameKey = camelCase(parentPath || "index");
865
+ return nameKey;
866
+ }
867
+ function createNameKeyFromFullName(route, level, parentName) {
868
+ let splitted = [];
869
+ splitted = route.name?.split("-") ?? [];
870
+ splitted = splitted.slice(level, splitted.length);
871
+ if (splitted[0] === parentName) {
872
+ splitted.splice(0, 1);
873
+ }
874
+ const keyName = route.path === "" ? "index" : camelCase(splitted.join("-")) || "index";
875
+ return keyName;
876
+ }
803
877
  function walkThoughRoutes({
804
878
  route,
805
879
  level,
@@ -816,9 +890,14 @@ function walkThoughRoutes({
816
890
  const isRootSibling = lastChunkArray === "index";
817
891
  if (route.children?.length && !haveMatchingSiblings || !route.children?.length && haveMatchingSiblings && isRootSibling) {
818
892
  let childrenChunks = haveMatchingSiblings ? matchingSiblings : route.children;
819
- const splittedPaths = route.path.split("/");
820
- const parentPath = splittedPaths[splittedPaths.length - 1];
821
- const nameKey = camelCase(parentPath || "index");
893
+ let nameKey = createKeyedName(route);
894
+ const hasSamePathI18nSibling = siblings?.some((sibling) => {
895
+ const _name = createKeyedName(sibling);
896
+ return _name === nameKey;
897
+ });
898
+ if (hasSamePathI18nSibling) {
899
+ nameKey = camelCase(route.path.split("/").join("-"));
900
+ }
822
901
  output.routesObjectTemplate += `${nameKey}:{`;
823
902
  output.routesDeclTemplate += `"${nameKey}":{`;
824
903
  const allRouteParams = extractRouteParamsFromPath(route.path, false, previousParams);
@@ -836,13 +915,14 @@ function walkThoughRoutes({
836
915
  output.routesObjectTemplate += "},";
837
916
  output.routesDeclTemplate += `}${isLast ? "" : ","}`;
838
917
  } else if (route.name) {
839
- let splitted = [];
840
- splitted = route.name.split("-");
841
- splitted = splitted.slice(level, splitted.length);
842
- if (splitted[0] === parentName) {
843
- splitted.splice(0, 1);
918
+ let keyName = createNameKeyFromFullName(route, level, parentName);
919
+ const hasSameNameI18nSibling = siblings?.some((sibling) => {
920
+ const _name = createNameKeyFromFullName(sibling, level, parentName);
921
+ return _name === keyName;
922
+ });
923
+ if (hasSameNameI18nSibling) {
924
+ keyName = camelCase(route.name);
844
925
  }
845
- const keyName = route.path === "" ? "index" : camelCase(splitted.join("-")) || "index";
846
926
  output.routesObjectTemplate += `'${keyName}': '${route.name}' as const,`;
847
927
  output.routesDeclTemplate += `"${keyName}": "${route.name}"${isLast ? "" : ","}`;
848
928
  output.routesList.push(route.name);
@@ -877,7 +957,7 @@ function constructRouteMap(routesConfig) {
877
957
  }
878
958
  function startGenerator({ output, routesConfig }) {
879
959
  routesConfig.forEach((route, index) => {
880
- const rootSiblingsRoutes = routesConfig.filter((rt) => rt.chunkName !== route.chunkName);
960
+ const rootSiblingsRoutes = routesConfig.filter((rt) => rt.path !== route.path);
881
961
  walkThoughRoutes({
882
962
  route,
883
963
  level: 0,
@@ -946,16 +1026,21 @@ const module = defineNuxtModule({
946
1026
  meta: {
947
1027
  name: "nuxt-typed-router",
948
1028
  configKey: "nuxtTypedRouter",
949
- compatibility: { nuxt: "^3.0.0-rc.1", bridge: false }
1029
+ compatibility: { nuxt: "^3.0.0", bridge: false }
950
1030
  },
951
1031
  defaults: {
952
1032
  plugin: false,
953
1033
  strict: false
954
1034
  },
955
1035
  setup(moduleOptions, nuxt) {
956
- const rootDir = nuxt.options.rootDir;
957
- moduleOptionStore.updateOptions(moduleOptions);
958
1036
  const { resolve } = createResolver(import.meta.url);
1037
+ const rootDir = nuxt.options.rootDir;
1038
+ const hasi18nModuleRegistered = !!nuxt.options.modules.find((mod) => mod === "@nuxtjs/i18n");
1039
+ moduleOptionStore.updateOptions({
1040
+ ...moduleOptions,
1041
+ i18n: hasi18nModuleRegistered,
1042
+ i18nLocales: nuxt.options?.i18n?.locales ?? []
1043
+ });
959
1044
  nuxt.options.alias = {
960
1045
  ...nuxt.options.alias,
961
1046
  "@typed-router": resolve(`${rootDir}/.nuxt/typed-router`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-typed-router",
3
- "version": "2.2.2-beta.1",
3
+ "version": "2.3.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",
@@ -21,10 +21,10 @@
21
21
  "dev:build": "nuxi build playground",
22
22
  "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground && pnpm run test:prepare-fixtures",
23
23
  "build:test": "cross-env NUXT_BUILD_TYPE=stub pnpm run prepack && pnpm run dev:build",
24
- "test:prepare-fixtures": "nuxi prepare test/fixtures/simple && nuxi generate test/fixtures/complex",
24
+ "test:prepare-fixtures": "nuxi prepare test/fixtures/simple && nuxi prepare test/fixtures/withOptions && nuxi prepare test/fixtures/complex",
25
25
  "test:fixtures": " vitest run --dir test",
26
26
  "test:types": "pnpm run typecheck && pnpm run test:vue && vitest typecheck --run --dir test",
27
- "test:vue": "vue-tsc -p test/fixtures/simple/tsconfig.json --noEmit && vue-tsc -p test/fixtures/complex/tsconfig.json --noEmit",
27
+ "test:vue": "vue-tsc -p test/fixtures/simple/tsconfig.json --noEmit && vue-tsc -p test/fixtures/complex/tsconfig.json --noEmit && vue-tsc -p test/fixtures/withOptions/tsconfig.json --noEmit",
28
28
  "test": "pnpm run dev:prepare && pnpm run test:fixtures && pnpm run test:types",
29
29
  "docs:dev": "cd docs && pnpm run dev",
30
30
  "docs:build": "npm run dev:prepare && cd docs && nuxi generate",
@@ -59,31 +59,33 @@
59
59
  "dependencies": {
60
60
  "@nuxt/kit": "3.1.1",
61
61
  "chalk": "^5.2.0",
62
+ "defu": "^6.1.2",
62
63
  "lodash-es": "^4.17.21",
63
64
  "log-symbols": "^5.1.0",
64
65
  "mkdirp": "^1.0.4",
65
66
  "pathe": "1.1.0",
66
- "prettier": "2.8.3",
67
- "defu": "^6.1.2"
67
+ "prettier": "2.8.3"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@nuxt/module-builder": "^0.2.1",
71
71
  "@nuxt/test-utils": "^3.1.1",
72
72
  "@nuxt/types": "^2.15.8",
73
73
  "@nuxtjs/eslint-config-typescript": "^12.0.0",
74
+ "@nuxtjs/i18n": "8.0.0-beta.9",
74
75
  "@types/lodash-es": "^4.17.6",
75
76
  "@types/mkdirp": "^1.0.2",
76
77
  "@types/node": "^17.0.23",
77
78
  "@types/prettier": "^2.7.2",
78
- "@typescript-eslint/eslint-plugin": "^5.49.0",
79
- "@typescript-eslint/parser": "^5.49.0",
79
+ "@typescript-eslint/eslint-plugin": "^5.50.0",
80
+ "@typescript-eslint/parser": "^5.50.0",
81
+ "@vue/test-utils": "^2.2.8",
80
82
  "cross-env": "^7.0.3",
81
83
  "eslint": "8.33.0",
82
84
  "eslint-config-prettier": "^8.6.0",
83
85
  "eslint-plugin-vue": "^9.9.0",
84
86
  "nuxt": "3.1.1",
85
87
  "playwright": "1.30.0",
86
- "typescript": "^4.9.4",
88
+ "typescript": "^4.9.5",
87
89
  "vitest": "^0.28.3",
88
90
  "vue-eslint-parser": "^9.1.0",
89
91
  "vue-router": "^4.1.6",