next-vite-router 1.1.1 → 1.2.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 +46 -9
- package/dist/cli.cjs +10 -1
- package/dist/index.d.ts +25 -16
- package/dist/index.js +155 -115
- package/dist/path-BodMUTCM.js +14 -0
- package/dist/plugin.js +126 -4
- package/package.json +3 -1
- package/dist/index.plugin--ecyiAem.js +0 -132
package/README.md
CHANGED
|
@@ -6,6 +6,25 @@ Define routes with folders and let the package generate the
|
|
|
6
6
|
[React Router](https://reactrouter.com/) `RouteObject[]`. It does not replace
|
|
7
7
|
React Router, navigation, history, loaders, or application state.
|
|
8
8
|
|
|
9
|
+
## Release notes
|
|
10
|
+
|
|
11
|
+
### Plugin import
|
|
12
|
+
|
|
13
|
+
The Vite plugin is no longer exported from the main
|
|
14
|
+
`next-vite-router` entry point. Existing imports must be migrated:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// Previous import
|
|
18
|
+
import { nextViteRouter } from "next-vite-router";
|
|
19
|
+
|
|
20
|
+
// Current import
|
|
21
|
+
import { nextViteRouter } from "next-vite-router/plugin";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The plugin remains available through `next-vite-router/plugin`. This separation
|
|
25
|
+
prevents the plugin's Node dependencies from being included in the application's
|
|
26
|
+
browser dependency graph.
|
|
27
|
+
|
|
9
28
|
## Start here
|
|
10
29
|
|
|
11
30
|
```bash
|
|
@@ -83,16 +102,19 @@ npm run dev
|
|
|
83
102
|
|
|
84
103
|
## Use React Router directly
|
|
85
104
|
|
|
86
|
-
`Router` is only a convenience wrapper around
|
|
87
|
-
|
|
105
|
+
`Router` is only a convenience wrapper around `useRoutes(routes)`. Routes are
|
|
106
|
+
generated lazily on the first render, so calls to `configureRouter` and
|
|
107
|
+
`registerMiddleware` made during application setup are applied before route
|
|
108
|
+
elements are created. You can keep using React Router APIs:
|
|
88
109
|
|
|
89
110
|
```tsx
|
|
90
111
|
import {
|
|
91
112
|
Link,
|
|
92
113
|
generateRoutes,
|
|
93
114
|
useRoutes,
|
|
115
|
+
useParams,
|
|
94
116
|
} from "virtual:next-vite-router";
|
|
95
|
-
import { useNavigate
|
|
117
|
+
import { useNavigate } from "react-router-dom";
|
|
96
118
|
|
|
97
119
|
const routes = generateRoutes();
|
|
98
120
|
|
|
@@ -107,9 +129,10 @@ export default function App() {
|
|
|
107
129
|
}
|
|
108
130
|
```
|
|
109
131
|
|
|
110
|
-
Use `useNavigate`,
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
Use `useNavigate`, loaders, actions, and other React Router APIs from
|
|
133
|
+
`react-router-dom`. The package `useParams` hook is also available from the
|
|
134
|
+
virtual module and maps catch-all names such as `slug`. The package does not
|
|
135
|
+
create competing navigation or data-loading APIs.
|
|
113
136
|
|
|
114
137
|
## File conventions
|
|
115
138
|
|
|
@@ -119,10 +142,19 @@ navigation or data-loading APIs.
|
|
|
119
142
|
| `layout.tsx` | Nested layout rendered with React Router's `Outlet` |
|
|
120
143
|
| `not-found.tsx` | `*` route for that subtree |
|
|
121
144
|
| `[id]` | Dynamic segment `:id` |
|
|
122
|
-
| `[...slug]` | Catch-all `*` segment |
|
|
123
|
-
| `[[...slug]]` | Optional catch-all `*` segment |
|
|
145
|
+
| `[...slug]` | Catch-all `*` segment; package `useParams` exposes it as `slug` |
|
|
146
|
+
| `[[...slug]]` | Optional catch-all `*` segment; package `useParams` exposes it as `slug` |
|
|
124
147
|
| `(admin)` | Route group omitted from the URL |
|
|
125
148
|
|
|
149
|
+
Catch-all pages and `not-found.tsx` at the same level both use React Router's
|
|
150
|
+
`*` path and should not be declared together if their fallback behavior must
|
|
151
|
+
be distinct. The catch-all parameter provider wraps the page route, so a
|
|
152
|
+
layout cannot read that parameter through the package `useParams` hook.
|
|
153
|
+
|
|
154
|
+
The Vite plugin accepts custom `pageFile`, `layoutFile`, and `notFoundFile`
|
|
155
|
+
names. `createRouteGenerator` accepts the same options, plus `pagesDir`, as
|
|
156
|
+
its optional fourth argument when custom glob paths or extensions are used.
|
|
157
|
+
|
|
126
158
|
Layouts are the recommended way to add route-scoped navigation, providers, or
|
|
127
159
|
authorization:
|
|
128
160
|
|
|
@@ -146,6 +178,10 @@ export default function DashboardLayout() {
|
|
|
146
178
|
}
|
|
147
179
|
```
|
|
148
180
|
|
|
181
|
+
The Vite plugin is a Node/Vite integration and should be imported from
|
|
182
|
+
`next-vite-router/plugin`; the main `next-vite-router` entry remains safe for
|
|
183
|
+
browser application code.
|
|
184
|
+
|
|
149
185
|
## Type-safe paths
|
|
150
186
|
|
|
151
187
|
The Vite plugin generates `virtual-next-vite-router.d.ts` automatically:
|
|
@@ -167,7 +203,8 @@ No manual declaration for the virtual module is required.
|
|
|
167
203
|
|
|
168
204
|
Pages and `not-found.tsx` files use Vite's non-eager `import.meta.glob`, so
|
|
169
205
|
Vite can emit separate chunks for pages. Layouts are eager because they wrap
|
|
170
|
-
the generated route tree
|
|
206
|
+
the generated route tree; changing a layout can therefore cause a full
|
|
207
|
+
development reload. Lazy loading is enabled by default:
|
|
171
208
|
|
|
172
209
|
```tsx
|
|
173
210
|
import { configureRouter } from "next-vite-router";
|
package/dist/cli.cjs
CHANGED
|
@@ -218,10 +218,19 @@ function createVirtualDTS(projectDir, useSrc) {
|
|
|
218
218
|
|
|
219
219
|
export const generateRoutes: () => RouteObject[];
|
|
220
220
|
export function Router(): ReactElement | null;
|
|
221
|
+
export function useParams<T extends Record<string, string | undefined> = Record<string, string | undefined>>(): T;
|
|
221
222
|
export { Link, useRoutes } from 'react-router-dom';
|
|
222
223
|
}
|
|
223
224
|
`;
|
|
224
225
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
226
|
+
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
227
|
+
const entry = useSrc ? "src/virtual-next-vite-router.d.ts" : "virtual-next-vite-router.d.ts";
|
|
228
|
+
const currentGitignore = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf-8") : "";
|
|
229
|
+
if (!currentGitignore.split(/\r?\n/).includes(entry)) {
|
|
230
|
+
const prefix = currentGitignore && !currentGitignore.endsWith("\n") ? "\n" : "";
|
|
231
|
+
fs.writeFileSync(gitignorePath, `${currentGitignore}${prefix}${entry}
|
|
232
|
+
`, "utf-8");
|
|
233
|
+
}
|
|
225
234
|
}
|
|
226
235
|
function createMainTSX(projectDir, useSrc) {
|
|
227
236
|
const targetDir = useSrc ? path.join(projectDir, "src") : projectDir;
|
|
@@ -535,7 +544,7 @@ const init = new commander.Command().name("init").description(
|
|
|
535
544
|
Project "${projectName}" is ready!
|
|
536
545
|
`);
|
|
537
546
|
});
|
|
538
|
-
const version = "1.
|
|
547
|
+
const version = "1.2.0";
|
|
539
548
|
const pkg = {
|
|
540
549
|
version
|
|
541
550
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
1
2
|
import { default as default_2 } from 'react';
|
|
2
|
-
import { Plugin as Plugin_2 } from 'vite';
|
|
3
3
|
import { ReactElement } from 'react';
|
|
4
4
|
import { RouteObject } from 'react-router-dom';
|
|
5
5
|
|
|
6
|
-
export declare function buildRouteTree(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any
|
|
6
|
+
export declare function buildRouteTree(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any>>, fileOptions?: RouteFileOptions): RouteNode;
|
|
7
|
+
|
|
8
|
+
export declare function CatchAllParamProvider({ name, children, }: {
|
|
9
|
+
name: string;
|
|
10
|
+
children: default_2.ReactElement;
|
|
11
|
+
}): default_2.ReactElement;
|
|
7
12
|
|
|
8
13
|
export declare function clearMiddlewares(): void;
|
|
9
14
|
|
|
@@ -34,23 +39,14 @@ export declare function createLazyElement(loader: () => Promise<any>, cacheKey:
|
|
|
34
39
|
* // Then use it
|
|
35
40
|
* const routes = generateCustomRoutes();
|
|
36
41
|
*/
|
|
37
|
-
export declare function createRouteGenerator(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any
|
|
42
|
+
export declare function createRouteGenerator(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any>>, fileOptions?: RouteFileOptions): () => RouteObject[];
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
45
|
* Extract the route path from a file path
|
|
41
46
|
* @example "../../app/blog/[id]/page.tsx" -> "blog/[id]"
|
|
42
47
|
* @example "../../app/page.tsx" -> ""
|
|
43
48
|
*/
|
|
44
|
-
export declare function extractRoutePath(filePath: string): string;
|
|
45
|
-
|
|
46
|
-
export declare function nextViteRouter(options?: NextViteRouterPluginOptions): Plugin_2;
|
|
47
|
-
|
|
48
|
-
export declare type NextViteRouterPluginOptions = {
|
|
49
|
-
pagesDir?: string;
|
|
50
|
-
pageFile?: string;
|
|
51
|
-
layoutFile?: string;
|
|
52
|
-
notFoundFile?: string;
|
|
53
|
-
};
|
|
49
|
+
export declare function extractRoutePath(filePath: string, { pagesDir, pageFile, layoutFile, notFoundFile }?: RouteFileOptions): string;
|
|
54
50
|
|
|
55
51
|
/**
|
|
56
52
|
* Normalize a Next.js segment to a React Router segment
|
|
@@ -62,14 +58,25 @@ export declare function normalizeSegment(segment: string): string;
|
|
|
62
58
|
|
|
63
59
|
export declare function registerMiddleware(pattern: RegExp, middleware: RouteMiddleware): void;
|
|
64
60
|
|
|
61
|
+
export declare type RouteFileOptions = {
|
|
62
|
+
pagesDir?: string;
|
|
63
|
+
pageFile?: string;
|
|
64
|
+
layoutFile?: string;
|
|
65
|
+
notFoundFile?: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
65
68
|
export declare type RouteMiddleware = (element: ReactElement) => ReactElement;
|
|
66
69
|
|
|
70
|
+
export declare type RouteModuleLoader = () => Promise<{
|
|
71
|
+
default: ComponentType;
|
|
72
|
+
}>;
|
|
73
|
+
|
|
67
74
|
export declare type RouteNode = {
|
|
68
75
|
segment: string;
|
|
69
76
|
fullPath: string;
|
|
70
|
-
page?:
|
|
71
|
-
layout?:
|
|
72
|
-
notFound?:
|
|
77
|
+
page?: RouteModuleLoader;
|
|
78
|
+
layout?: ComponentType;
|
|
79
|
+
notFound?: RouteModuleLoader;
|
|
73
80
|
children: Map<string, RouteNode>;
|
|
74
81
|
};
|
|
75
82
|
|
|
@@ -81,4 +88,6 @@ export declare type RouterConfig = {
|
|
|
81
88
|
|
|
82
89
|
export declare function treeToRoutes(node: RouteNode, isRoot?: boolean, currentPath?: string): RouteObject[];
|
|
83
90
|
|
|
91
|
+
export declare function useParams<T extends Record<string, string | undefined> = Record<string, string | undefined>>(): T;
|
|
92
|
+
|
|
84
93
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,156 +1,196 @@
|
|
|
1
|
-
import { e as
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
const
|
|
1
|
+
import { e as z, n as p } from "./path-BodMUTCM.js";
|
|
2
|
+
import i, { Suspense as E, lazy as L, createContext as M, useContext as x } from "react";
|
|
3
|
+
import { useParams as k } from "react-router-dom";
|
|
4
|
+
const A = {
|
|
5
5
|
enableLazyLoading: !0
|
|
6
6
|
};
|
|
7
|
-
let
|
|
8
|
-
function
|
|
9
|
-
|
|
7
|
+
let m = { ...A };
|
|
8
|
+
function B(e) {
|
|
9
|
+
m = { ...m, ...e };
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
return
|
|
11
|
+
function S() {
|
|
12
|
+
return m;
|
|
13
13
|
}
|
|
14
|
-
let
|
|
15
|
-
function
|
|
16
|
-
|
|
14
|
+
let g = [];
|
|
15
|
+
function H(e, t) {
|
|
16
|
+
g.push({ pattern: e, middleware: t });
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
|
|
18
|
+
function I() {
|
|
19
|
+
g = [];
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
let n =
|
|
23
|
-
for (const
|
|
24
|
-
|
|
21
|
+
function d(e, t) {
|
|
22
|
+
let n = t;
|
|
23
|
+
for (const r of g)
|
|
24
|
+
r.pattern.test(e) && (n = r.middleware(n));
|
|
25
25
|
return n;
|
|
26
26
|
}
|
|
27
|
-
const
|
|
28
|
-
function
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
const
|
|
32
|
-
|
|
27
|
+
const T = () => i.createElement("div"), C = /* @__PURE__ */ new Map();
|
|
28
|
+
function w(e, t, n) {
|
|
29
|
+
const r = S();
|
|
30
|
+
if (r.enableLazyLoading === !1) {
|
|
31
|
+
const u = i.lazy(e), s = i.createElement(
|
|
32
|
+
E,
|
|
33
33
|
{ fallback: null },
|
|
34
34
|
// No loading fallback when disabled
|
|
35
|
-
i.createElement(
|
|
35
|
+
i.createElement(u)
|
|
36
36
|
);
|
|
37
|
-
return n ?
|
|
37
|
+
return n ? d(n, s) : s;
|
|
38
38
|
}
|
|
39
|
-
let
|
|
40
|
-
|
|
41
|
-
const o =
|
|
42
|
-
|
|
39
|
+
let a = C.get(t);
|
|
40
|
+
a || (a = L(e), C.set(t, a));
|
|
41
|
+
const o = r.loadingFallback || T, l = i.createElement(
|
|
42
|
+
E,
|
|
43
43
|
{ fallback: i.createElement(o) },
|
|
44
|
-
i.createElement(
|
|
44
|
+
i.createElement(a)
|
|
45
45
|
);
|
|
46
|
-
return n ?
|
|
47
|
-
}
|
|
48
|
-
function
|
|
49
|
-
const
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
segment:
|
|
54
|
-
fullPath:
|
|
46
|
+
return n ? d(n, l) : l;
|
|
47
|
+
}
|
|
48
|
+
function h(e, t, n, r, a) {
|
|
49
|
+
const o = z(t, a), l = o ? o.split("/") : [];
|
|
50
|
+
let u = e, s = "";
|
|
51
|
+
l.forEach((c, v) => {
|
|
52
|
+
s = s ? `${s}/${c}` : c, u.children.has(c) || u.children.set(c, {
|
|
53
|
+
segment: c,
|
|
54
|
+
fullPath: s,
|
|
55
55
|
children: /* @__PURE__ */ new Map()
|
|
56
|
-
}),
|
|
57
|
-
}),
|
|
56
|
+
}), u = u.children.get(c), v === l.length - 1 && (u[r] = n);
|
|
57
|
+
}), l.length === 0 && (e[r] = n);
|
|
58
58
|
}
|
|
59
|
-
function
|
|
60
|
-
const
|
|
59
|
+
function j(e, t, n, r) {
|
|
60
|
+
const a = {
|
|
61
61
|
segment: "",
|
|
62
62
|
fullPath: "",
|
|
63
63
|
children: /* @__PURE__ */ new Map()
|
|
64
64
|
};
|
|
65
|
-
return Object.entries(
|
|
66
|
-
|
|
67
|
-
}), Object.entries(e).forEach(([
|
|
68
|
-
|
|
69
|
-
}), Object.entries(n).forEach(([
|
|
70
|
-
|
|
71
|
-
}),
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
return Object.entries(t).forEach(([o, l]) => {
|
|
66
|
+
h(a, o, l.default, "layout", r);
|
|
67
|
+
}), Object.entries(e).forEach(([o, l]) => {
|
|
68
|
+
h(a, o, l, "page", r);
|
|
69
|
+
}), Object.entries(n).forEach(([o, l]) => {
|
|
70
|
+
h(a, o, l, "notFound", r);
|
|
71
|
+
}), a;
|
|
72
|
+
}
|
|
73
|
+
const F = M(void 0);
|
|
74
|
+
function N({
|
|
75
|
+
name: e,
|
|
76
|
+
children: t
|
|
77
|
+
}) {
|
|
78
|
+
return i.createElement(
|
|
79
|
+
F.Provider,
|
|
80
|
+
{ value: e },
|
|
81
|
+
t
|
|
78
82
|
);
|
|
79
|
-
return n ? { index: !0, element: t } : { path: f(e.segment), element: t };
|
|
80
83
|
}
|
|
81
|
-
function
|
|
84
|
+
function D() {
|
|
85
|
+
return x(F);
|
|
86
|
+
}
|
|
87
|
+
function P(e, t) {
|
|
88
|
+
return d(e, i.createElement(t));
|
|
89
|
+
}
|
|
90
|
+
function f(e, t, n = !1) {
|
|
91
|
+
const r = e.page;
|
|
92
|
+
if (!r)
|
|
93
|
+
throw new Error(`Route node "${e.fullPath}" has no page loader`);
|
|
94
|
+
const a = w(
|
|
95
|
+
async () => ({ default: (await r()).default }),
|
|
96
|
+
`page:${e.fullPath || t}`,
|
|
97
|
+
t
|
|
98
|
+
), o = e.segment.match(/^\[\[?\.\.\.(.+?)\]\]?$/), l = o ? i.createElement(N, {
|
|
99
|
+
name: o[1],
|
|
100
|
+
children: a
|
|
101
|
+
}) : a;
|
|
102
|
+
return n ? { index: !0, element: l } : { path: p(e.segment), element: l };
|
|
103
|
+
}
|
|
104
|
+
function R(e) {
|
|
105
|
+
const t = e.notFound;
|
|
106
|
+
if (!t)
|
|
107
|
+
throw new Error(`Route node "${e.fullPath}" has no not-found loader`);
|
|
108
|
+
const n = e.fullPath ? `/${e.fullPath}` : "/";
|
|
82
109
|
return {
|
|
83
110
|
path: "*",
|
|
84
|
-
element:
|
|
85
|
-
async () => ({ default: (await
|
|
86
|
-
`notfound:${e.fullPath}
|
|
111
|
+
element: w(
|
|
112
|
+
async () => ({ default: (await t()).default }),
|
|
113
|
+
`notfound:${e.fullPath}`,
|
|
114
|
+
n
|
|
87
115
|
)
|
|
88
116
|
};
|
|
89
117
|
}
|
|
90
|
-
function
|
|
118
|
+
function $(e, t) {
|
|
91
119
|
const n = [];
|
|
92
|
-
return [...e.children.entries()].sort(([
|
|
93
|
-
const o =
|
|
94
|
-
n.push(...
|
|
120
|
+
return [...e.children.entries()].sort(([r], [a]) => r.localeCompare(a)).forEach(([r, a]) => {
|
|
121
|
+
const o = p(r), l = o ? t ? `${t}/${o}` : `/${o}` : t;
|
|
122
|
+
n.push(...b(a, !1, l));
|
|
95
123
|
}), n;
|
|
96
124
|
}
|
|
97
|
-
function
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
function y(e, t) {
|
|
126
|
+
const n = [];
|
|
127
|
+
return e.page && n.push(f(e, t || "/", !0)), n.push(...$(e, t)), e.notFound && n.push(R(e)), n;
|
|
128
|
+
}
|
|
129
|
+
function b(e, t = !1, n = "") {
|
|
130
|
+
const r = [];
|
|
131
|
+
if (t) {
|
|
132
|
+
const c = y(e, "");
|
|
133
|
+
return e.layout ? (r.push({
|
|
134
|
+
path: "/",
|
|
135
|
+
element: P("/", e.layout),
|
|
136
|
+
children: c
|
|
137
|
+
}), r) : e.page ? (r.push({
|
|
138
|
+
path: "/",
|
|
139
|
+
element: f(e, "/", !1).element,
|
|
140
|
+
children: c.slice(1).length > 0 ? c.slice(1) : void 0
|
|
141
|
+
}), r) : e.notFound ? (r.push({
|
|
102
142
|
path: "/",
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}), t;
|
|
143
|
+
children: c
|
|
144
|
+
}), r) : c;
|
|
106
145
|
}
|
|
146
|
+
const a = p(e.segment), o = !a, l = o ? n : n + "/" + a, u = $(e, l), s = e.notFound ? [R(e)] : [];
|
|
107
147
|
if (e.layout) {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
element: i.createElement(e.layout),
|
|
115
|
-
children: u.length > 0 ? u : void 0
|
|
116
|
-
}), t;
|
|
148
|
+
const c = y(e, l);
|
|
149
|
+
return [{
|
|
150
|
+
...o ? {} : { path: a },
|
|
151
|
+
element: P(l, e.layout),
|
|
152
|
+
children: c.length > 0 ? c : void 0
|
|
153
|
+
}];
|
|
117
154
|
}
|
|
118
|
-
if (e.page)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
155
|
+
if (e.page && !o)
|
|
156
|
+
return [{
|
|
157
|
+
path: a,
|
|
158
|
+
element: f(e, l).element,
|
|
159
|
+
children: [...u, ...s].length > 0 ? [...u, ...s] : void 0
|
|
160
|
+
}];
|
|
161
|
+
if (e.page || e.notFound || !o) {
|
|
162
|
+
const c = [
|
|
163
|
+
...e.page ? [f(e, l, !0)] : [],
|
|
164
|
+
...u,
|
|
165
|
+
...s
|
|
166
|
+
];
|
|
167
|
+
return [{
|
|
168
|
+
...o ? {} : { path: a },
|
|
169
|
+
children: c.length > 0 ? c : void 0
|
|
170
|
+
}];
|
|
132
171
|
}
|
|
133
|
-
|
|
134
|
-
return l ? [{
|
|
135
|
-
path: l,
|
|
136
|
-
children: o.length > 0 ? o : void 0
|
|
137
|
-
}] : o;
|
|
172
|
+
return u;
|
|
138
173
|
}
|
|
139
|
-
function
|
|
174
|
+
function J(e, t, n, r) {
|
|
140
175
|
return () => {
|
|
141
|
-
const
|
|
142
|
-
return
|
|
176
|
+
const a = j(e, t, n, r);
|
|
177
|
+
return b(a, !0);
|
|
143
178
|
};
|
|
144
179
|
}
|
|
180
|
+
function Q() {
|
|
181
|
+
const e = k(), t = D();
|
|
182
|
+
return typeof t == "string" && e["*"] !== void 0 ? { ...e, [t]: e["*"] } : e;
|
|
183
|
+
}
|
|
145
184
|
export {
|
|
146
|
-
|
|
147
|
-
j as
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
185
|
+
N as CatchAllParamProvider,
|
|
186
|
+
j as buildRouteTree,
|
|
187
|
+
I as clearMiddlewares,
|
|
188
|
+
B as configureRouter,
|
|
189
|
+
w as createLazyElement,
|
|
190
|
+
J as createRouteGenerator,
|
|
191
|
+
z as extractRoutePath,
|
|
192
|
+
p as normalizeSegment,
|
|
193
|
+
H as registerMiddleware,
|
|
194
|
+
b as treeToRoutes,
|
|
195
|
+
Q as useParams
|
|
156
196
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function $(e) {
|
|
2
|
+
return /^\([^)]+\)$/.test(e) ? "" : /^\[\[\.\.\.(.+)\]\]$/.test(e) || /^\[\.\.\.(.+)\]$/.test(e) ? "*" : /^\[([^\]]+)\]$/.test(e) ? e.replace(/^\[([^\]]+)\]$/, ":$1") : e;
|
|
3
|
+
}
|
|
4
|
+
function u(e, { pagesDir: t, pageFile: a = "page.tsx", layoutFile: n = "layout.tsx", notFoundFile: c = "not-found.tsx" } = {}) {
|
|
5
|
+
const o = e.replace(/\\/g, "/"), r = t == null ? void 0 : t.replace(/\\/g, "/").replace(/^\/|\/$/g, ""), l = [a, n, c].map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
6
|
+
return o.replace(
|
|
7
|
+
r ? new RegExp(`^.*(?:^|/)${r}/`) : /^.*(?:^|\/)(?:app|pages)\//,
|
|
8
|
+
""
|
|
9
|
+
).replace(/\\/g, "/").replace(new RegExp(`/?(?:${l})$`), "");
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
u as e,
|
|
13
|
+
$ as n
|
|
14
|
+
};
|
package/dist/plugin.js
CHANGED
|
@@ -1,6 +1,128 @@
|
|
|
1
|
-
import "node:fs";
|
|
2
|
-
import "node:path";
|
|
3
|
-
import {
|
|
1
|
+
import m from "node:fs";
|
|
2
|
+
import i from "node:path";
|
|
3
|
+
import { n as g } from "./path-BodMUTCM.js";
|
|
4
|
+
function h(o) {
|
|
5
|
+
const e = o.split(i.sep).filter(Boolean).map(g).filter(Boolean);
|
|
6
|
+
return e.length > 0 ? `/${e.join("/")}` : "/";
|
|
7
|
+
}
|
|
8
|
+
function x(o, e) {
|
|
9
|
+
const s = /* @__PURE__ */ new Set();
|
|
10
|
+
function r(n) {
|
|
11
|
+
for (const u of m.readdirSync(n, { withFileTypes: !0 })) {
|
|
12
|
+
const a = i.join(n, u.name);
|
|
13
|
+
u.isDirectory() ? r(a) : u.isFile() && u.name === e && s.add(h(i.relative(o, n)));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return m.existsSync(o) && r(o), [...s].sort();
|
|
17
|
+
}
|
|
18
|
+
function v(o) {
|
|
19
|
+
const e = o.match(/^\/(?:\[\.\.\.|:)([^/\]]+)\]?$/);
|
|
20
|
+
return e == null ? void 0 : e[1];
|
|
21
|
+
}
|
|
22
|
+
function P(o) {
|
|
23
|
+
const e = o.map((r) => ` | "${r}"`).join(`
|
|
24
|
+
`), s = o.map((r) => {
|
|
25
|
+
const n = r.split("/").map((a) => v(`/${a}`)).filter((a) => !!a), u = n.length > 0 ? n.map((a) => ` ${a}: string;`).join(`
|
|
26
|
+
`) : " [key: string]: never;";
|
|
27
|
+
return ` "${r}": {
|
|
28
|
+
${u}
|
|
29
|
+
};`;
|
|
30
|
+
}).join(`
|
|
31
|
+
`);
|
|
32
|
+
return `declare module 'virtual:next-vite-router' {
|
|
33
|
+
import type { ReactElement } from 'react';
|
|
34
|
+
import type { RouteObject } from 'react-router-dom';
|
|
35
|
+
|
|
36
|
+
export type RoutePath =
|
|
37
|
+
${e || ' | "/"'};
|
|
38
|
+
|
|
39
|
+
export type RouteParams<P extends RoutePath> = P extends keyof RoutePathParams
|
|
40
|
+
? RoutePathParams[P]
|
|
41
|
+
: never;
|
|
42
|
+
|
|
43
|
+
export interface RoutePathParams {
|
|
44
|
+
${s}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const generateRoutes: () => RouteObject[];
|
|
48
|
+
export function Router(): ReactElement | null;
|
|
49
|
+
export function useParams<T extends Record<string, string | undefined> = Record<string, string | undefined>>(): T;
|
|
50
|
+
export { Link, useRoutes } from 'react-router-dom';
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
function k(o = {}) {
|
|
55
|
+
const {
|
|
56
|
+
pagesDir: e = "src/app",
|
|
57
|
+
pageFile: s = "page.tsx",
|
|
58
|
+
layoutFile: r = "layout.tsx",
|
|
59
|
+
notFoundFile: n = "not-found.tsx"
|
|
60
|
+
} = o, u = { pagesDir: e, pageFile: s, layoutFile: r, notFoundFile: n }, a = "virtual:next-vite-router", c = "\0" + a;
|
|
61
|
+
let l = "", p = "";
|
|
62
|
+
function d() {
|
|
63
|
+
const t = P(x(l, s));
|
|
64
|
+
m.writeFileSync(p, t, "utf-8");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
name: "next-vite-router",
|
|
68
|
+
configResolved(t) {
|
|
69
|
+
l = i.resolve(t.root, e), p = i.join(i.dirname(l), "virtual-next-vite-router.d.ts"), d();
|
|
70
|
+
},
|
|
71
|
+
configureServer(t) {
|
|
72
|
+
const f = (y) => {
|
|
73
|
+
if (i.relative(l, y).startsWith("..") || ![s, r, n].includes(i.basename(y)))
|
|
74
|
+
return;
|
|
75
|
+
d();
|
|
76
|
+
const R = t.moduleGraph.getModuleById(c);
|
|
77
|
+
R && (t.moduleGraph.invalidateModule(R), t.ws.send({ type: "full-reload", path: "*" }));
|
|
78
|
+
};
|
|
79
|
+
t.watcher.on("add", f), t.watcher.on("unlink", f);
|
|
80
|
+
},
|
|
81
|
+
resolveId(t) {
|
|
82
|
+
if (t === a)
|
|
83
|
+
return c;
|
|
84
|
+
},
|
|
85
|
+
load(t) {
|
|
86
|
+
if (t === c)
|
|
87
|
+
return `
|
|
88
|
+
import { buildRouteTree, treeToRoutes, normalizeSegment, createLazyElement, useParams } from 'next-vite-router';
|
|
89
|
+
import { Link, useRoutes } from 'react-router-dom';
|
|
90
|
+
|
|
91
|
+
const pagesGlob = import.meta.glob('/${e}/**/${s}');
|
|
92
|
+
const layoutsGlob = import.meta.glob('/${e}/**/${r}', { eager: true });
|
|
93
|
+
const notFoundsGlob = import.meta.glob('/${e}/**/${n}');
|
|
94
|
+
|
|
95
|
+
const transformPath = (path) => path.replace('/${e}/', '');
|
|
96
|
+
|
|
97
|
+
const pages = Object.fromEntries(
|
|
98
|
+
Object.entries(pagesGlob).map(([key, value]) => [transformPath(key), value])
|
|
99
|
+
);
|
|
100
|
+
const layouts = Object.fromEntries(
|
|
101
|
+
Object.entries(layoutsGlob).map(([key, value]) => [transformPath(key), value])
|
|
102
|
+
);
|
|
103
|
+
const notFounds = Object.fromEntries(
|
|
104
|
+
Object.entries(notFoundsGlob).map(([key, value]) => [transformPath(key), value])
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
export function generateRoutes() {
|
|
108
|
+
const tree = buildRouteTree(pages, layouts, notFounds, ${JSON.stringify(u)});
|
|
109
|
+
const routes = treeToRoutes(tree, true);
|
|
110
|
+
return routes;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function Router() {
|
|
114
|
+
routes ??= generateRoutes();
|
|
115
|
+
return useRoutes(routes);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let routes;
|
|
119
|
+
|
|
120
|
+
export { Link, useRoutes };
|
|
121
|
+
export { useParams };
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
4
126
|
export {
|
|
5
|
-
|
|
127
|
+
k as nextViteRouter
|
|
6
128
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-vite-router",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Next.js-like file-based routing for React + Vite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
8
9
|
"bin": {
|
|
9
10
|
"create-next-vite-router": "./dist/cli.cjs"
|
|
10
11
|
},
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"test:local": "node test/test-package.js",
|
|
66
67
|
"test:routes": "node test/test-route-types.js",
|
|
67
68
|
"test:generator": "node --test test/routes.test.js",
|
|
69
|
+
"test:params": "node --test test/params.test.js",
|
|
68
70
|
"test:compatibility": "node test/test-compatibility.js",
|
|
69
71
|
"preview": "pnpm pack --dry-run",
|
|
70
72
|
"cli": "cd test && node ../dist/cli.cjs"
|
|
@@ -1,132 +0,0 @@
|
|
|
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
|
-
};
|