next-vite-router 1.0.8 → 1.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/README.md +279 -287
- package/dist/cli.cjs +11 -3
- package/dist/index.js +110 -104
- package/dist/index.plugin--ecyiAem.js +132 -0
- package/dist/plugin.js +4 -57
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,287 +1,279 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
npm
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
##
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Pass a custom `pagesDir` if your project uses `src/pages` or another convention.
|
|
284
|
-
|
|
285
|
-
## License
|
|
286
|
-
|
|
287
|
-
MIT
|
|
1
|
+
# Next Vite Router
|
|
2
|
+
|
|
3
|
+
File-based routing for React + Vite.
|
|
4
|
+
|
|
5
|
+
Define routes with folders and let the package generate the
|
|
6
|
+
[React Router](https://reactrouter.com/) `RouteObject[]`. It does not replace
|
|
7
|
+
React Router, navigation, history, loaders, or application state.
|
|
8
|
+
|
|
9
|
+
## Start here
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm create vite@latest my-app -- --template react-ts
|
|
13
|
+
cd my-app
|
|
14
|
+
npm install next-vite-router react-router-dom
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Add the plugin to `vite.config.ts`:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { defineConfig } from "vite";
|
|
21
|
+
import react from "@vitejs/plugin-react";
|
|
22
|
+
import { nextViteRouter } from "next-vite-router/plugin";
|
|
23
|
+
|
|
24
|
+
export default defineConfig({
|
|
25
|
+
plugins: [react(), nextViteRouter()],
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Create your route tree:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
src/app/
|
|
33
|
+
├── page.tsx
|
|
34
|
+
├── about/
|
|
35
|
+
│ └── page.tsx
|
|
36
|
+
└── users/
|
|
37
|
+
├── page.tsx
|
|
38
|
+
└── [id]/
|
|
39
|
+
└── page.tsx
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This generates:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
/
|
|
46
|
+
/about
|
|
47
|
+
/users
|
|
48
|
+
/users/:id
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use the generated router inside a normal React Router provider:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// src/App.tsx
|
|
55
|
+
import { Router } from "virtual:next-vite-router";
|
|
56
|
+
|
|
57
|
+
export default function App() {
|
|
58
|
+
return <Router />;
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// src/main.tsx
|
|
64
|
+
import { StrictMode } from "react";
|
|
65
|
+
import { createRoot } from "react-dom/client";
|
|
66
|
+
import { BrowserRouter } from "react-router-dom";
|
|
67
|
+
import App from "./App";
|
|
68
|
+
|
|
69
|
+
createRoot(document.getElementById("root")!).render(
|
|
70
|
+
<StrictMode>
|
|
71
|
+
<BrowserRouter>
|
|
72
|
+
<App />
|
|
73
|
+
</BrowserRouter>
|
|
74
|
+
</StrictMode>,
|
|
75
|
+
);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
That is the complete setup. Run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run dev
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Use React Router directly
|
|
85
|
+
|
|
86
|
+
`Router` is only a convenience wrapper around
|
|
87
|
+
`useRoutes(generateRoutes())`. You can keep using React Router APIs:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import {
|
|
91
|
+
Link,
|
|
92
|
+
generateRoutes,
|
|
93
|
+
useRoutes,
|
|
94
|
+
} from "virtual:next-vite-router";
|
|
95
|
+
import { useNavigate, useParams } from "react-router-dom";
|
|
96
|
+
|
|
97
|
+
const routes = generateRoutes();
|
|
98
|
+
|
|
99
|
+
export default function App() {
|
|
100
|
+
const element = useRoutes(routes);
|
|
101
|
+
return (
|
|
102
|
+
<>
|
|
103
|
+
<Link to="/users">Users</Link>
|
|
104
|
+
{element}
|
|
105
|
+
</>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Use `useNavigate`, `useParams`, loaders, actions, and other React Router APIs
|
|
111
|
+
from `react-router-dom`. The package intentionally does not create competing
|
|
112
|
+
navigation or data-loading APIs.
|
|
113
|
+
|
|
114
|
+
## File conventions
|
|
115
|
+
|
|
116
|
+
| File or folder | Result |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `page.tsx` | Route page |
|
|
119
|
+
| `layout.tsx` | Nested layout rendered with React Router's `Outlet` |
|
|
120
|
+
| `not-found.tsx` | `*` route for that subtree |
|
|
121
|
+
| `[id]` | Dynamic segment `:id` |
|
|
122
|
+
| `[...slug]` | Catch-all `*` segment |
|
|
123
|
+
| `[[...slug]]` | Optional catch-all `*` segment |
|
|
124
|
+
| `(admin)` | Route group omitted from the URL |
|
|
125
|
+
|
|
126
|
+
Layouts are the recommended way to add route-scoped navigation, providers, or
|
|
127
|
+
authorization:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
src/app/
|
|
131
|
+
├── layout.tsx
|
|
132
|
+
└── dashboard/
|
|
133
|
+
├── layout.tsx
|
|
134
|
+
└── page.tsx
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { Outlet } from "react-router-dom";
|
|
139
|
+
|
|
140
|
+
export default function DashboardLayout() {
|
|
141
|
+
return (
|
|
142
|
+
<AuthGuard>
|
|
143
|
+
<Outlet />
|
|
144
|
+
</AuthGuard>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Type-safe paths
|
|
150
|
+
|
|
151
|
+
The Vite plugin generates `virtual-next-vite-router.d.ts` automatically:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import type {
|
|
155
|
+
RouteParams,
|
|
156
|
+
RoutePath,
|
|
157
|
+
} from "virtual:next-vite-router";
|
|
158
|
+
|
|
159
|
+
const path = "/users/:id" satisfies RoutePath;
|
|
160
|
+
const params: RouteParams<typeof path> = { id: "123" };
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Invalid paths and missing dynamic parameters are reported by TypeScript.
|
|
164
|
+
No manual declaration for the virtual module is required.
|
|
165
|
+
|
|
166
|
+
## Lazy loading
|
|
167
|
+
|
|
168
|
+
Pages and `not-found.tsx` files use Vite's non-eager `import.meta.glob`, so
|
|
169
|
+
Vite can emit separate chunks for pages. Layouts are eager because they wrap
|
|
170
|
+
the generated route tree. Lazy loading is enabled by default:
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { configureRouter } from "next-vite-router";
|
|
174
|
+
|
|
175
|
+
configureRouter({
|
|
176
|
+
loadingFallback: () => <div>Loading route...</div>,
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`enableLazyLoading: false` is available for compatibility and testing. It does
|
|
181
|
+
not turn Vite's asynchronous imports into synchronous imports.
|
|
182
|
+
|
|
183
|
+
## Optional middleware
|
|
184
|
+
|
|
185
|
+
Prefer layouts for route-scoped behavior. `registerMiddleware` is an optional
|
|
186
|
+
escape hatch for cross-cutting wrappers:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { registerMiddleware } from "next-vite-router";
|
|
190
|
+
|
|
191
|
+
registerMiddleware(/^\/dashboard/, (element) => (
|
|
192
|
+
<AuthGuard>{element}</AuthGuard>
|
|
193
|
+
));
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Middleware wraps generated route elements; it does not create routes or
|
|
197
|
+
replace React Router.
|
|
198
|
+
|
|
199
|
+
## CLI
|
|
200
|
+
|
|
201
|
+
For an interactive setup:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npx create-next-vite-router init
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The CLI creates a Vite React project, installs the package and
|
|
208
|
+
`react-router-dom@^7.0.0`, configures the plugin, and adds an example route
|
|
209
|
+
tree.
|
|
210
|
+
|
|
211
|
+
## Compatibility
|
|
212
|
+
|
|
213
|
+
| Package | Supported versions |
|
|
214
|
+
| --- | --- |
|
|
215
|
+
| React / React DOM | `>=18 <20` |
|
|
216
|
+
| React Router DOM | `>=6 <8` |
|
|
217
|
+
| Vite | `>=5 <9` |
|
|
218
|
+
|
|
219
|
+
The compatibility workflow tests React 18 and 19 with React Router 6 and 7
|
|
220
|
+
across Vite 5, 6, 7, and 8.
|
|
221
|
+
|
|
222
|
+
## Advanced API
|
|
223
|
+
|
|
224
|
+
For custom glob layouts, the package exports:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import {
|
|
228
|
+
buildRouteTree,
|
|
229
|
+
createRouteGenerator,
|
|
230
|
+
treeToRoutes,
|
|
231
|
+
} from "next-vite-router";
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The normal pipeline is:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
filesystem -> buildRouteTree() -> treeToRoutes() -> RouteObject[] -> React Router
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Examples
|
|
241
|
+
|
|
242
|
+
The repository includes a runnable showcase in [`examples/`](./examples/):
|
|
243
|
+
|
|
244
|
+
- `basic`
|
|
245
|
+
- `layouts`
|
|
246
|
+
- `dynamic-routes`
|
|
247
|
+
- `middleware`
|
|
248
|
+
- `typed-routes`
|
|
249
|
+
|
|
250
|
+
Run it from the examples directory:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
cd examples
|
|
254
|
+
pnpm install
|
|
255
|
+
pnpm dev
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The app opens a small navigation menu so each example can be tested
|
|
259
|
+
immediately. The `predev` script builds the local package automatically.
|
|
260
|
+
|
|
261
|
+
## Development
|
|
262
|
+
|
|
263
|
+
From this repository:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
pnpm install
|
|
267
|
+
pnpm run build
|
|
268
|
+
pnpm run test:generator
|
|
269
|
+
pnpm run test:routes
|
|
270
|
+
pnpm run test:compatibility
|
|
271
|
+
pnpm exec tsc -p test/fixture/tsconfig.json --noEmit
|
|
272
|
+
pnpm run test:local
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The route matrix lives in [`test/routes.test.js`](./test/routes.test.js).
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|
package/dist/cli.cjs
CHANGED
|
@@ -200,10 +200,18 @@ function createVirtualDTS(projectDir, useSrc) {
|
|
|
200
200
|
}
|
|
201
201
|
const filePath = path.join(targetDir, "virtual-next-vite-router.d.ts");
|
|
202
202
|
const content = `declare module 'virtual:next-vite-router' {
|
|
203
|
+
import type { ReactElement } from 'react';
|
|
203
204
|
import type { RouteObject } from 'react-router-dom';
|
|
204
205
|
|
|
206
|
+
export type RoutePath = string;
|
|
207
|
+
export type RoutePathParams = Record<string, Record<string, string>>;
|
|
208
|
+
export type RouteParams<P extends RoutePath> = P extends keyof RoutePathParams
|
|
209
|
+
? RoutePathParams[P]
|
|
210
|
+
: never;
|
|
211
|
+
|
|
205
212
|
export const generateRoutes: () => RouteObject[];
|
|
206
|
-
export
|
|
213
|
+
export function Router(): ReactElement | null;
|
|
214
|
+
export { Link, useRoutes } from 'react-router-dom';
|
|
207
215
|
}
|
|
208
216
|
`;
|
|
209
217
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
@@ -518,7 +526,7 @@ const init = new commander.Command().name("init").description(
|
|
|
518
526
|
);
|
|
519
527
|
const addDepsCmd = pm.addDepsCommand([
|
|
520
528
|
"next-vite-router",
|
|
521
|
-
"react-router-dom
|
|
529
|
+
"react-router-dom@^7.0.0"
|
|
522
530
|
]);
|
|
523
531
|
child_process.execSync(addDepsCmd, { stdio: "inherit", cwd: projectName });
|
|
524
532
|
logger.info("Configuring next-vite-router in the project...");
|
|
@@ -531,7 +539,7 @@ const init = new commander.Command().name("init").description(
|
|
|
531
539
|
Project "${projectName}" is ready!
|
|
532
540
|
`);
|
|
533
541
|
});
|
|
534
|
-
const version = "1.0
|
|
542
|
+
const version = "1.1.0";
|
|
535
543
|
const pkg = {
|
|
536
544
|
version
|
|
537
545
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,86 +1,84 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function k(e) {
|
|
11
|
-
m = { ...m, ...e };
|
|
1
|
+
import { e as C, n as f } from "./index.plugin--ecyiAem.js";
|
|
2
|
+
import { a as G } from "./index.plugin--ecyiAem.js";
|
|
3
|
+
import i, { Suspense as y, lazy as F } from "react";
|
|
4
|
+
const L = {
|
|
5
|
+
enableLazyLoading: !0
|
|
6
|
+
};
|
|
7
|
+
let d = { ...L };
|
|
8
|
+
function x(e) {
|
|
9
|
+
d = { ...d, ...e };
|
|
12
10
|
}
|
|
13
|
-
function
|
|
14
|
-
return
|
|
11
|
+
function $() {
|
|
12
|
+
return d;
|
|
15
13
|
}
|
|
16
|
-
let
|
|
17
|
-
function
|
|
18
|
-
|
|
14
|
+
let E = [];
|
|
15
|
+
function T(e, r) {
|
|
16
|
+
E.push({ pattern: e, middleware: r });
|
|
19
17
|
}
|
|
20
|
-
function
|
|
21
|
-
|
|
18
|
+
function j() {
|
|
19
|
+
E = [];
|
|
22
20
|
}
|
|
23
|
-
function
|
|
24
|
-
let
|
|
25
|
-
for (const
|
|
26
|
-
|
|
27
|
-
return
|
|
21
|
+
function z(e, r) {
|
|
22
|
+
let n = r;
|
|
23
|
+
for (const t of E)
|
|
24
|
+
t.pattern.test(e) && (n = t.middleware(n));
|
|
25
|
+
return n;
|
|
28
26
|
}
|
|
29
|
-
const
|
|
30
|
-
function R(e,
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
-
const
|
|
34
|
-
|
|
27
|
+
const M = () => i.createElement("div"), b = /* @__PURE__ */ new Map();
|
|
28
|
+
function R(e, r, n) {
|
|
29
|
+
const t = $();
|
|
30
|
+
if (t.enableLazyLoading === !1) {
|
|
31
|
+
const c = i.lazy(e), u = i.createElement(
|
|
32
|
+
y,
|
|
35
33
|
{ fallback: null },
|
|
36
34
|
// No loading fallback when disabled
|
|
37
|
-
i.createElement(
|
|
35
|
+
i.createElement(c)
|
|
38
36
|
);
|
|
39
|
-
return
|
|
37
|
+
return n ? z(n, u) : u;
|
|
40
38
|
}
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
{ fallback: i.createElement(
|
|
46
|
-
i.createElement(
|
|
39
|
+
let l = b.get(r);
|
|
40
|
+
l || (l = F(e), b.set(r, l));
|
|
41
|
+
const o = t.loadingFallback || M, a = i.createElement(
|
|
42
|
+
y,
|
|
43
|
+
{ fallback: i.createElement(o) },
|
|
44
|
+
i.createElement(l)
|
|
47
45
|
);
|
|
48
|
-
return
|
|
46
|
+
return n ? z(n, a) : a;
|
|
49
47
|
}
|
|
50
|
-
function p(e,
|
|
51
|
-
const
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
segment:
|
|
56
|
-
fullPath:
|
|
48
|
+
function p(e, r, n, t) {
|
|
49
|
+
const l = C(r), o = l ? l.split("/") : [];
|
|
50
|
+
let a = e, c = "";
|
|
51
|
+
o.forEach((u, h) => {
|
|
52
|
+
c = c ? `${c}/${u}` : u, a.children.has(u) || a.children.set(u, {
|
|
53
|
+
segment: u,
|
|
54
|
+
fullPath: c,
|
|
57
55
|
children: /* @__PURE__ */ new Map()
|
|
58
|
-
}),
|
|
59
|
-
}),
|
|
56
|
+
}), a = a.children.get(u), h === o.length - 1 && (a[t] = n);
|
|
57
|
+
}), o.length === 0 && (e[t] = n);
|
|
60
58
|
}
|
|
61
|
-
function
|
|
62
|
-
const
|
|
59
|
+
function S(e, r, n) {
|
|
60
|
+
const t = {
|
|
63
61
|
segment: "",
|
|
64
62
|
fullPath: "",
|
|
65
63
|
children: /* @__PURE__ */ new Map()
|
|
66
64
|
};
|
|
67
|
-
return Object.entries(
|
|
68
|
-
p(
|
|
69
|
-
}), Object.entries(e).forEach(([
|
|
70
|
-
p(
|
|
71
|
-
}), Object.entries(
|
|
72
|
-
p(
|
|
73
|
-
}),
|
|
65
|
+
return Object.entries(r).forEach(([l, o]) => {
|
|
66
|
+
p(t, l, o.default, "layout");
|
|
67
|
+
}), Object.entries(e).forEach(([l, o]) => {
|
|
68
|
+
p(t, l, o, "page");
|
|
69
|
+
}), Object.entries(n).forEach(([l, o]) => {
|
|
70
|
+
p(t, l, o, "notFound");
|
|
71
|
+
}), t;
|
|
74
72
|
}
|
|
75
|
-
function
|
|
76
|
-
const
|
|
73
|
+
function m(e, r, n = !1) {
|
|
74
|
+
const t = R(
|
|
77
75
|
async () => ({ default: (await e.page()).default }),
|
|
78
|
-
`page:${e.fullPath ||
|
|
79
|
-
|
|
76
|
+
`page:${e.fullPath || r}`,
|
|
77
|
+
r
|
|
80
78
|
);
|
|
81
|
-
return
|
|
79
|
+
return n ? { index: !0, element: t } : { path: f(e.segment), element: t };
|
|
82
80
|
}
|
|
83
|
-
function
|
|
81
|
+
function g(e) {
|
|
84
82
|
return {
|
|
85
83
|
path: "*",
|
|
86
84
|
element: R(
|
|
@@ -89,62 +87,70 @@ function $(e) {
|
|
|
89
87
|
)
|
|
90
88
|
};
|
|
91
89
|
}
|
|
92
|
-
function s(e,
|
|
93
|
-
const r = [];
|
|
94
|
-
return e.children.forEach((n, t) => {
|
|
95
|
-
const l = f(t), o = l ? a ? `${a}/${l}` : `/${l}` : a;
|
|
96
|
-
r.push(...b(n, !1, o));
|
|
97
|
-
}), r;
|
|
98
|
-
}
|
|
99
|
-
function b(e, a = !1, r = "") {
|
|
90
|
+
function s(e, r) {
|
|
100
91
|
const n = [];
|
|
101
|
-
|
|
102
|
-
const t =
|
|
103
|
-
|
|
92
|
+
return [...e.children.entries()].sort(([t], [l]) => t.localeCompare(l)).forEach(([t, l]) => {
|
|
93
|
+
const o = f(t), a = o ? r ? `${r}/${o}` : `/${o}` : r;
|
|
94
|
+
n.push(...w(l, !1, a));
|
|
95
|
+
}), n;
|
|
96
|
+
}
|
|
97
|
+
function w(e, r = !1, n = "") {
|
|
98
|
+
const t = [];
|
|
99
|
+
if (r && e.layout) {
|
|
100
|
+
const a = [];
|
|
101
|
+
return e.page && a.push(m(e, "/", !0)), a.push(...s(e, "")), e.notFound && a.push(g(e)), t.push({
|
|
104
102
|
path: "/",
|
|
105
103
|
element: i.createElement(e.layout),
|
|
106
|
-
children:
|
|
107
|
-
}),
|
|
104
|
+
children: a
|
|
105
|
+
}), t;
|
|
108
106
|
}
|
|
109
107
|
if (e.layout) {
|
|
110
|
-
const
|
|
111
|
-
if (!
|
|
112
|
-
return s(e,
|
|
113
|
-
const
|
|
114
|
-
return e.page &&
|
|
115
|
-
path:
|
|
108
|
+
const a = f(e.segment);
|
|
109
|
+
if (!a)
|
|
110
|
+
return s(e, n);
|
|
111
|
+
const c = n + "/" + a, u = [];
|
|
112
|
+
return e.page && u.push(m(e, c, !0)), u.push(...s(e, c)), e.notFound && u.push(g(e)), t.push({
|
|
113
|
+
path: a,
|
|
116
114
|
element: i.createElement(e.layout),
|
|
117
|
-
children:
|
|
118
|
-
}),
|
|
115
|
+
children: u.length > 0 ? u : void 0
|
|
116
|
+
}), t;
|
|
119
117
|
}
|
|
120
118
|
if (e.page) {
|
|
121
|
-
const
|
|
122
|
-
if (!
|
|
123
|
-
return
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
119
|
+
const a = f(e.segment);
|
|
120
|
+
if (!a)
|
|
121
|
+
return r ? (t.push({
|
|
122
|
+
path: "/",
|
|
123
|
+
element: m(e, "/", !0).element,
|
|
124
|
+
children: s(e, n).length > 0 ? s(e, n) : void 0
|
|
125
|
+
}), e.notFound && t.push(g(e)), t) : s(e, n);
|
|
126
|
+
const c = n + "/" + a, u = s(e, c), h = m(e, c, !1);
|
|
127
|
+
return t.push({
|
|
128
|
+
path: h.path,
|
|
129
|
+
element: h.element,
|
|
130
|
+
children: u.length > 0 ? u : void 0
|
|
131
|
+
}), t;
|
|
130
132
|
}
|
|
131
|
-
|
|
133
|
+
const l = f(e.segment), o = s(e, n);
|
|
134
|
+
return l ? [{
|
|
135
|
+
path: l,
|
|
136
|
+
children: o.length > 0 ? o : void 0
|
|
137
|
+
}] : o;
|
|
132
138
|
}
|
|
133
|
-
function
|
|
139
|
+
function O(e, r, n) {
|
|
134
140
|
return () => {
|
|
135
|
-
const
|
|
136
|
-
return
|
|
141
|
+
const t = S(e, r, n);
|
|
142
|
+
return w(t, !0);
|
|
137
143
|
};
|
|
138
144
|
}
|
|
139
145
|
export {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
S as buildRouteTree,
|
|
147
|
+
j as clearMiddlewares,
|
|
148
|
+
x as configureRouter,
|
|
143
149
|
R as createLazyElement,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
O as createRouteGenerator,
|
|
151
|
+
C as extractRoutePath,
|
|
152
|
+
G as nextViteRouter,
|
|
147
153
|
f as normalizeSegment,
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
T as registerMiddleware,
|
|
155
|
+
w as treeToRoutes
|
|
150
156
|
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import p from "node:fs";
|
|
2
|
+
import i from "node:path";
|
|
3
|
+
function y(e) {
|
|
4
|
+
return /^\([^)]+\)$/.test(e) ? "" : /^\[\[\.\.\.(.+)\]\]$/.test(e) || /^\[\.\.\.(.+)\]$/.test(e) ? "*" : /^\[([^\]]+)\]$/.test(e) ? e.replace(/^\[([^\]]+)\]$/, ":$1") : e;
|
|
5
|
+
}
|
|
6
|
+
function b(e) {
|
|
7
|
+
return e.replace(/\\/g, "/").replace("../../app/", "").replace(/\/?(page|layout|not-found)\.tsx$/, "");
|
|
8
|
+
}
|
|
9
|
+
function R(e) {
|
|
10
|
+
const t = e.split(i.sep).filter(Boolean).map(y).filter(Boolean);
|
|
11
|
+
return t.length > 0 ? `/${t.join("/")}` : "/";
|
|
12
|
+
}
|
|
13
|
+
function h(e, t) {
|
|
14
|
+
const s = /* @__PURE__ */ new Set();
|
|
15
|
+
function n(a) {
|
|
16
|
+
for (const u of p.readdirSync(a, { withFileTypes: !0 })) {
|
|
17
|
+
const r = i.join(a, u.name);
|
|
18
|
+
u.isDirectory() ? n(r) : u.isFile() && u.name === t && s.add(R(i.relative(e, a)));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return p.existsSync(e) && n(e), [...s].sort();
|
|
22
|
+
}
|
|
23
|
+
function x(e) {
|
|
24
|
+
const t = e.match(/^\/(?:\[\.\.\.|:)([^/\]]+)\]?$/);
|
|
25
|
+
return t == null ? void 0 : t[1];
|
|
26
|
+
}
|
|
27
|
+
function $(e) {
|
|
28
|
+
const t = e.map((n) => ` | "${n}"`).join(`
|
|
29
|
+
`), s = e.map((n) => {
|
|
30
|
+
const a = n.split("/").map((r) => x(`/${r}`)).filter((r) => !!r), u = a.length > 0 ? a.map((r) => ` ${r}: string;`).join(`
|
|
31
|
+
`) : " [key: string]: never;";
|
|
32
|
+
return ` "${n}": {
|
|
33
|
+
${u}
|
|
34
|
+
};`;
|
|
35
|
+
}).join(`
|
|
36
|
+
`);
|
|
37
|
+
return `declare module 'virtual:next-vite-router' {
|
|
38
|
+
import type { ReactElement } from 'react';
|
|
39
|
+
import type { RouteObject } from 'react-router-dom';
|
|
40
|
+
|
|
41
|
+
export type RoutePath =
|
|
42
|
+
${t || ' | "/"'};
|
|
43
|
+
|
|
44
|
+
export type RouteParams<P extends RoutePath> = P extends keyof RoutePathParams
|
|
45
|
+
? RoutePathParams[P]
|
|
46
|
+
: never;
|
|
47
|
+
|
|
48
|
+
export interface RoutePathParams {
|
|
49
|
+
${s}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const generateRoutes: () => RouteObject[];
|
|
53
|
+
export function Router(): ReactElement | null;
|
|
54
|
+
export { Link, useRoutes } from 'react-router-dom';
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
function g(e = {}) {
|
|
59
|
+
const {
|
|
60
|
+
pagesDir: t = "src/app",
|
|
61
|
+
pageFile: s = "page.tsx",
|
|
62
|
+
layoutFile: n = "layout.tsx",
|
|
63
|
+
notFoundFile: a = "not-found.tsx"
|
|
64
|
+
} = e, u = "virtual:next-vite-router", r = "\0" + u;
|
|
65
|
+
let l = "", m = "";
|
|
66
|
+
function f() {
|
|
67
|
+
const o = $(h(l, s));
|
|
68
|
+
p.writeFileSync(m, o, "utf-8");
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
name: "next-vite-router",
|
|
72
|
+
configResolved(o) {
|
|
73
|
+
l = i.resolve(o.root, t), m = i.join(i.dirname(l), "virtual-next-vite-router.d.ts"), f();
|
|
74
|
+
},
|
|
75
|
+
resolveId(o) {
|
|
76
|
+
if (o === u)
|
|
77
|
+
return r;
|
|
78
|
+
},
|
|
79
|
+
load(o) {
|
|
80
|
+
if (o === r)
|
|
81
|
+
return `
|
|
82
|
+
import { buildRouteTree, treeToRoutes, normalizeSegment, createLazyElement } from 'next-vite-router';
|
|
83
|
+
import { Link, useRoutes } from 'react-router-dom';
|
|
84
|
+
|
|
85
|
+
const pagesGlob = import.meta.glob('/${t}/**/${s}');
|
|
86
|
+
const layoutsGlob = import.meta.glob('/${t}/**/${n}', { eager: true });
|
|
87
|
+
const notFoundsGlob = import.meta.glob('/${t}/**/${a}');
|
|
88
|
+
|
|
89
|
+
const transformPath = (path) => path.replace('/${t}/', '');
|
|
90
|
+
|
|
91
|
+
const pages = Object.fromEntries(
|
|
92
|
+
Object.entries(pagesGlob).map(([key, value]) => [transformPath(key), value])
|
|
93
|
+
);
|
|
94
|
+
const layouts = Object.fromEntries(
|
|
95
|
+
Object.entries(layoutsGlob).map(([key, value]) => [transformPath(key), value])
|
|
96
|
+
);
|
|
97
|
+
const notFounds = Object.fromEntries(
|
|
98
|
+
Object.entries(notFoundsGlob).map(([key, value]) => [transformPath(key), value])
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
export function generateRoutes() {
|
|
102
|
+
const tree = buildRouteTree(pages, layouts, notFounds);
|
|
103
|
+
const routes = treeToRoutes(tree, true);
|
|
104
|
+
return routes;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function Router() {
|
|
108
|
+
return useRoutes(generateRoutes());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { Link, useRoutes };
|
|
112
|
+
`;
|
|
113
|
+
},
|
|
114
|
+
handleHotUpdate({ file: o, server: c }) {
|
|
115
|
+
if (o.includes(t) && (o.endsWith(s) || o.endsWith(n) || o.endsWith(a))) {
|
|
116
|
+
f();
|
|
117
|
+
const d = c.moduleGraph.getModuleById(
|
|
118
|
+
r
|
|
119
|
+
);
|
|
120
|
+
d && (c.moduleGraph.invalidateModule(d), c.ws.send({
|
|
121
|
+
type: "full-reload",
|
|
122
|
+
path: "*"
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
g as a,
|
|
130
|
+
b as e,
|
|
131
|
+
y as n
|
|
132
|
+
};
|
package/dist/plugin.js
CHANGED
|
@@ -1,59 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
pageFile: n = "page.tsx",
|
|
5
|
-
layoutFile: a = "layout.tsx",
|
|
6
|
-
notFoundFile: u = "not-found.tsx"
|
|
7
|
-
} = i, s = "virtual:next-vite-router", o = "\0" + s;
|
|
8
|
-
return {
|
|
9
|
-
name: "next-vite-router",
|
|
10
|
-
resolveId(e) {
|
|
11
|
-
if (e === s)
|
|
12
|
-
return o;
|
|
13
|
-
},
|
|
14
|
-
load(e) {
|
|
15
|
-
if (e === o)
|
|
16
|
-
return `
|
|
17
|
-
import { buildRouteTree, treeToRoutes, normalizeSegment, createLazyElement } from 'next-vite-router';
|
|
18
|
-
|
|
19
|
-
const pagesGlob = import.meta.glob('/${t}/**/${n}');
|
|
20
|
-
const layoutsGlob = import.meta.glob('/${t}/**/${a}', { eager: true });
|
|
21
|
-
const notFoundsGlob = import.meta.glob('/${t}/**/${u}');
|
|
22
|
-
|
|
23
|
-
const transformPath = (path) => path.replace('/${t}/', '');
|
|
24
|
-
|
|
25
|
-
const pages = Object.fromEntries(
|
|
26
|
-
Object.entries(pagesGlob).map(([key, value]) => [transformPath(key), value])
|
|
27
|
-
);
|
|
28
|
-
const layouts = Object.fromEntries(
|
|
29
|
-
Object.entries(layoutsGlob).map(([key, value]) => [transformPath(key), value])
|
|
30
|
-
);
|
|
31
|
-
const notFounds = Object.fromEntries(
|
|
32
|
-
Object.entries(notFoundsGlob).map(([key, value]) => [transformPath(key), value])
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export function generateRoutes() {
|
|
36
|
-
const tree = buildRouteTree(pages, layouts, notFounds);
|
|
37
|
-
const routes = treeToRoutes(tree, true);
|
|
38
|
-
return routes;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export { useRoutes } from 'react-router-dom';
|
|
42
|
-
`;
|
|
43
|
-
},
|
|
44
|
-
handleHotUpdate({ file: e, server: r }) {
|
|
45
|
-
if (e.includes(t) && (e.endsWith(n) || e.endsWith(a) || e.endsWith(u))) {
|
|
46
|
-
const l = r.moduleGraph.getModuleById(
|
|
47
|
-
o
|
|
48
|
-
);
|
|
49
|
-
l && (r.moduleGraph.invalidateModule(l), r.ws.send({
|
|
50
|
-
type: "full-reload",
|
|
51
|
-
path: "*"
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
1
|
+
import "node:fs";
|
|
2
|
+
import "node:path";
|
|
3
|
+
import { a as i } from "./index.plugin--ecyiAem.js";
|
|
57
4
|
export {
|
|
58
|
-
|
|
5
|
+
i as nextViteRouter
|
|
59
6
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-vite-router",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Next.js-like file-based routing for React + Vite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"homepage": "https://github.com/Rlzee/next-vite-router#readme",
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"react": "
|
|
44
|
-
"react-dom": "
|
|
45
|
-
"react-router-dom": "
|
|
46
|
-
"vite": "
|
|
43
|
+
"react": ">=18 <20",
|
|
44
|
+
"react-dom": ">=18 <20",
|
|
45
|
+
"react-router-dom": ">=6 <8",
|
|
46
|
+
"vite": ">=5 <9"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/node": "^24.10.1",
|
|
@@ -63,6 +63,9 @@
|
|
|
63
63
|
"dev": "vite build --watch",
|
|
64
64
|
"build": "vite build && vite build -c vite.config.cli.ts",
|
|
65
65
|
"test:local": "node test/test-package.js",
|
|
66
|
+
"test:routes": "node test/test-route-types.js",
|
|
67
|
+
"test:generator": "node --test test/routes.test.js",
|
|
68
|
+
"test:compatibility": "node test/test-compatibility.js",
|
|
66
69
|
"preview": "pnpm pack --dry-run",
|
|
67
70
|
"cli": "cd test && node ../dist/cli.cjs"
|
|
68
71
|
}
|