nuxt-typed-router 2.0.0-beta.0 โ†’ 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # ๐Ÿš—๐Ÿšฆ Typed Router for Nuxt
1
+
2
2
 
3
3
  <p align="center">
4
- <img width='100' src="https://raw.githubusercontent.com/victorgarciaesgi/nuxt-typed-router/master/.github/images/logo.png" alt="nuxt-typed-router logo">
4
+ <img src="https://raw.githubusercontent.com/victorgarciaesgi/nuxt-typed-router/master/.github/images/cover.png" alt="nuxt-typed-router cover">
5
5
  </p>
6
6
 
7
7
 
@@ -18,26 +18,40 @@
18
18
 
19
19
  ## Provide a type safe router to Nuxt with auto-generated typed definitions for route names and autocompletion for route params
20
20
 
21
- - ๐ŸŽ Provides a hook `useTypedRouter` that returns an alias of `$typedRouter` and also a typed list of your routes
22
- - ๐Ÿšš Expose a global method `$typedRouter` (clone of vue-router), but typed with the routes defined in `pages` directory
23
- - ๐Ÿšฆ Provides auto-completion and errors for route params in `push` and `replace` methods
21
+ - Automaticaly provides types check and autocomplete to `NuxtLink`
22
+ - Exports a `useTypedRouter` and `useTypedRoute` composable
23
+ - Supports routes defined in `config.extendRoutes`
24
+ - Infer route params based on route name
25
+ - Provide global `$typedRouter` util
26
+
27
+ <br/>
24
28
 
25
29
  <br/>
30
+ <p align="center">
31
+ <img src="https://github.com/victorgarciaesgi/nuxt-typed-router/blob/master/.github/images/nuxt-router.gif?raw=true"/>
32
+ </p>
33
+ <br/>
34
+
35
+
36
+ # Documentation
37
+
38
+ [![Open in StackBlitz](https://github.com/victorgarciaesgi/nuxt-typed-router/blob/master/.github/images/redirectDoc.svg?raw=true)](https://nuxt-typed-router.vercel.app/)
26
39
 
27
- Demo ๐Ÿงช : [nuxt-typed-router-demo](https://github.com/victorgarciaesgi/nuxt-typed-router-demo)
40
+ # Play with it
41
+ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/github-7e4xvw?file=store/testRouter.ts)
28
42
 
29
- ## Compatibility:
43
+ Demo repo ๐Ÿงช : [nuxt-typed-router-demo](https://github.com/victorgarciaesgi/nuxt-typed-router-demo)
44
+
45
+ <br/>
46
+
47
+ # Compatibility:
30
48
 
31
49
  - Nuxt 3
32
50
  - Nuxt 2 (via [`nuxt2` branch](https://github.com/victorgarciaesgi/nuxt-typed-router/tree/nuxt2))
33
51
 
34
- <br/>
35
- <p align="center">
36
- <img src="https://github.com/victorgarciaesgi/nuxt-typed-router/blob/master/.github/images/in-action.gif?raw=true"/>
37
- </p>
38
- <br/>
39
52
 
40
- # Installation
53
+
54
+ # Quick start
41
55
 
42
56
  ### For Nuxt 3
43
57
 
@@ -47,9 +61,10 @@ yarn add -D nuxt-typed-router
47
61
  npm install -D nuxt-typed-router
48
62
  ```
49
63
 
50
- ### For Nuxt 2
64
+ ### Nuxt 2 legacy
51
65
 
52
- For Nuxt 2 usage, check out the docs at the [`nuxt2` branch](https://github.com/victorgarciaesgi/nuxt-typed-router/tree/nuxt2)
66
+ Nuxt 2 version is no longer maintained, but still available in [`nuxt2` branch](https://github.com/victorgarciaesgi/nuxt-typed-router/tree/nuxt2)
67
+ It only has route name autocomplete functionnality
53
68
 
54
69
  ```bash
55
70
  yarn add -D nuxt-typed-router@legacy
@@ -58,192 +73,22 @@ npm install -D nuxt-typed-router@legacy
58
73
  ```
59
74
 
60
75
  # Configuration
61
-
62
- First, register the module in the `nuxt.config.ts`
76
+ Register the module in the `nuxt.config.ts`, done!
63
77
 
64
78
  ```ts
65
79
  export default defineNuxtConfig({
66
80
  modules: ['nuxt-typed-router'],
67
- nuxtTypedRouter: {
68
- // options
69
- },
70
81
  });
71
82
  ```
72
83
 
73
- ## Options:
74
-
75
- ```ts
76
- interface ModuleOptions {
77
- /** Output directory where you cant the files to be saved
78
- * (ex: "./models")
79
- * @default "<srcDir>/generated"
80
- */
81
- outDir?: string;
82
- /** Name of the routesNames object (ex: "routesTree")
83
- * @default "routerPagesNames"
84
- * */
85
- routesObjectName?: string;
86
- /**
87
- * Set to false if you don't want a plugin generated
88
- * @default false
89
- */
90
- plugin?: boolean;
91
- }
92
- ```
93
-
94
- # Generated files
95
-
96
- The module will generate 4 files each time you modify the `pages` folder :
97
-
98
- - `~/<outDir>/__routes.ts` with the global object of the route names inside.
99
- - `~/<outDir>/__useTypedRouter.ts` Composable to simply access your typed routes
100
- - `~/<outDir>/typed-router.d.ts` containing the global typecript definitions and exports
101
- - `~/plugins/__typed_router.ts` Plugin that will inject `$typedRouter` and `$routesList` (`@nuxt/kit` has problems registering plugin templates so this is a workaround)
102
-
103
- # Usage in Vue/Nuxt
104
-
105
- <br/>
106
-
107
- ### **_Requirements_**
108
-
109
- You can specify the output dir of the generated files in your configuration. It defaults to `<srcDir>/generated`
110
-
111
- ```ts
112
- export default defineNuxtConfig({
113
- modules: ['nuxt-typed-router'],
114
- nuxtTypedRouter: {
115
- outDir: './generated',
116
- },
117
- });
118
- ```
119
-
120
- # How it works
121
-
122
- Given this structure
123
-
124
- โ”œโ”€โ”€ pages
125
- โ”œโ”€โ”€ index
126
- โ”œโ”€โ”€ content
127
- โ”œโ”€โ”€ [id].vue
128
- โ”œโ”€โ”€ content.vue
129
- โ”œโ”€โ”€ index.vue
130
- โ”œโ”€โ”€ communication.vue
131
- โ”œโ”€โ”€ statistics.vue
132
- โ”œโ”€โ”€ [user].vue
133
- โ”œโ”€โ”€ index.vue
134
- โ”œโ”€โ”€ forgotpassword.vue
135
- โ”œโ”€โ”€ reset-password.vue
136
- โ”‚ โ””โ”€โ”€ login.vue
137
- โ””โ”€โ”€ ...
138
-
139
- The generated route list will look like this
140
-
141
- ```ts
142
- export const routerPagesNames = {
143
- forgotpassword: 'forgotpassword' as const,
144
- login: 'login' as const,
145
- resetPassword: 'reset-password' as const,
146
- index: {
147
- index: 'index' as const,
148
- communication: 'index-communication' as const,
149
- content: {
150
- id: 'index-content-id' as const,
151
- },
152
- statistics: 'index-statistics' as const,
153
- user: 'index-user' as const,
154
- },
155
- };
156
- export type TypedRouteList =
157
- | 'forgotpassword'
158
- | 'login'
159
- | 'reset-password'
160
- | 'index'
161
- | 'index-communication'
162
- | 'index-content-id'
163
- | 'index-statistics'
164
- | 'index-user';
165
- ```
166
-
167
- > nuxt-typed-router will also create a plugin in your `<srcDir>/plugins` folder with the injected `$typedRouter` and `$routesList` helpers
168
-
169
- # Usage with `useTypedRouter` hook
170
-
171
- `useTypedRouter` is an exported composable from nuxt-typed-router. It contains a clone of `vue-router` but with strictly typed route names and params type-check
172
-
173
- ```vue
174
- <script lang="ts">
175
- // The path here is `~/generated` because I set `outDir: './generated'` in my module options
176
- import { useTypedRouter } from '~/generated';
177
-
178
- export default defineComponent({
179
- setup() {
180
- // Fully typed
181
- const { router, routes } = useTypedRouter();
182
-
183
- function navigate() {
184
- // Autocompletes the name and infer the params
185
- router.push({ name: routes.index.user, params: { user: 1 } }); // โœ… valid
186
- router.push({ name: routes.index.user, params: { foo: 1 } }); // โŒ invalid
187
- }
188
-
189
- return { navigate };
190
- },
191
- });
192
- </script>
193
- ```
194
-
195
- # Usage with `$typedRouter` and `$routesList` injected helpers
196
-
197
- `$typedRouter` is an injected clone of vue-router `$router`, but fully typed with all your routes.
198
- It's available anywhere you have access to Nuxt context
199
-
200
- ```vue
201
- <script lang="ts">
202
- import { defineComponent } from 'vue';
203
-
204
- export default defineComponent({
205
- name: 'Index',
206
- setup() {
207
- const { $typedRouter, $routesList } = useNuxtApp();
208
-
209
- function navigate() {
210
- $typedRouter.push({ name: $routesList.activate });
211
- }
212
-
213
- return {
214
- navigate,
215
- };
216
- },
217
- });
218
- </script>
219
- ```
220
-
221
- # Usage outside Vue component
222
-
223
- You can import the `useTypedRouter` composable from where it's generated.
224
- Exemple with `pinia` store here
225
-
226
- ```ts
227
- import pinia from 'pinia';
228
- import { useTypedRouter } from '~/generated';
229
-
230
- export const useFooStore = defineStore('foo', {
231
- actions: {
232
- bar() {
233
- const { router, routes } = useTypedRouter();
234
- router.push({ name: routes.index.user, params: { user: 2 } });
235
- },
236
- },
237
- });
238
- ```
239
84
 
240
85
  ## Development
241
86
 
242
87
  1. Clone this repository
243
- 2. Install dependencies using `yarn`
244
- 3. Build project for local tests `yarn build:local`
245
- 4. Start dev playground `yarn play`
246
- 5. Build project for deploy `yarn prepack`
88
+ 2. Install dependencies using `pnpm`
89
+ 3. Build project for local tests `pnpm build:local`
90
+ 4. Start dev playground `pnpm play`
91
+ 5. Build project for deploy `pnpm prepack`
247
92
 
248
93
  ## ๐Ÿ“‘ License
249
94
 
package/dist/module.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "nuxt": "^3.0.0-rc.1",
6
6
  "bridge": false
7
7
  },
8
- "version": "2.0.0-beta.0"
8
+ "version": "2.0.0"
9
9
  }
package/dist/module.mjs CHANGED
@@ -120,7 +120,7 @@ declare global {
120
120
  }
121
121
 
122
122
  type TypedNuxtLinkProps = Omit<NuxtLinkProps, 'to'> & {
123
- to: Omit<Exclude<RouteLocationRaw, string>, 'name'> & TypedRouteNamedMapper;
123
+ to: string | Omit<Exclude<RouteLocationRaw, string>, 'name'> & TypedRouteNamedMapper;
124
124
  };
125
125
 
126
126
  type _NuxtLink = DefineComponent<
@@ -171,7 +171,7 @@ function createDeclarationRoutesFile() {
171
171
  function createRuntimeIndexFile() {
172
172
  return `
173
173
  ${watermarkTemplate}
174
- export * from './__routes';
174
+ export {routesNames, TypedRouteList} from './__routes';
175
175
  export * from './__useTypedRouter';
176
176
  export * from './__useTypedRoute';
177
177
  `;
@@ -227,7 +227,9 @@ function createResolvedTypedRouteNamedMapperExport(routesParams) {
227
227
  } & (
228
228
  ${routesParams.map(
229
229
  ({ name, params }) => `{name: "${name}" ${params.length ? `, params: {
230
- ${params.map(({ key, type }) => `"${key}": ${type}`).join(",\n")}
230
+ ${params.map(
231
+ ({ key, notRequiredOnPage, type }) => `"${key}"${notRequiredOnPage ? "?" : ""}: ${type}`
232
+ ).join(",\n")}
231
233
  }` : ""}}`
232
234
  ).join("|\n")}
233
235
  )
@@ -307,7 +309,6 @@ function createRuntimeUseTypedRouterFile(routesDeclTemplate) {
307
309
  ${watermarkTemplate}
308
310
  import { useRouter } from '#app';
309
311
  import { TypedRouter } from './typed-router';
310
- import { RouteListDecl } from './__routes';
311
312
 
312
313
  /** Returns instances of $typedRouter and $routesList fully typed to use in your components or your Vuex/Pinia store
313
314
  *
@@ -357,11 +358,12 @@ dirname(fileURLToPath(import.meta.url));
357
358
  async function processPathAndWriteFile({
358
359
  content,
359
360
  fileName,
360
- srcDir
361
+ srcDir,
362
+ outDir
361
363
  }) {
362
364
  try {
363
- const outDir = `.nuxt/typed-router`;
364
- const processedOutDir = resolve(srcDir, outDir);
365
+ const finalOutDir = outDir ?? `.nuxt/typed-router`;
366
+ const processedOutDir = resolve(srcDir, finalOutDir);
365
367
  const outputFile = resolve(process.cwd(), `${processedOutDir}/${fileName}`);
366
368
  const formatedContent = await formatOutputWithPrettier(content);
367
369
  if (fs.existsSync(outputFile)) {
@@ -403,6 +405,7 @@ function handlePluginFileSave({
403
405
  });
404
406
  }
405
407
 
408
+ let previousGeneratedRoutes = "";
406
409
  async function saveGeneratedFiles({
407
410
  srcDir,
408
411
  outputData: { routesDeclTemplate, routesList, routesObjectTemplate, routesParams }
@@ -437,22 +440,35 @@ async function saveGeneratedFiles({
437
440
  await Promise.all(
438
441
  filesMap.map(({ content, fileName }) => processPathAndWriteFile({ srcDir, content, fileName }))
439
442
  );
440
- console.log(logSymbols.success, `[typed-router] Routes definitions generated`);
443
+ if (previousGeneratedRoutes !== routesList.join(",")) {
444
+ previousGeneratedRoutes = routesList.join(",");
445
+ console.log(logSymbols.success, `[typed-router] Routes definitions generated`);
446
+ }
441
447
  }
442
448
 
443
449
  function isItemLast(array, index) {
444
450
  return array ? index === array.length - 1 : false;
445
451
  }
446
452
 
447
- const routeParamExtractRegxp = /:(\w+)/;
453
+ const routeParamExtractRegxp = /(:(\w+)(\?)?)+/g;
448
454
  function extractRouteParamsFromPath(path, isIndexFileForRouting, previousParams) {
449
- const params = path.match(routeParamExtractRegxp) ?? [];
450
- params?.shift();
455
+ let params = [];
456
+ let matches;
457
+ do {
458
+ matches = routeParamExtractRegxp.exec(path);
459
+ if (matches) {
460
+ const [_, mtch, key, optional] = matches;
461
+ if (mtch) {
462
+ params.push({ name: key, optional: !!optional });
463
+ }
464
+ }
465
+ } while (matches);
451
466
  let allMergedParams = params.map(
452
- (m) => ({
453
- key: m,
467
+ ({ name, optional }) => ({
468
+ key: name,
454
469
  type: "string | number",
455
- required: true
470
+ required: !optional,
471
+ notRequiredOnPage: optional
456
472
  })
457
473
  );
458
474
  if (previousParams?.length) {
@@ -633,6 +649,7 @@ const module = defineNuxtModule({
633
649
  )
634
650
  };
635
651
  nuxt.hook("pages:extend", () => createTypedRouter({ srcDir, nuxt, plugin }));
652
+ createTypedRouter({ srcDir, nuxt, plugin });
636
653
  }
637
654
  });
638
655
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-typed-router",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.0.0",
4
4
  "description": "Provide autocompletion for pages route names generated by Nuxt router",
5
5
  "type": "module",
6
6
  "main": "./dist/module.cjs",
@@ -24,7 +24,7 @@
24
24
  "test": "pnpm run dev:prepare && pnpm run build:test && vitest run",
25
25
  "test:watch": "pnpm run build:test && vitest",
26
26
  "docs:dev": "cd docs && pnpm run dev",
27
- "docs:buid": "(cd docs && nuxi build)"
27
+ "docs:build": "npm run dev:prepare && cd docs && nuxi generate"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"
@@ -39,6 +39,7 @@
39
39
  "nuxt 3",
40
40
  "nuxt 3 router"
41
41
  ],
42
+ "homepage": "https://nuxt-typed-router.vercel.app/",
42
43
  "repository": {
43
44
  "type": "git",
44
45
  "url": "git+https://victorgarciaesgi@github.com/victorgarciaesgi/nuxt-typed-router.git"