next-vite-router 1.2.3 → 1.2.4

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
@@ -133,13 +133,17 @@ create competing navigation or data-loading APIs.
133
133
  | `(admin)` | Route group omitted from the URL |
134
134
 
135
135
  Catch-all pages and `not-found.tsx` at the same level both use React Router's
136
- `*` path and should not be declared together if their fallback behavior must
137
- be distinct. The catch-all parameter provider wraps the page route, so a
138
- layout cannot read that parameter through the package `useParams` hook.
136
+ `*` path and are rejected during route validation because their fallback
137
+ behavior would be ambiguous. The catch-all parameter provider wraps the page
138
+ route, so a layout cannot read that parameter through the package `useParams`
139
+ hook.
139
140
 
140
141
  The Vite plugin accepts custom `pageFile`, `layoutFile`, and `notFoundFile`
141
142
  names. `createRouteGenerator` accepts the same options, plus `pagesDir`, as
142
143
  its optional fourth argument when custom glob paths or extensions are used.
144
+ For the Vite plugin, `pagesDir` is relative to the Vite project root (the
145
+ default is `src/app`). The lower-level route path utility also accepts an
146
+ absolute `pagesDir`, which is useful for custom route generators.
143
147
 
144
148
  Layouts are the recommended way to add route-scoped navigation, providers, or
145
149
  authorization:
@@ -302,7 +306,12 @@ pnpm exec tsc -p test/fixture/tsconfig.json --noEmit
302
306
  pnpm run test:local
303
307
  ```
304
308
 
305
- The route matrix lives in [`test/routes.test.js`](./test/routes.test.js).
309
+ The route matrix lives in [`test/routes.test.js`](./test/routes.test.js) and
310
+ includes React Router `matchRoutes` assertions for dynamic parameters, route
311
+ groups, nested `not-found.tsx` fallbacks, and catch-all pages under layouts.
312
+ `test/test-route-types.js` covers generated TypeScript declarations and HMR
313
+ updates, while `test/test-compatibility.js` checks the supported dependency
314
+ ranges and generated production bootstrap.
306
315
 
307
316
  ## License
308
317
 
package/dist/cli.cjs CHANGED
@@ -548,7 +548,7 @@ const init = new commander.Command().name("init").description(
548
548
  Project "${projectName}" is ready!
549
549
  `);
550
550
  });
551
- const version = "1.2.3";
551
+ const version = "1.2.4";
552
552
  const pkg = {
553
553
  version
554
554
  };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,8 @@ import { RouteObject } from 'react-router-dom';
5
5
 
6
6
  export declare function assertNoDuplicateRoutes(filePaths: string[], fileOptions?: RouteFileOptions): void;
7
7
 
8
+ export declare function assertNoRouteConflicts(pageFiles: string[], notFoundFiles: string[], fileOptions?: RouteFileOptions): void;
9
+
8
10
  export declare function buildRouteTree(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any>>, fileOptions?: RouteFileOptions): RouteNode;
9
11
 
10
12
  export declare function CatchAllParamProvider({ name, children, }: {
@@ -47,6 +49,9 @@ export declare function createRouteGenerator(pages: Record<string, () => Promise
47
49
  * Extract the route path from a file path
48
50
  * @example "../../app/blog/[id]/page.tsx" -> "blog/[id]"
49
51
  * @example "../../app/page.tsx" -> ""
52
+ *
53
+ * `pagesDir` may be relative or absolute. The plugin resolves it relative to
54
+ * the Vite project root before passing it here.
50
55
  */
51
56
  export declare function extractRoutePath(filePath: string, { pagesDir, pageFile, layoutFile, notFoundFile }?: RouteFileOptions): string;
52
57
 
@@ -58,6 +63,8 @@ export declare function extractRoutePath(filePath: string, { pagesDir, pageFile,
58
63
  */
59
64
  export declare function normalizeSegment(segment: string): string;
60
65
 
66
+ export declare function parseRouteSegments(routePath: string): RouteSegment[];
67
+
61
68
  export declare function registerMiddleware(pattern: RegExp, middleware: RouteMiddleware): void;
62
69
 
63
70
  export declare type RouteFileOptions = {
@@ -84,10 +91,26 @@ export declare type RouteNode = {
84
91
 
85
92
  export declare type RouterConfig = {
86
93
  loadingFallback?: React.ComponentType;
87
- pagesDir?: string;
88
94
  enableLazyLoading?: boolean;
89
95
  };
90
96
 
97
+ export declare type RouteSegment = {
98
+ kind: "group";
99
+ name: string;
100
+ } | {
101
+ kind: "static";
102
+ name: string;
103
+ } | {
104
+ kind: "dynamic";
105
+ name: string;
106
+ } | {
107
+ kind: "catchAll";
108
+ name: string;
109
+ optional: boolean;
110
+ };
111
+
112
+ export declare function routeSegmentToPath(segment: RouteSegment): string;
113
+
91
114
  export declare function treeToRoutes(node: RouteNode, isRoot?: boolean, currentPath?: string): RouteObject[];
92
115
 
93
116
  export declare function useParams<T extends Record<string, string | undefined> = Record<string, string | undefined>>(): T;
package/dist/index.js CHANGED
@@ -1,193 +1,222 @@
1
- import { n as p, b as z } from "./tree-HwBRWJNI.js";
2
- import { a as V, e as W } from "./tree-HwBRWJNI.js";
3
- import c, { Suspense as d, lazy as b, createContext as v, useContext as L } from "react";
4
- import { useParams as A } from "react-router-dom";
5
- const M = {
1
+ import { p, r as v, b as z } from "./tree-DW5A1s6W.js";
2
+ import { a as K, c as tt, e as et, n as nt } from "./tree-DW5A1s6W.js";
3
+ import u, { Suspense as R, lazy as F, createContext as $, useContext as A } from "react";
4
+ import { useParams as k } from "react-router-dom";
5
+ const N = {
6
6
  enableLazyLoading: !0
7
7
  };
8
- let m = { ...M };
9
- function j(e) {
10
- m = { ...m, ...e };
8
+ let f = { ...N };
9
+ function U(t) {
10
+ f = { ...f, ...t };
11
11
  }
12
- function k() {
13
- return m;
12
+ function M() {
13
+ return f;
14
14
  }
15
- let g = [];
16
- function q(e, t) {
17
- g.push({ pattern: e, middleware: t });
15
+ let h = [];
16
+ function V(t, e) {
17
+ h.push({ pattern: t, middleware: e });
18
18
  }
19
- function B() {
20
- g = [];
19
+ function W() {
20
+ h = [];
21
21
  }
22
- function h(e, t) {
23
- let n = t;
24
- for (const r of g)
25
- r.pattern.lastIndex = 0, r.pattern.test(e) && (n = r.middleware(n)), r.pattern.lastIndex = 0;
22
+ function m(t, e) {
23
+ let n = e;
24
+ for (const r of h)
25
+ r.pattern.lastIndex = 0, r.pattern.test(t) && (n = r.middleware(n)), r.pattern.lastIndex = 0;
26
26
  return n;
27
27
  }
28
- const S = () => c.createElement("div"), C = /* @__PURE__ */ new Map();
29
- function R(e, t, n) {
30
- const r = k();
28
+ const T = () => u.createElement("div"), C = /* @__PURE__ */ new Map();
29
+ function y(t, e, n) {
30
+ const r = M();
31
31
  if (r.enableLazyLoading === !1) {
32
- const s = c.lazy(e), i = c.createElement(
33
- d,
32
+ const c = u.lazy(t), d = u.createElement(
33
+ R,
34
34
  { fallback: null },
35
35
  // No loading fallback when disabled
36
- c.createElement(s)
36
+ u.createElement(c)
37
37
  );
38
- return n ? h(n, i) : i;
38
+ return n ? m(n, d) : d;
39
39
  }
40
- let a = C.get(t);
41
- a || (a = b(e), C.set(t, a));
42
- const l = r.loadingFallback || S, o = c.createElement(
43
- d,
44
- { fallback: c.createElement(l) },
45
- c.createElement(a)
40
+ let a = C.get(e);
41
+ a || (a = F(t), C.set(e, a));
42
+ const o = r.loadingFallback || T, l = u.createElement(
43
+ R,
44
+ { fallback: u.createElement(o) },
45
+ u.createElement(a)
46
46
  );
47
- return n ? h(n, o) : o;
47
+ return n ? m(n, l) : l;
48
48
  }
49
- const w = v(void 0);
49
+ const E = $(void 0);
50
50
  function D({
51
- name: e,
52
- children: t
51
+ name: t,
52
+ children: e
53
53
  }) {
54
- return c.createElement(
55
- w.Provider,
56
- { value: e },
57
- t
54
+ return u.createElement(
55
+ E.Provider,
56
+ { value: t },
57
+ e
58
58
  );
59
59
  }
60
- function N() {
61
- return L(w);
60
+ function I() {
61
+ return A(E);
62
62
  }
63
- function y(e, t) {
64
- return h(e, c.createElement(t));
63
+ function w(t, e) {
64
+ return m(t, u.createElement(e));
65
65
  }
66
- function T(e) {
67
- return /^\[\[\.\.\..+\]\]$/.test(e);
66
+ function S(t) {
67
+ const e = p(t)[0];
68
+ return (e == null ? void 0 : e.kind) === "catchAll" && e.optional;
68
69
  }
69
- function f(e, t, n = !1) {
70
- const r = e.page;
70
+ function g(t) {
71
+ const e = p(t)[0];
72
+ return e ? v(e) : "";
73
+ }
74
+ function s(t, e) {
75
+ return i(t, e, !0);
76
+ }
77
+ function i(t, e, n = !1) {
78
+ const r = t.page;
71
79
  if (!r)
72
- throw new Error(`Route node "${e.fullPath}" has no page loader`);
73
- const a = R(
80
+ throw new Error(`Route node "${t.fullPath}" has no page loader`);
81
+ const a = y(
74
82
  async () => ({ default: (await r()).default }),
75
- `page:${e.fullPath || t}`,
76
- t
77
- ), l = e.segment.match(/^\[\[?\.\.\.(.+?)\]\]?$/), o = l ? c.createElement(D, {
78
- name: l[1],
83
+ `page:${t.fullPath || e}`,
84
+ e
85
+ ), o = p(t.segment)[0], l = (o == null ? void 0 : o.kind) === "catchAll" ? u.createElement(D, {
86
+ name: o.name,
79
87
  children: a
80
88
  }) : a;
81
- return n ? { index: !0, element: o } : { path: p(e.segment), element: o };
89
+ return n ? { index: !0, element: l } : { path: g(t.segment), element: l };
82
90
  }
83
- function P(e) {
84
- const t = e.notFound;
85
- if (!t)
86
- throw new Error(`Route node "${e.fullPath}" has no not-found loader`);
87
- const n = e.fullPath ? `/${e.fullPath}` : "/";
91
+ function G(t) {
92
+ const e = t.notFound;
93
+ if (!e)
94
+ throw new Error(`Route node "${t.fullPath}" has no not-found loader`);
95
+ const n = t.fullPath ? `/${t.fullPath}` : "/";
88
96
  return {
89
97
  path: "*",
90
- element: R(
91
- async () => ({ default: (await t()).default }),
92
- `notfound:${e.fullPath}`,
98
+ element: y(
99
+ async () => ({ default: (await e()).default }),
100
+ `notfound:${t.fullPath}`,
93
101
  n
94
102
  )
95
103
  };
96
104
  }
97
- function F(e, t) {
105
+ function P(t) {
106
+ return t.notFound ? [G(t)] : [];
107
+ }
108
+ function O(t, e, n, r) {
109
+ return {
110
+ ...e ? { path: e } : {},
111
+ element: w(t, n),
112
+ children: r.length > 0 ? r : void 0
113
+ };
114
+ }
115
+ function x(t, e) {
98
116
  const n = [];
99
- return [...e.children.entries()].sort(([r], [a]) => r.localeCompare(a)).forEach(([r, a]) => {
100
- const l = p(r), o = l ? t ? `${t}/${l}` : `/${l}` : t;
101
- n.push(...$(a, !1, o));
117
+ return [...t.children.entries()].sort(([r], [a]) => r.localeCompare(a)).forEach(([r, a]) => {
118
+ const o = g(r), l = o ? e ? `${e}/${o}` : `/${o}` : e;
119
+ n.push(...b(a, !1, l));
102
120
  }), n;
103
121
  }
104
- function E(e, t) {
122
+ function L(t, e) {
105
123
  const n = [];
106
- return e.page && n.push(f(e, t || "/", !0)), n.push(...F(e, t)), e.notFound && n.push(P(e)), n;
107
- }
108
- function $(e, t = !1, n = "") {
109
- const r = [];
110
- if (t) {
111
- const u = E(e, "");
112
- return e.layout ? (r.push({
113
- path: "/",
114
- element: y("/", e.layout),
115
- children: u
116
- }), r) : e.page ? u.length > 1 ? [{
117
- path: "/",
118
- children: u
119
- }] : [{
120
- path: "/",
121
- element: f(e, "/", !1).element
122
- }] : e.notFound ? (r.push({
123
- path: "/",
124
- children: u
125
- }), r) : u;
126
- }
127
- const a = p(e.segment), l = !a, o = l ? n : n + "/" + a, s = F(e, o), i = e.notFound ? [P(e)] : [];
128
- if (e.layout) {
129
- const u = E(e, o);
130
- return [{
131
- ...l ? {} : { path: a },
132
- element: y(o, e.layout),
133
- children: u.length > 0 ? u : void 0
134
- }];
135
- }
136
- if (e.page && !l) {
137
- if (T(e.segment)) {
138
- const u = f(e, n, !0), x = {
139
- path: a,
140
- element: f(e, o).element,
141
- children: [...s, ...i].length > 0 ? [...s, ...i] : void 0
142
- };
143
- return [u, x];
144
- }
145
- return s.length > 0 || i.length > 0 ? [{
146
- path: a,
147
- children: [
148
- f(e, o, !0),
149
- ...s,
150
- ...i
151
- ]
152
- }] : [{
153
- path: a,
154
- element: f(e, o).element
155
- }];
156
- }
157
- if (e.page || e.notFound || !l) {
158
- const u = [
159
- ...e.page ? [f(e, o, !0)] : [],
160
- ...s,
161
- ...i
124
+ return t.page && n.push(s(t, e || "/")), n.push(...x(t, e)), n.push(...P(t)), n;
125
+ }
126
+ function j(t, e, n, r, a, o) {
127
+ if (S(t.segment)) {
128
+ const l = [...a, ...o];
129
+ return [
130
+ s(t, e),
131
+ {
132
+ path: r,
133
+ element: i(t, n).element,
134
+ children: l.length > 0 ? l : void 0
135
+ }
162
136
  ];
163
- return [{
164
- ...l ? {} : { path: a },
165
- children: u.length > 0 ? u : void 0
166
- }];
167
137
  }
168
- return s;
169
- }
170
- function H(e, t, n, r) {
138
+ return a.length > 0 || o.length > 0 ? [{
139
+ path: r,
140
+ children: [
141
+ s(t, n),
142
+ ...a,
143
+ ...o
144
+ ]
145
+ }] : [{
146
+ path: r,
147
+ element: i(t, n).element
148
+ }];
149
+ }
150
+ function q(t, e, n, r) {
151
+ const a = [
152
+ ...t.page ? [s(t, e)] : [],
153
+ ...n,
154
+ ...r
155
+ ];
156
+ return [{
157
+ children: a.length > 0 ? a : void 0
158
+ }];
159
+ }
160
+ function B(t) {
161
+ const e = L(t, "");
162
+ return t.layout ? [{
163
+ path: "/",
164
+ element: w("/", t.layout),
165
+ children: e
166
+ }] : t.page ? e.length > 1 ? [{ path: "/", children: e }] : [{
167
+ path: "/",
168
+ element: i(t, "/", !1).element
169
+ }] : t.notFound ? [{ path: "/", children: e }] : e;
170
+ }
171
+ function b(t, e = !1, n = "") {
172
+ if (e)
173
+ return B(t);
174
+ const r = g(t.segment), a = !r, o = a ? n : n + "/" + r, l = x(t, o), c = P(t);
175
+ return t.layout ? [O(
176
+ o,
177
+ a ? "" : r,
178
+ t.layout,
179
+ L(t, o)
180
+ )] : t.page && !a ? j(
181
+ t,
182
+ n,
183
+ o,
184
+ r,
185
+ l,
186
+ c
187
+ ) : t.page || t.notFound || !a ? a ? q(t, o, l, c) : [{
188
+ path: r,
189
+ children: [
190
+ ...t.page ? [s(t, o)] : [],
191
+ ...l,
192
+ ...c
193
+ ]
194
+ }] : l;
195
+ }
196
+ function X(t, e, n, r) {
171
197
  return () => {
172
- const a = z(e, t, n, r);
173
- return $(a, !0);
198
+ const a = z(t, e, n, r);
199
+ return b(a, !0);
174
200
  };
175
201
  }
176
- function J() {
177
- const e = A(), t = N();
178
- return typeof t == "string" && e["*"] !== void 0 ? { ...e, [t]: e["*"] } : e;
202
+ function Y() {
203
+ const t = k(), e = I();
204
+ return typeof e == "string" && t["*"] !== void 0 ? { ...t, [e]: t["*"] } : t;
179
205
  }
180
206
  export {
181
207
  D as CatchAllParamProvider,
182
- V as assertNoDuplicateRoutes,
208
+ K as assertNoDuplicateRoutes,
209
+ tt as assertNoRouteConflicts,
183
210
  z as buildRouteTree,
184
- B as clearMiddlewares,
185
- j as configureRouter,
186
- R as createLazyElement,
187
- H as createRouteGenerator,
188
- W as extractRoutePath,
189
- p as normalizeSegment,
190
- q as registerMiddleware,
191
- $ as treeToRoutes,
192
- J as useParams
211
+ W as clearMiddlewares,
212
+ U as configureRouter,
213
+ y as createLazyElement,
214
+ X as createRouteGenerator,
215
+ et as extractRoutePath,
216
+ nt as normalizeSegment,
217
+ p as parseRouteSegments,
218
+ V as registerMiddleware,
219
+ v as routeSegmentToPath,
220
+ b as treeToRoutes,
221
+ Y as useParams
193
222
  };
package/dist/plugin.js CHANGED
@@ -1,60 +1,77 @@
1
- import h from "node:fs";
1
+ import P from "node:fs";
2
2
  import l from "node:path";
3
- import { a as b, n as j } from "./tree-HwBRWJNI.js";
4
- function $(n) {
5
- const e = n.split(l.sep).filter(Boolean).map(j).filter(Boolean);
6
- return e.length > 0 ? `/${e.join("/")}` : "/";
3
+ import { c as $, p as S, r as k } from "./tree-DW5A1s6W.js";
4
+ function j(n) {
5
+ const a = n.map(k).filter(Boolean);
6
+ return a.length > 0 ? `/${a.join("/")}` : "/";
7
7
  }
8
- function T(n, e) {
8
+ function w(n) {
9
+ const a = [
10
+ ["pagesDir", n.pagesDir],
11
+ ["pageFile", n.pageFile],
12
+ ["layoutFile", n.layoutFile],
13
+ ["notFoundFile", n.notFoundFile]
14
+ ];
15
+ for (const [e, o] of a)
16
+ if (!o.trim())
17
+ throw new Error(`nextViteRouter option "${e}" must not be empty.`);
18
+ const r = a.slice(1), i = r.find(
19
+ ([, e], o) => r.slice(o + 1).some(([, u]) => u === e)
20
+ );
21
+ if (i)
22
+ throw new Error(
23
+ `nextViteRouter file options must be distinct; "${i[0]}" is duplicated.`
24
+ );
25
+ }
26
+ function O(n) {
27
+ return n.replace(/\\/g, "/").replace(/^\/|\/$/g, "");
28
+ }
29
+ function T(n, a) {
9
30
  const r = /* @__PURE__ */ new Map();
10
- function i(a) {
11
- for (const o of h.readdirSync(a, { withFileTypes: !0 })) {
12
- const p = l.join(a, o.name);
31
+ function i(e) {
32
+ for (const o of P.readdirSync(e, { withFileTypes: !0 })) {
33
+ const u = l.join(e, o.name);
13
34
  if (o.isDirectory())
14
- i(p);
15
- else if (o.isFile() && o.name === e) {
16
- const s = l.relative(n, a), u = s.split(l.sep).filter(Boolean), y = u.map((t) => {
17
- const c = t.match(/^\[\[\.\.\.(.+)\]\]$/);
18
- if (c)
19
- return { name: c[1], optional: !0 };
20
- const m = t.match(/^\[\.\.\.(.+)\]$/);
21
- return m ? { name: m[1], optional: !1 } : void 0;
22
- }).filter((t) => !!t), d = $(s);
23
- r.set(d, { path: d, params: y });
24
- const f = u[u.length - 1];
25
- if (f == null ? void 0 : f.match(/^\[\[\.\.\.(.+)\]\]$/)) {
26
- const t = u.slice(0, -1).join(l.sep), c = $(t);
27
- r.has(c) || r.set(c, { path: c, params: [] });
35
+ i(u);
36
+ else if (o.isFile() && o.name === a) {
37
+ const d = l.relative(n, e).split(l.sep).filter(Boolean), s = S(d.join("/")), R = s.filter(
38
+ (c) => c.kind === "dynamic" || c.kind === "catchAll"
39
+ ).map((c) => ({
40
+ name: c.name,
41
+ optional: c.kind === "catchAll" && c.optional
42
+ })), m = j(s);
43
+ r.set(m, { path: m, params: R });
44
+ const f = s[s.length - 1];
45
+ if ((f == null ? void 0 : f.kind) === "catchAll" && f.optional) {
46
+ const c = d.slice(0, -1).join(l.sep), t = j(
47
+ S(c.split(l.sep).join("/"))
48
+ );
49
+ r.has(t) || r.set(t, { path: t, params: [] });
28
50
  }
29
51
  }
30
52
  }
31
53
  }
32
- return h.existsSync(n) && i(n), [...r.values()].sort((a, o) => a.path.localeCompare(o.path));
54
+ return P.existsSync(n) && i(n), [...r.values()].sort((e, o) => e.path.localeCompare(o.path));
33
55
  }
34
- function F(n, e) {
56
+ function b(n, a) {
35
57
  const r = [];
36
- function i(a) {
37
- for (const o of h.readdirSync(a, { withFileTypes: !0 })) {
38
- const p = l.join(a, o.name);
39
- o.isDirectory() ? i(p) : o.isFile() && o.name === e && r.push(l.relative(n, p));
58
+ function i(e) {
59
+ for (const o of P.readdirSync(e, { withFileTypes: !0 })) {
60
+ const u = l.join(e, o.name);
61
+ o.isDirectory() ? i(u) : o.isFile() && o.name === a && r.push(l.relative(n, u));
40
62
  }
41
63
  }
42
- return h.existsSync(n) && i(n), r;
64
+ return P.existsSync(n) && i(n), r;
43
65
  }
44
- function k(n) {
45
- const e = n.match(/^\/(?:\[\.\.\.|:)([^/\]]+)\]?$/);
46
- return e == null ? void 0 : e[1];
47
- }
48
- function w(n) {
49
- const e = n.map(({ path: i }) => ` | "${i}"`).join(`
50
- `), r = n.map(({ path: i, params: a }) => {
51
- const o = i.split("/").map((s) => k(`/${s}`)).filter((s) => !!s), p = o.length > 0 || a.length > 0 ? [
52
- ...o.map((s) => ` ${s}: string;`),
53
- ...a.map(({ name: s, optional: u }) => ` ${s}${u ? "?" : ""}: string;`)
66
+ function G(n) {
67
+ const a = n.map(({ path: i }) => ` | ${JSON.stringify(i)}`).join(`
68
+ `), r = n.map(({ path: i, params: e }) => {
69
+ const o = e.length > 0 ? [
70
+ ...e.map(({ name: u, optional: y }) => ` ${u}${y ? "?" : ""}: string;`)
54
71
  ].join(`
55
72
  `) : " [key: string]: never;";
56
- return ` "${i}": {
57
- ${p}
73
+ return ` ${JSON.stringify(i)}: {
74
+ ${o}
58
75
  };`;
59
76
  }).join(`
60
77
  `);
@@ -63,7 +80,7 @@ ${p}
63
80
  import type { RouteObject } from 'react-router-dom';
64
81
 
65
82
  export type RoutePath =
66
- ${e || ' | "/"'};
83
+ ${a || ' | "/"'};
67
84
 
68
85
  export type RouteParams<P extends RoutePath> = P extends keyof RoutePathParams
69
86
  ? RoutePathParams[P]
@@ -80,65 +97,76 @@ ${r}
80
97
  }
81
98
  `;
82
99
  }
83
- function D(n = {}) {
100
+ function M(n = {}) {
84
101
  const {
85
- pagesDir: e = "src/app",
102
+ pagesDir: a = "src/app",
86
103
  pageFile: r = "page.tsx",
87
104
  layoutFile: i = "layout.tsx",
88
- notFoundFile: a = "not-found.tsx"
89
- } = n, o = { pagesDir: e, pageFile: r, layoutFile: i, notFoundFile: a }, p = "virtual:next-vite-router", s = "\0" + p;
90
- let u = "", y = "", d, f;
91
- function g() {
92
- const t = w(T(u, r));
93
- h.writeFileSync(y, t, "utf-8");
105
+ notFoundFile: e = "not-found.tsx"
106
+ } = n;
107
+ w({ pagesDir: a, pageFile: r, layoutFile: i, notFoundFile: e });
108
+ const o = O(a), u = { pagesDir: a, pageFile: r, layoutFile: i, notFoundFile: e }, y = "virtual:next-vite-router", d = "\0" + y;
109
+ let s = "", R = "", m, f;
110
+ function c() {
111
+ const t = G(T(s, r));
112
+ P.writeFileSync(R, t, "utf-8");
94
113
  }
95
114
  return {
96
115
  name: "next-vite-router",
97
116
  configResolved(t) {
98
- u = l.resolve(t.root, e), y = l.join(l.dirname(u), "virtual-next-vite-router.d.ts"), b(F(u, r), o), g();
117
+ s = l.resolve(t.root, a), R = l.join(l.dirname(s), "virtual-next-vite-router.d.ts"), $(
118
+ b(s, r),
119
+ b(s, e),
120
+ u
121
+ ), c();
99
122
  },
100
123
  configureServer(t) {
101
- if (d === t)
124
+ if (m === t)
102
125
  return;
103
- f == null || f(), d = t;
104
- const c = (P) => {
105
- if (l.relative(u, P).startsWith("..") || ![r, i, a].includes(l.basename(P)))
126
+ f == null || f(), m = t;
127
+ const h = (x) => {
128
+ if (l.relative(s, x).startsWith("..") || ![r, i, e].includes(l.basename(x)))
106
129
  return;
107
130
  try {
108
- b(F(u, r), o);
109
- } catch (x) {
131
+ $(
132
+ b(s, r),
133
+ b(s, e),
134
+ u
135
+ );
136
+ } catch (v) {
110
137
  t.config.logger.error(
111
- x instanceof Error ? x.message : String(x)
138
+ v instanceof Error ? v.message : String(v)
112
139
  );
113
140
  return;
114
141
  }
115
- g();
116
- const v = t.moduleGraph.getModuleById(s);
117
- v && (t.moduleGraph.invalidateModule(v), m && clearTimeout(m), m = setTimeout(() => {
118
- m = void 0, t.ws.send({ type: "full-reload", path: "*" });
142
+ c();
143
+ const F = t.moduleGraph.getModuleById(d);
144
+ F && (t.moduleGraph.invalidateModule(F), p && clearTimeout(p), p = setTimeout(() => {
145
+ p = void 0, t.ws.send({ type: "full-reload", path: "*" });
119
146
  }, 0));
120
147
  };
121
- let m;
122
- const R = () => {
123
- m && (clearTimeout(m), m = void 0), t.watcher.off("add", c), t.watcher.off("unlink", c), t.watcher.off("close", R), d === t && (d = void 0, f = void 0);
148
+ let p;
149
+ const g = () => {
150
+ p && (clearTimeout(p), p = void 0), t.watcher.off("add", h), t.watcher.off("unlink", h), t.watcher.off("close", g), m === t && (m = void 0, f = void 0);
124
151
  };
125
- t.watcher.on("add", c), t.watcher.on("unlink", c), t.watcher.once("close", R), f = R;
152
+ t.watcher.on("add", h), t.watcher.on("unlink", h), t.watcher.once("close", g), f = g;
126
153
  },
127
154
  resolveId(t) {
128
- if (t === p)
129
- return s;
155
+ if (t === y)
156
+ return d;
130
157
  },
131
158
  load(t) {
132
- if (t === s)
159
+ if (t === d) {
160
+ const h = `/${o}/**/${r}`, p = `/${o}/**/${i}`, g = `/${o}/**/${e}`, x = `/${o}/`;
133
161
  return `
134
162
  import { buildRouteTree, treeToRoutes, normalizeSegment, createLazyElement, useParams } from 'next-vite-router';
135
163
  import { Link, useRoutes } from 'react-router-dom';
136
164
 
137
- const pagesGlob = import.meta.glob('/${e}/**/${r}');
138
- const layoutsGlob = import.meta.glob('/${e}/**/${i}', { eager: true });
139
- const notFoundsGlob = import.meta.glob('/${e}/**/${a}');
165
+ const pagesGlob = import.meta.glob(${JSON.stringify(h)});
166
+ const layoutsGlob = import.meta.glob(${JSON.stringify(p)}, { eager: true });
167
+ const notFoundsGlob = import.meta.glob(${JSON.stringify(g)});
140
168
 
141
- const transformPath = (path) => path.replace('/${e}/', '');
169
+ const transformPath = (path) => path.replace(${JSON.stringify(x)}, '');
142
170
 
143
171
  const pages = Object.fromEntries(
144
172
  Object.entries(pagesGlob).map(([key, value]) => [transformPath(key), value])
@@ -151,7 +179,7 @@ const notFounds = Object.fromEntries(
151
179
  );
152
180
 
153
181
  export function generateRoutes() {
154
- const tree = buildRouteTree(pages, layouts, notFounds, ${JSON.stringify(o)});
182
+ const tree = buildRouteTree(pages, layouts, notFounds, ${JSON.stringify(u)});
155
183
  const routes = treeToRoutes(tree, true);
156
184
  return routes;
157
185
  }
@@ -172,9 +200,10 @@ if (import.meta.hot) {
172
200
  export { Link, useRoutes };
173
201
  export { useParams };
174
202
  `;
203
+ }
175
204
  }
176
205
  };
177
206
  }
178
207
  export {
179
- D as nextViteRouter
208
+ M as nextViteRouter
180
209
  };
@@ -0,0 +1,143 @@
1
+ function R(t) {
2
+ return /^\([^)]+\)$/.test(t) ? "" : /^\[\[\.\.\.(.+)\]\]$/.test(t) || /^\[\.\.\.(.+)\]$/.test(t) ? "*" : /^\[([^\]]+)\]$/.test(t) ? t.replace(/^\[([^\]]+)\]$/, ":$1") : t;
3
+ }
4
+ const P = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ function A(t) {
6
+ return t.kind === "group" ? "" : t.kind === "dynamic" ? `:${t.name}` : t.kind === "catchAll" ? "*" : t.name;
7
+ }
8
+ function m(t) {
9
+ const s = t ? t.split("/").filter(Boolean) : [], o = [], c = /* @__PURE__ */ new Set();
10
+ for (const a of s) {
11
+ const n = a.match(/^\(([^)]+)\)$/), e = a.match(/^\[\[\.\.\.(.+)\]\]$/), r = a.match(/^\[\.\.\.(.+)\]$/), i = a.match(/^\[([^\]]+)\]$/), l = e ? { name: e[1], optional: !0, kind: "catchAll" } : r ? { name: r[1], optional: !1, kind: "catchAll" } : i ? { name: i[1], optional: !1, kind: "dynamic" } : void 0;
12
+ if (n)
13
+ o.push({ kind: "group", name: n[1] });
14
+ else if (l) {
15
+ if (!P.test(l.name))
16
+ throw new Error(
17
+ `Invalid route parameter name "${l.name}" in segment "${a}". Parameter names must start with a letter or underscore and contain only letters, numbers, and underscores.`
18
+ );
19
+ if (c.has(l.name))
20
+ throw new Error(
21
+ `Duplicate route parameter "${l.name}" in route "${t || "/"}".`
22
+ );
23
+ c.add(l.name), o.push(l);
24
+ } else
25
+ o.push({ kind: "static", name: a });
26
+ }
27
+ return o;
28
+ }
29
+ function f(t, { pagesDir: s, pageFile: o = "page.tsx", layoutFile: c = "layout.tsx", notFoundFile: a = "not-found.tsx" } = {}) {
30
+ const n = t.replace(/\\/g, "/"), e = s == null ? void 0 : s.replace(/\\/g, "/").replace(/^\/|\/$/g, ""), r = e == null ? void 0 : e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), i = [o, c, a].map((l) => l.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
31
+ return n.replace(
32
+ e ? new RegExp(`^.*(?:^|/)${r}/`) : /^.*(?:^|\/)(?:app|pages)\//,
33
+ ""
34
+ ).replace(/\\/g, "/").replace(new RegExp(`/?(?:${i})$`), "");
35
+ }
36
+ function $(t) {
37
+ return t.kind === "group" ? "" : t.kind === "catchAll" ? "*" : t.kind === "dynamic" ? ":param" : A(t);
38
+ }
39
+ function g(t, s) {
40
+ const o = f(t, s), c = m(o), a = c.map($).filter(Boolean), n = [
41
+ a.length > 0 ? `/${a.join("/")}` : "/"
42
+ ], e = c[c.length - 1];
43
+ if ((e == null ? void 0 : e.kind) === "catchAll" && e.optional) {
44
+ const r = a.slice(0, -1);
45
+ n.push(r.length > 0 ? `/${r.join("/")}` : "/");
46
+ }
47
+ return n;
48
+ }
49
+ function w(t) {
50
+ return /(^|\/)(:param|\*)($|\/)/.test(t) ? "pattern" : "literal";
51
+ }
52
+ function j(t, s, o) {
53
+ const c = t.flatMap((n) => {
54
+ const e = f(n, o), r = m(e), i = r[r.length - 1];
55
+ if ((i == null ? void 0 : i.kind) !== "catchAll")
56
+ return [];
57
+ const l = r.map($).filter(Boolean), u = l.length > 0 ? `/${l.join("/")}` : "/", p = l.slice(0, -1), k = p.length > 0 ? `/${p.join("/")}` : "/";
58
+ return [{ filePath: n, paths: /* @__PURE__ */ new Set([u, k]) }];
59
+ }), a = /* @__PURE__ */ new Map();
60
+ for (const n of s)
61
+ for (const e of g(n, o))
62
+ for (const r of c)
63
+ if (r.paths.has(e)) {
64
+ const i = `${e}\0${r.filePath}\0${n}`;
65
+ a.set(i, {
66
+ routePath: e,
67
+ pageFile: r.filePath,
68
+ notFoundFile: n
69
+ });
70
+ }
71
+ if (a.size > 0)
72
+ throw new Error(
73
+ [...a.values()].sort((n, e) => n.routePath.localeCompare(e.routePath)).map(
74
+ ({ routePath: n, pageFile: e, notFoundFile: r }) => `Conflicting fallback routes "${n}" detected:
75
+ catch-all page: ${e}
76
+ not-found file: ${r}
77
+ A not-found route and a catch-all page cannot coexist at the same route level.`
78
+ ).join(`
79
+
80
+ `)
81
+ );
82
+ }
83
+ function d(t, s) {
84
+ const o = /* @__PURE__ */ new Map();
85
+ for (const a of t)
86
+ for (const n of g(a, s)) {
87
+ const e = o.get(n) ?? [];
88
+ e.push({ kind: w(n), filePath: a }), o.set(n, e);
89
+ }
90
+ const c = [...o.entries()].filter(([, a]) => a.length > 1).sort(([a], [n]) => a.localeCompare(n));
91
+ if (c.length > 0)
92
+ throw new Error(
93
+ c.map(
94
+ ([a, n]) => `${n[0].kind === "pattern" ? "Ambiguous route pattern" : "Duplicate route"} "${a}" detected:
95
+ ${n.sort((e, r) => e.filePath.localeCompare(r.filePath)).map((e) => ` ${e.filePath}`).join(`
96
+ `)}`
97
+ ).join(`
98
+
99
+ `)
100
+ );
101
+ }
102
+ function E(t, s, o) {
103
+ d(t, o), d(s, o), j(t, s, o);
104
+ }
105
+ function h(t, s, o, c, a) {
106
+ const n = f(s, a), e = n ? n.split("/") : [];
107
+ let r = t, i = "";
108
+ e.forEach((l, u) => {
109
+ i = i ? `${i}/${l}` : l, r.children.has(l) || r.children.set(l, {
110
+ segment: l,
111
+ fullPath: i,
112
+ children: /* @__PURE__ */ new Map()
113
+ }), r = r.children.get(l), u === e.length - 1 && (r[c] = o);
114
+ }), e.length === 0 && (t[c] = o);
115
+ }
116
+ function b(t, s, o, c) {
117
+ E(
118
+ Object.keys(t),
119
+ Object.keys(o),
120
+ c
121
+ );
122
+ const a = {
123
+ segment: "",
124
+ fullPath: "",
125
+ children: /* @__PURE__ */ new Map()
126
+ };
127
+ return Object.entries(s).forEach(([n, e]) => {
128
+ h(a, n, e.default, "layout", c);
129
+ }), Object.entries(t).forEach(([n, e]) => {
130
+ h(a, n, e, "page", c);
131
+ }), Object.entries(o).forEach(([n, e]) => {
132
+ h(a, n, e, "notFound", c);
133
+ }), a;
134
+ }
135
+ export {
136
+ d as a,
137
+ b,
138
+ E as c,
139
+ f as e,
140
+ R as n,
141
+ m as p,
142
+ A as r
143
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-vite-router",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Next.js-like file-based routing for React + Vite",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,64 +0,0 @@
1
- function p(t) {
2
- return /^\([^)]+\)$/.test(t) ? "" : /^\[\[\.\.\.(.+)\]\]$/.test(t) || /^\[\.\.\.(.+)\]$/.test(t) ? "*" : /^\[([^\]]+)\]$/.test(t) ? t.replace(/^\[([^\]]+)\]$/, ":$1") : t;
3
- }
4
- function h(t, { pagesDir: c, pageFile: a = "page.tsx", layoutFile: o = "layout.tsx", notFoundFile: r = "not-found.tsx" } = {}) {
5
- const n = t.replace(/\\/g, "/"), e = c == null ? void 0 : c.replace(/\\/g, "/").replace(/^\/|\/$/g, ""), l = [a, o, r].map((u) => u.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
6
- return n.replace(
7
- e ? new RegExp(`^.*(?:^|/)${e}/`) : /^.*(?:^|\/)(?:app|pages)\//,
8
- ""
9
- ).replace(/\\/g, "/").replace(new RegExp(`/?(?:${l})$`), "");
10
- }
11
- function $(t, c) {
12
- const a = h(t, c), o = a ? a.split("/").map(p).filter(Boolean) : [];
13
- return o.length > 0 ? `/${o.join("/")}` : "/";
14
- }
15
- function d(t, c) {
16
- const a = /* @__PURE__ */ new Map();
17
- for (const r of t) {
18
- const n = $(r, c), e = a.get(n) ?? [];
19
- e.push(r), a.set(n, e);
20
- }
21
- const o = [...a.entries()].filter(([, r]) => r.length > 1).sort(([r], [n]) => r.localeCompare(n));
22
- if (o.length > 0)
23
- throw new Error(
24
- o.map(
25
- ([r, n]) => `Duplicate route "${r}" detected:
26
- ${n.sort((e, l) => e.localeCompare(l)).map((e) => ` ${e}`).join(`
27
- `)}`
28
- ).join(`
29
-
30
- `)
31
- );
32
- }
33
- function i(t, c, a, o, r) {
34
- const n = h(c, r), e = n ? n.split("/") : [];
35
- let l = t, u = "";
36
- e.forEach((s, f) => {
37
- u = u ? `${u}/${s}` : s, l.children.has(s) || l.children.set(s, {
38
- segment: s,
39
- fullPath: u,
40
- children: /* @__PURE__ */ new Map()
41
- }), l = l.children.get(s), f === e.length - 1 && (l[o] = a);
42
- }), e.length === 0 && (t[o] = a);
43
- }
44
- function P(t, c, a, o) {
45
- d(Object.keys(t), o);
46
- const r = {
47
- segment: "",
48
- fullPath: "",
49
- children: /* @__PURE__ */ new Map()
50
- };
51
- return Object.entries(c).forEach(([n, e]) => {
52
- i(r, n, e.default, "layout", o);
53
- }), Object.entries(t).forEach(([n, e]) => {
54
- i(r, n, e, "page", o);
55
- }), Object.entries(a).forEach(([n, e]) => {
56
- i(r, n, e, "notFound", o);
57
- }), r;
58
- }
59
- export {
60
- d as a,
61
- P as b,
62
- h as e,
63
- p as n
64
- };