next-vite-router 1.2.4 → 1.3.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 +26 -9
- package/dist/cli.cjs +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +144 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +126 -121
- package/dist/plugin.cjs +63 -0
- package/dist/plugin.d.cts +12 -0
- package/dist/plugin.js +91 -99
- package/dist/{tree-DW5A1s6W.js → tree-DfnssFxU.js} +2 -2
- package/dist/tree-hc1dSMdb.cjs +10 -0
- package/package.json +20 -7
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ File-based routing for React + Vite.
|
|
|
4
4
|
|
|
5
5
|
Define routes with folders and let the package generate the
|
|
6
6
|
[React Router](https://reactrouter.com/) `RouteObject[]`. It does not replace
|
|
7
|
-
React Router, navigation, history,
|
|
7
|
+
React Router, navigation, history, or application state.
|
|
8
8
|
|
|
9
9
|
## Start here
|
|
10
10
|
|
|
@@ -31,6 +31,10 @@ export default defineConfig({
|
|
|
31
31
|
});
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
The plugin ships both ESM and CJS, so this works when `package.json` has no
|
|
35
|
+
`"type": "module"` (Vite 5 loads `vite.config.ts` with `require()` in that
|
|
36
|
+
case). You can also rename the file to `vite.config.mts` to force ESM.
|
|
37
|
+
|
|
34
38
|
Create your route tree:
|
|
35
39
|
|
|
36
40
|
```text
|
|
@@ -88,10 +92,12 @@ npm run dev
|
|
|
88
92
|
|
|
89
93
|
## Use React Router directly
|
|
90
94
|
|
|
91
|
-
`Router` is only a convenience wrapper around `useRoutes(routes)`.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
`Router` is only a convenience wrapper around `useRoutes(routes)`. The
|
|
96
|
+
lazy-loading behavior and `loadingFallback` configured through `configureRouter`
|
|
97
|
+
are read at render time, so they take effect even when `generateRoutes()` runs
|
|
98
|
+
at module scope before application setup finishes. Register middleware with
|
|
99
|
+
`registerMiddleware` *before* calling `generateRoutes()`, because route elements
|
|
100
|
+
are wrapped while the tree is built. You can keep using React Router APIs:
|
|
95
101
|
|
|
96
102
|
```tsx
|
|
97
103
|
import {
|
|
@@ -115,10 +121,18 @@ export default function App() {
|
|
|
115
121
|
}
|
|
116
122
|
```
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
`
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
Keep using React Router's navigation and hooks from `react-router-dom`
|
|
125
|
+
(`useNavigate`, `useLocation`, `Link`, `Outlet`). For parameters, import
|
|
126
|
+
`useParams` from the virtual module instead of `react-router-dom`: dynamic
|
|
127
|
+
`[id]` segments work with either hook, but named catch-all segments
|
|
128
|
+
(`[...slug]`, `[[...slug]]`) are only surfaced as `slug` through the package
|
|
129
|
+
`useParams`. React Router's own `useParams` returns them under `params["*"]`.
|
|
130
|
+
|
|
131
|
+
The generated `RouteObject[]` carries page, layout, and not-found elements only.
|
|
132
|
+
There is no file convention that produces React Router route `loader` or
|
|
133
|
+
`action` functions, and those data APIs run only under a data router
|
|
134
|
+
(`createBrowserRouter` + `RouterProvider`), not under `BrowserRouter` with
|
|
135
|
+
`useRoutes`. Fetch data inside your page components instead.
|
|
122
136
|
|
|
123
137
|
## File conventions
|
|
124
138
|
|
|
@@ -210,6 +224,9 @@ configureRouter({
|
|
|
210
224
|
});
|
|
211
225
|
```
|
|
212
226
|
|
|
227
|
+
The fallback is resolved when a route suspends during render, so the order of
|
|
228
|
+
`configureRouter` relative to `generateRoutes()` does not matter.
|
|
229
|
+
|
|
213
230
|
`enableLazyLoading: false` is available for compatibility and testing. It does
|
|
214
231
|
not turn Vite's asynchronous imports into synchronous imports.
|
|
215
232
|
|
package/dist/cli.cjs
CHANGED
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("./tree-hc1dSMdb.cjs"),l=require("react"),S=require("react-router-dom"),C={enableLazyLoading:!0};let f={...C};function v(e){f={...f,...e}}function F(){f={...C}}function T(){return f}let g=[];function x(e,t){g.push({pattern:e,middleware:t})}function A(){g=[]}function y(e,t){let r=t;for(const n of g)n.pattern.lastIndex=0,n.pattern.test(e)&&(r=n.middleware(r)),n.pattern.lastIndex=0;return r}const M=()=>l.createElement("div"),h=new Map;function N(){const e=T();if(e.enableLazyLoading===!1)return null;const t=e.loadingFallback||M;return l.createElement(t)}function $(){h.clear()}function p(e,t,r){let n=h.get(t);n||(n=l.lazy(e),h.set(t,n));const a=l.createElement(l.Suspense,{fallback:l.createElement(N)},l.createElement(n));return r?y(r,a):a}const P=l.createContext(void 0);function w({name:e,children:t}){return l.createElement(P.Provider,{value:e},t)}function k(){return l.useContext(P)}function E(e,t){return y(e,l.createElement(t))}function D(e){const t=c.parseRouteSegments(e)[0];return(t==null?void 0:t.kind)==="catchAll"&&t.optional}function d(e){const t=c.parseRouteSegments(e)[0];return t?c.routeSegmentToPath(t):""}function s(e,t){return m(e,t,!0)}function m(e,t,r=!1){const n=e.page;if(!n)throw new Error(`Route node "${e.fullPath}" has no page loader`);const a=p(async()=>({default:(await n()).default}),`page:${e.fullPath||t}`,t),o=c.parseRouteSegments(e.segment)[0],u=(o==null?void 0:o.kind)==="catchAll"?l.createElement(w,{name:o.name,children:a}):a;return r?{index:!0,element:u}:{path:d(e.segment),element:u}}function q(e){const t=e.notFound;if(!t)throw new Error(`Route node "${e.fullPath}" has no not-found loader`);const r=e.fullPath?`/${e.fullPath}`:"/";return{path:"*",element:p(async()=>({default:(await t()).default}),`notfound:${e.fullPath}`,r)}}function L(e){return e.notFound?[q(e)]:[]}function I(e,t,r,n){return{...t?{path:t}:{},element:E(e,r),children:n.length>0?n:void 0}}function b(e,t){const r=[];return[...e.children.entries()].sort(([n],[a])=>n.localeCompare(a)).forEach(([n,a])=>{const o=d(n),u=o?t?`${t}/${o}`:`/${o}`:t;r.push(...R(a,!1,u))}),r}function z(e,t){const r=[];return e.page&&r.push(s(e,t||"/")),r.push(...b(e,t)),r.push(...L(e)),r}function G(e,t,r,n,a,o){if(D(e.segment)){const u=[...a,...o];return[s(e,t),{path:n,element:m(e,r).element,children:u.length>0?u:void 0}]}return a.length>0||o.length>0?[{path:n,children:[s(e,r),...a,...o]}]:[{path:n,element:m(e,r).element}]}function O(e,t,r,n){const a=[...e.page?[s(e,t)]:[],...r,...n];return[{children:a.length>0?a:void 0}]}function j(e){const t=z(e,"");return e.layout?[{path:"/",element:E("/",e.layout),children:t}]:e.page?t.length>1?[{path:"/",children:t}]:[{path:"/",element:m(e,"/",!1).element}]:e.notFound?[{path:"/",children:t}]:t}function R(e,t=!1,r=""){if(t)return j(e);const n=d(e.segment),a=!n,o=a?r:r+"/"+n,u=b(e,o),i=L(e);return e.layout?[I(o,a?"":n,e.layout,z(e,o))]:e.page&&!a?G(e,r,o,n,u,i):e.page||e.notFound||!a?a?O(e,o,u,i):[{path:n,children:[...e.page?[s(e,o)]:[],...u,...i]}]:u}function B(e,t,r,n){return()=>{const a=c.buildRouteTree(e,t,r,n);return R(a,!0)}}function H(){const e=S.useParams(),t=k();return typeof t=="string"&&e["*"]!==void 0?{...e,[t]:e["*"]}:e}exports.assertNoDuplicateRoutes=c.assertNoDuplicateRoutes;exports.assertNoRouteConflicts=c.assertNoRouteConflicts;exports.buildRouteTree=c.buildRouteTree;exports.extractRoutePath=c.extractRoutePath;exports.normalizeSegment=c.normalizeSegment;exports.parseRouteSegments=c.parseRouteSegments;exports.routeSegmentToPath=c.routeSegmentToPath;exports.CatchAllParamProvider=w;exports.clearLazyCache=$;exports.clearMiddlewares=A;exports.configureRouter=v;exports.createLazyElement=p;exports.createRouteGenerator=B;exports.registerMiddleware=x;exports.resetRouterConfig=F;exports.treeToRoutes=R;exports.useParams=H;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { default as default_2 } from 'react';
|
|
3
|
+
import { ReactElement } from 'react';
|
|
4
|
+
import { RouteObject } from 'react-router-dom';
|
|
5
|
+
|
|
6
|
+
export declare function assertNoDuplicateRoutes(filePaths: string[], fileOptions?: RouteFileOptions): void;
|
|
7
|
+
|
|
8
|
+
export declare function assertNoRouteConflicts(pageFiles: string[], notFoundFiles: string[], fileOptions?: RouteFileOptions): void;
|
|
9
|
+
|
|
10
|
+
export declare function buildRouteTree(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any>>, fileOptions?: RouteFileOptions): RouteNode;
|
|
11
|
+
|
|
12
|
+
export declare function CatchAllParamProvider({ name, children, }: {
|
|
13
|
+
name: string;
|
|
14
|
+
children: default_2.ReactElement;
|
|
15
|
+
}): default_2.ReactElement;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Clear the module-level lazy component cache.
|
|
19
|
+
*
|
|
20
|
+
* `lazyComponentCache` is a package-wide singleton keyed by route path, so a
|
|
21
|
+
* `React.lazy` component created in one context (for example a previous test)
|
|
22
|
+
* is reused whenever another context requests the same key with a different
|
|
23
|
+
* loader. Exposing an explicit reset keeps that state from leaking, mirroring
|
|
24
|
+
* `clearMiddlewares` for the middleware registry.
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearLazyCache(): void;
|
|
27
|
+
|
|
28
|
+
export declare function clearMiddlewares(): void;
|
|
29
|
+
|
|
30
|
+
export declare function configureRouter(config: RouterConfig): void;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a lazy element wrapped with Suspense and apply middlewares
|
|
34
|
+
* @param loader - Function that loads the component
|
|
35
|
+
* @param cacheKey - Unique key for the cache
|
|
36
|
+
* @param routePath - Route path for middlewares
|
|
37
|
+
*/
|
|
38
|
+
export declare function createLazyElement(loader: () => Promise<any>, cacheKey: string, routePath?: string): default_2.ReactElement;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a custom route generator function using your own files
|
|
42
|
+
*
|
|
43
|
+
* ⚠️ NOTE: This function is intended for advanced use only.
|
|
44
|
+
* For most cases, use the Vite plugin which automatically handles the paths.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // In your configuration file
|
|
48
|
+
* const pages = import.meta.glob("../../pages/**\\/page.tsx");
|
|
49
|
+
* const layouts = import.meta.glob("../../pages/**\\/layout.tsx", { eager: true });
|
|
50
|
+
* const notFounds = import.meta.glob("../../pages/**\\/not-found.tsx");
|
|
51
|
+
*
|
|
52
|
+
* export const generateCustomRoutes = createRouteGenerator(pages, layouts, notFounds);
|
|
53
|
+
*
|
|
54
|
+
* // Then use it
|
|
55
|
+
* const routes = generateCustomRoutes();
|
|
56
|
+
*/
|
|
57
|
+
export declare function createRouteGenerator(pages: Record<string, () => Promise<any>>, layouts: Record<string, any>, notFounds: Record<string, () => Promise<any>>, fileOptions?: RouteFileOptions): () => RouteObject[];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract the route path from a file path
|
|
61
|
+
* @example "../../app/blog/[id]/page.tsx" -> "blog/[id]"
|
|
62
|
+
* @example "../../app/page.tsx" -> ""
|
|
63
|
+
*
|
|
64
|
+
* `pagesDir` may be relative or absolute. The plugin resolves it relative to
|
|
65
|
+
* the Vite project root before passing it here.
|
|
66
|
+
* The prefix is stripped lazily so it matches the FIRST directory boundary
|
|
67
|
+
* that equals `pagesDir` (or the leading `app`/`pages` root marker when no
|
|
68
|
+
* `pagesDir` is supplied). A greedy match would instead consume up to the
|
|
69
|
+
* LAST such folder, which mangles routes that legitimately contain a nested
|
|
70
|
+
* folder named like the pages directory (e.g. `app/docs/app/page.tsx`).
|
|
71
|
+
*/
|
|
72
|
+
export declare function extractRoutePath(filePath: string, { pagesDir, pageFile, layoutFile, notFoundFile }?: RouteFileOptions): string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Normalize a Next.js segment to a React Router segment
|
|
76
|
+
* @example (admin) -> ""
|
|
77
|
+
* @example [id] -> :id
|
|
78
|
+
* @example [...slug] -> *
|
|
79
|
+
*/
|
|
80
|
+
export declare function normalizeSegment(segment: string): string;
|
|
81
|
+
|
|
82
|
+
export declare function parseRouteSegments(routePath: string): RouteSegment[];
|
|
83
|
+
|
|
84
|
+
export declare function registerMiddleware(pattern: RegExp, middleware: RouteMiddleware): void;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Restore the module-level router configuration to its defaults.
|
|
88
|
+
*
|
|
89
|
+
* `configureRouter` merges, so it can never drop keys back to their initial
|
|
90
|
+
* state. A dedicated reset lets isolated contexts (for example tests) start
|
|
91
|
+
* from a known configuration instead of inheriting whatever was set globally
|
|
92
|
+
* before, matching the explicit reset offered by `clearMiddlewares`.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resetRouterConfig(): void;
|
|
95
|
+
|
|
96
|
+
export declare type RouteFileOptions = {
|
|
97
|
+
pagesDir?: string;
|
|
98
|
+
pageFile?: string;
|
|
99
|
+
layoutFile?: string;
|
|
100
|
+
notFoundFile?: string;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export declare type RouteMiddleware = (element: ReactElement) => ReactElement;
|
|
104
|
+
|
|
105
|
+
export declare type RouteModuleLoader = () => Promise<{
|
|
106
|
+
default: ComponentType;
|
|
107
|
+
}>;
|
|
108
|
+
|
|
109
|
+
export declare type RouteNode = {
|
|
110
|
+
segment: string;
|
|
111
|
+
fullPath: string;
|
|
112
|
+
page?: RouteModuleLoader;
|
|
113
|
+
layout?: ComponentType;
|
|
114
|
+
notFound?: RouteModuleLoader;
|
|
115
|
+
children: Map<string, RouteNode>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export declare type RouterConfig = {
|
|
119
|
+
loadingFallback?: React.ComponentType;
|
|
120
|
+
enableLazyLoading?: boolean;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export declare type RouteSegment = {
|
|
124
|
+
kind: "group";
|
|
125
|
+
name: string;
|
|
126
|
+
} | {
|
|
127
|
+
kind: "static";
|
|
128
|
+
name: string;
|
|
129
|
+
} | {
|
|
130
|
+
kind: "dynamic";
|
|
131
|
+
name: string;
|
|
132
|
+
} | {
|
|
133
|
+
kind: "catchAll";
|
|
134
|
+
name: string;
|
|
135
|
+
optional: boolean;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export declare function routeSegmentToPath(segment: RouteSegment): string;
|
|
139
|
+
|
|
140
|
+
export declare function treeToRoutes(node: RouteNode, isRoot?: boolean, currentPath?: string): RouteObject[];
|
|
141
|
+
|
|
142
|
+
export declare function useParams<T extends Record<string, string | undefined> = Record<string, string | undefined>>(): T;
|
|
143
|
+
|
|
144
|
+
export { }
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,17 @@ export declare function CatchAllParamProvider({ name, children, }: {
|
|
|
14
14
|
children: default_2.ReactElement;
|
|
15
15
|
}): default_2.ReactElement;
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Clear the module-level lazy component cache.
|
|
19
|
+
*
|
|
20
|
+
* `lazyComponentCache` is a package-wide singleton keyed by route path, so a
|
|
21
|
+
* `React.lazy` component created in one context (for example a previous test)
|
|
22
|
+
* is reused whenever another context requests the same key with a different
|
|
23
|
+
* loader. Exposing an explicit reset keeps that state from leaking, mirroring
|
|
24
|
+
* `clearMiddlewares` for the middleware registry.
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearLazyCache(): void;
|
|
27
|
+
|
|
17
28
|
export declare function clearMiddlewares(): void;
|
|
18
29
|
|
|
19
30
|
export declare function configureRouter(config: RouterConfig): void;
|
|
@@ -52,6 +63,11 @@ export declare function createRouteGenerator(pages: Record<string, () => Promise
|
|
|
52
63
|
*
|
|
53
64
|
* `pagesDir` may be relative or absolute. The plugin resolves it relative to
|
|
54
65
|
* the Vite project root before passing it here.
|
|
66
|
+
* The prefix is stripped lazily so it matches the FIRST directory boundary
|
|
67
|
+
* that equals `pagesDir` (or the leading `app`/`pages` root marker when no
|
|
68
|
+
* `pagesDir` is supplied). A greedy match would instead consume up to the
|
|
69
|
+
* LAST such folder, which mangles routes that legitimately contain a nested
|
|
70
|
+
* folder named like the pages directory (e.g. `app/docs/app/page.tsx`).
|
|
55
71
|
*/
|
|
56
72
|
export declare function extractRoutePath(filePath: string, { pagesDir, pageFile, layoutFile, notFoundFile }?: RouteFileOptions): string;
|
|
57
73
|
|
|
@@ -67,6 +83,16 @@ export declare function parseRouteSegments(routePath: string): RouteSegment[];
|
|
|
67
83
|
|
|
68
84
|
export declare function registerMiddleware(pattern: RegExp, middleware: RouteMiddleware): void;
|
|
69
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Restore the module-level router configuration to its defaults.
|
|
88
|
+
*
|
|
89
|
+
* `configureRouter` merges, so it can never drop keys back to their initial
|
|
90
|
+
* state. A dedicated reset lets isolated contexts (for example tests) start
|
|
91
|
+
* from a known configuration instead of inheriting whatever was set globally
|
|
92
|
+
* before, matching the explicit reset offered by `clearMiddlewares`.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resetRouterConfig(): void;
|
|
95
|
+
|
|
70
96
|
export declare type RouteFileOptions = {
|
|
71
97
|
pagesDir?: string;
|
|
72
98
|
pageFile?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,157 +1,160 @@
|
|
|
1
|
-
import { p, r as
|
|
2
|
-
import { a as
|
|
3
|
-
import u, {
|
|
1
|
+
import { p as h, r as F, b } from "./tree-DfnssFxU.js";
|
|
2
|
+
import { a as et, c as nt, e as rt, n as at } from "./tree-DfnssFxU.js";
|
|
3
|
+
import u, { lazy as v, Suspense as z, createContext as $, useContext as A } from "react";
|
|
4
4
|
import { useParams as k } from "react-router-dom";
|
|
5
|
-
const
|
|
5
|
+
const d = {
|
|
6
6
|
enableLazyLoading: !0
|
|
7
7
|
};
|
|
8
|
-
let
|
|
8
|
+
let i = { ...d };
|
|
9
9
|
function U(t) {
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function W() {
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
10
|
+
i = { ...i, ...t };
|
|
11
|
+
}
|
|
12
|
+
function V() {
|
|
13
|
+
i = { ...d };
|
|
14
|
+
}
|
|
15
|
+
function N() {
|
|
16
|
+
return i;
|
|
17
|
+
}
|
|
18
|
+
let p = [];
|
|
19
|
+
function W(t, e) {
|
|
20
|
+
p.push({ pattern: t, middleware: e });
|
|
21
|
+
}
|
|
22
|
+
function X() {
|
|
23
|
+
p = [];
|
|
24
|
+
}
|
|
25
|
+
function R(t, e) {
|
|
26
|
+
let r = e;
|
|
27
|
+
for (const n of p)
|
|
28
|
+
n.pattern.lastIndex = 0, n.pattern.test(t) && (r = n.middleware(r)), n.pattern.lastIndex = 0;
|
|
29
|
+
return r;
|
|
30
|
+
}
|
|
31
|
+
const M = () => u.createElement("div"), m = /* @__PURE__ */ new Map();
|
|
32
|
+
function T() {
|
|
33
|
+
const t = N();
|
|
34
|
+
if (t.enableLazyLoading === !1)
|
|
35
|
+
return null;
|
|
36
|
+
const e = t.loadingFallback || M;
|
|
37
|
+
return u.createElement(e);
|
|
38
|
+
}
|
|
39
|
+
function Y() {
|
|
40
|
+
m.clear();
|
|
41
|
+
}
|
|
42
|
+
function C(t, e, r) {
|
|
43
|
+
let n = m.get(e);
|
|
44
|
+
n || (n = v(t), m.set(e, n));
|
|
45
|
+
const a = u.createElement(
|
|
46
|
+
z,
|
|
47
|
+
{ fallback: u.createElement(T) },
|
|
48
|
+
u.createElement(n)
|
|
46
49
|
);
|
|
47
|
-
return
|
|
50
|
+
return r ? R(r, a) : a;
|
|
48
51
|
}
|
|
49
|
-
const
|
|
50
|
-
function
|
|
52
|
+
const y = $(void 0);
|
|
53
|
+
function I({
|
|
51
54
|
name: t,
|
|
52
55
|
children: e
|
|
53
56
|
}) {
|
|
54
57
|
return u.createElement(
|
|
55
|
-
|
|
58
|
+
y.Provider,
|
|
56
59
|
{ value: t },
|
|
57
60
|
e
|
|
58
61
|
);
|
|
59
62
|
}
|
|
60
|
-
function
|
|
61
|
-
return A(
|
|
63
|
+
function S() {
|
|
64
|
+
return A(y);
|
|
62
65
|
}
|
|
63
|
-
function
|
|
64
|
-
return
|
|
66
|
+
function P(t, e) {
|
|
67
|
+
return R(t, u.createElement(e));
|
|
65
68
|
}
|
|
66
|
-
function
|
|
67
|
-
const e =
|
|
69
|
+
function D(t) {
|
|
70
|
+
const e = h(t)[0];
|
|
68
71
|
return (e == null ? void 0 : e.kind) === "catchAll" && e.optional;
|
|
69
72
|
}
|
|
70
73
|
function g(t) {
|
|
71
|
-
const e =
|
|
72
|
-
return e ?
|
|
74
|
+
const e = h(t)[0];
|
|
75
|
+
return e ? F(e) : "";
|
|
73
76
|
}
|
|
74
|
-
function
|
|
75
|
-
return
|
|
77
|
+
function c(t, e) {
|
|
78
|
+
return f(t, e, !0);
|
|
76
79
|
}
|
|
77
|
-
function
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
80
|
+
function f(t, e, r = !1) {
|
|
81
|
+
const n = t.page;
|
|
82
|
+
if (!n)
|
|
80
83
|
throw new Error(`Route node "${t.fullPath}" has no page loader`);
|
|
81
|
-
const a =
|
|
82
|
-
async () => ({ default: (await
|
|
84
|
+
const a = C(
|
|
85
|
+
async () => ({ default: (await n()).default }),
|
|
83
86
|
`page:${t.fullPath || e}`,
|
|
84
87
|
e
|
|
85
|
-
), o =
|
|
88
|
+
), o = h(t.segment)[0], l = (o == null ? void 0 : o.kind) === "catchAll" ? u.createElement(I, {
|
|
86
89
|
name: o.name,
|
|
87
90
|
children: a
|
|
88
91
|
}) : a;
|
|
89
|
-
return
|
|
92
|
+
return r ? { index: !0, element: l } : { path: g(t.segment), element: l };
|
|
90
93
|
}
|
|
91
94
|
function G(t) {
|
|
92
95
|
const e = t.notFound;
|
|
93
96
|
if (!e)
|
|
94
97
|
throw new Error(`Route node "${t.fullPath}" has no not-found loader`);
|
|
95
|
-
const
|
|
98
|
+
const r = t.fullPath ? `/${t.fullPath}` : "/";
|
|
96
99
|
return {
|
|
97
100
|
path: "*",
|
|
98
|
-
element:
|
|
101
|
+
element: C(
|
|
99
102
|
async () => ({ default: (await e()).default }),
|
|
100
103
|
`notfound:${t.fullPath}`,
|
|
101
|
-
|
|
104
|
+
r
|
|
102
105
|
)
|
|
103
106
|
};
|
|
104
107
|
}
|
|
105
|
-
function
|
|
108
|
+
function E(t) {
|
|
106
109
|
return t.notFound ? [G(t)] : [];
|
|
107
110
|
}
|
|
108
|
-
function O(t, e,
|
|
111
|
+
function O(t, e, r, n) {
|
|
109
112
|
return {
|
|
110
113
|
...e ? { path: e } : {},
|
|
111
|
-
element:
|
|
112
|
-
children:
|
|
114
|
+
element: P(t, r),
|
|
115
|
+
children: n.length > 0 ? n : void 0
|
|
113
116
|
};
|
|
114
117
|
}
|
|
115
|
-
function
|
|
116
|
-
const
|
|
117
|
-
return [...t.children.entries()].sort(([
|
|
118
|
-
const o = g(
|
|
119
|
-
|
|
120
|
-
}),
|
|
118
|
+
function w(t, e) {
|
|
119
|
+
const r = [];
|
|
120
|
+
return [...t.children.entries()].sort(([n], [a]) => n.localeCompare(a)).forEach(([n, a]) => {
|
|
121
|
+
const o = g(n), l = o ? e ? `${e}/${o}` : `/${o}` : e;
|
|
122
|
+
r.push(...x(a, !1, l));
|
|
123
|
+
}), r;
|
|
121
124
|
}
|
|
122
125
|
function L(t, e) {
|
|
123
|
-
const
|
|
124
|
-
return t.page &&
|
|
126
|
+
const r = [];
|
|
127
|
+
return t.page && r.push(c(t, e || "/")), r.push(...w(t, e)), r.push(...E(t)), r;
|
|
125
128
|
}
|
|
126
|
-
function j(t, e,
|
|
127
|
-
if (
|
|
129
|
+
function j(t, e, r, n, a, o) {
|
|
130
|
+
if (D(t.segment)) {
|
|
128
131
|
const l = [...a, ...o];
|
|
129
132
|
return [
|
|
130
|
-
|
|
133
|
+
c(t, e),
|
|
131
134
|
{
|
|
132
|
-
path:
|
|
133
|
-
element:
|
|
135
|
+
path: n,
|
|
136
|
+
element: f(t, r).element,
|
|
134
137
|
children: l.length > 0 ? l : void 0
|
|
135
138
|
}
|
|
136
139
|
];
|
|
137
140
|
}
|
|
138
141
|
return a.length > 0 || o.length > 0 ? [{
|
|
139
|
-
path:
|
|
142
|
+
path: n,
|
|
140
143
|
children: [
|
|
141
|
-
|
|
144
|
+
c(t, r),
|
|
142
145
|
...a,
|
|
143
146
|
...o
|
|
144
147
|
]
|
|
145
148
|
}] : [{
|
|
146
|
-
path:
|
|
147
|
-
element:
|
|
149
|
+
path: n,
|
|
150
|
+
element: f(t, r).element
|
|
148
151
|
}];
|
|
149
152
|
}
|
|
150
|
-
function q(t, e,
|
|
153
|
+
function q(t, e, r, n) {
|
|
151
154
|
const a = [
|
|
152
|
-
...t.page ? [
|
|
153
|
-
...
|
|
154
|
-
...
|
|
155
|
+
...t.page ? [c(t, e)] : [],
|
|
156
|
+
...r,
|
|
157
|
+
...n
|
|
155
158
|
];
|
|
156
159
|
return [{
|
|
157
160
|
children: a.length > 0 ? a : void 0
|
|
@@ -161,62 +164,64 @@ function B(t) {
|
|
|
161
164
|
const e = L(t, "");
|
|
162
165
|
return t.layout ? [{
|
|
163
166
|
path: "/",
|
|
164
|
-
element:
|
|
167
|
+
element: P("/", t.layout),
|
|
165
168
|
children: e
|
|
166
169
|
}] : t.page ? e.length > 1 ? [{ path: "/", children: e }] : [{
|
|
167
170
|
path: "/",
|
|
168
|
-
element:
|
|
171
|
+
element: f(t, "/", !1).element
|
|
169
172
|
}] : t.notFound ? [{ path: "/", children: e }] : e;
|
|
170
173
|
}
|
|
171
|
-
function
|
|
174
|
+
function x(t, e = !1, r = "") {
|
|
172
175
|
if (e)
|
|
173
176
|
return B(t);
|
|
174
|
-
const
|
|
177
|
+
const n = g(t.segment), a = !n, o = a ? r : r + "/" + n, l = w(t, o), s = E(t);
|
|
175
178
|
return t.layout ? [O(
|
|
176
179
|
o,
|
|
177
|
-
a ? "" :
|
|
180
|
+
a ? "" : n,
|
|
178
181
|
t.layout,
|
|
179
182
|
L(t, o)
|
|
180
183
|
)] : t.page && !a ? j(
|
|
181
184
|
t,
|
|
182
|
-
n,
|
|
183
|
-
o,
|
|
184
185
|
r,
|
|
186
|
+
o,
|
|
187
|
+
n,
|
|
185
188
|
l,
|
|
186
|
-
|
|
187
|
-
) : t.page || t.notFound || !a ? a ? q(t, o, l,
|
|
188
|
-
path:
|
|
189
|
+
s
|
|
190
|
+
) : t.page || t.notFound || !a ? a ? q(t, o, l, s) : [{
|
|
191
|
+
path: n,
|
|
189
192
|
children: [
|
|
190
|
-
...t.page ? [
|
|
193
|
+
...t.page ? [c(t, o)] : [],
|
|
191
194
|
...l,
|
|
192
|
-
...
|
|
195
|
+
...s
|
|
193
196
|
]
|
|
194
197
|
}] : l;
|
|
195
198
|
}
|
|
196
|
-
function
|
|
199
|
+
function Z(t, e, r, n) {
|
|
197
200
|
return () => {
|
|
198
|
-
const a =
|
|
199
|
-
return
|
|
201
|
+
const a = b(t, e, r, n);
|
|
202
|
+
return x(a, !0);
|
|
200
203
|
};
|
|
201
204
|
}
|
|
202
|
-
function
|
|
203
|
-
const t = k(), e =
|
|
205
|
+
function _() {
|
|
206
|
+
const t = k(), e = S();
|
|
204
207
|
return typeof e == "string" && t["*"] !== void 0 ? { ...t, [e]: t["*"] } : t;
|
|
205
208
|
}
|
|
206
209
|
export {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
I as CatchAllParamProvider,
|
|
211
|
+
et as assertNoDuplicateRoutes,
|
|
212
|
+
nt as assertNoRouteConflicts,
|
|
213
|
+
b as buildRouteTree,
|
|
214
|
+
Y as clearLazyCache,
|
|
215
|
+
X as clearMiddlewares,
|
|
212
216
|
U as configureRouter,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
217
|
+
C as createLazyElement,
|
|
218
|
+
Z as createRouteGenerator,
|
|
219
|
+
rt as extractRoutePath,
|
|
220
|
+
at as normalizeSegment,
|
|
221
|
+
h as parseRouteSegments,
|
|
222
|
+
W as registerMiddleware,
|
|
223
|
+
V as resetRouterConfig,
|
|
224
|
+
F as routeSegmentToPath,
|
|
225
|
+
x as treeToRoutes,
|
|
226
|
+
_ as useParams
|
|
222
227
|
};
|
package/dist/plugin.cjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("node:fs"),m=require("node:path"),x=require("./tree-hc1dSMdb.cjs");function w(n){const a=n.map(x.routeSegmentToPath).filter(Boolean);return a.length>0?`/${a.join("/")}`:"/"}function S(n){const a=[["pagesDir",n.pagesDir],["pageFile",n.pageFile],["layoutFile",n.layoutFile],["notFoundFile",n.notFoundFile]];for(const[t,e]of a)if(!e.trim())throw new Error(`nextViteRouter option "${t}" must not be empty.`);const s=a.slice(1),i=s.find(([,t],e)=>s.slice(e+1).some(([,r])=>r===t));if(i)throw new Error(`nextViteRouter file options must be distinct; "${i[0]}" is duplicated.`)}function T(n){return n.replace(/\\/g,"/").replace(/^\.\//,"").replace(/^\/|\/$/g,"")}function k(n,a,s){const i=a.pageFile??"page.tsx",t=new Map;for(const e of R(n,i,s)){const r=x.extractRoutePath(e,a),l=x.parseRouteSegments(r),d=l.filter(u=>u.kind==="dynamic"||u.kind==="catchAll").map(u=>({name:u.name,optional:u.kind==="catchAll"&&u.optional})),c=w(l);t.set(c,{path:c,params:d});const f=l[l.length-1];if((f==null?void 0:f.kind)==="catchAll"&&f.optional){const u=w(l.slice(0,-1));t.has(u)||t.set(u,{path:u,params:[]})}}return[...t.values()].sort((e,r)=>e.path.localeCompare(r.path))}function R(n,a,s){const i=[];function t(e){for(const r of b.readdirSync(e,{withFileTypes:!0})){const l=m.join(e,r.name);if(r.isDirectory())t(l);else if(r.isFile()&&r.name===a){const d=m.relative(n,l).replace(/\\/g,"/");i.push(`/${s}/${d}`)}}}return b.existsSync(n)&&t(n),i}function G(n){const a=n.map(({path:i})=>` | ${JSON.stringify(i)}`).join(`
|
|
2
|
+
`),s=n.map(({path:i,params:t})=>{const e=t.length>0?[...t.map(({name:r,optional:l})=>` ${r}${l?"?":""}: string;`)].join(`
|
|
3
|
+
`):" [key: string]: never;";return` ${JSON.stringify(i)}: {
|
|
4
|
+
${e}
|
|
5
|
+
};`}).join(`
|
|
6
|
+
`);return`declare module 'virtual:next-vite-router' {
|
|
7
|
+
import type { ReactElement } from 'react';
|
|
8
|
+
import type { RouteObject } from 'react-router-dom';
|
|
9
|
+
|
|
10
|
+
export type RoutePath =
|
|
11
|
+
${a||' | "/"'};
|
|
12
|
+
|
|
13
|
+
export type RouteParams<P extends RoutePath> = P extends keyof RoutePathParams
|
|
14
|
+
? RoutePathParams[P]
|
|
15
|
+
: never;
|
|
16
|
+
|
|
17
|
+
export interface RoutePathParams {
|
|
18
|
+
${s}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const generateRoutes: () => RouteObject[];
|
|
22
|
+
export function Router(): ReactElement | null;
|
|
23
|
+
export function useParams<T extends Record<string, string | undefined> = Record<string, string | undefined>>(): T;
|
|
24
|
+
export { Link, useRoutes } from 'react-router-dom';
|
|
25
|
+
}
|
|
26
|
+
`}function O(n={}){const{pagesDir:a="src/app",pageFile:s="page.tsx",layoutFile:i="layout.tsx",notFoundFile:t="not-found.tsx"}=n;S({pagesDir:a,pageFile:s,layoutFile:i,notFoundFile:t});const e=T(a),r={pagesDir:e,pageFile:s,layoutFile:i,notFoundFile:t},l="virtual:next-vite-router",d="\0"+l;let c="",f="",u,h;function F(){const o=G(k(c,r,e));b.writeFileSync(f,o,"utf-8")}return{name:"next-vite-router",configResolved(o){c=m.resolve(o.root,a),f=m.join(m.dirname(c),"virtual-next-vite-router.d.ts"),x.assertNoRouteConflicts(R(c,s,e),R(c,t,e),r),F()},configureServer(o){if(u===o)return;h==null||h(),u=o;const g=$=>{if(m.relative(c,$).startsWith("..")||![s,i,t].includes(m.basename($)))return;try{x.assertNoRouteConflicts(R(c,s,e),R(c,t,e),r)}catch(P){o.config.logger.error(P instanceof Error?P.message:String(P));return}F();const v=o.moduleGraph.getModuleById(d);v&&(o.moduleGraph.invalidateModule(v),p&&clearTimeout(p),p=setTimeout(()=>{p=void 0,o.ws.send({type:"full-reload",path:"*"})},0))};let p;const y=()=>{p&&(clearTimeout(p),p=void 0),o.watcher.off("add",g),o.watcher.off("unlink",g),o.watcher.off("close",y),u===o&&(u=void 0,h=void 0)};o.watcher.on("add",g),o.watcher.on("unlink",g),o.watcher.once("close",y),h=y},resolveId(o){if(o===l)return d},load(o){if(o===d){const g=`/${e}/**/${s}`,p=`/${e}/**/${i}`,y=`/${e}/**/${t}`;return`
|
|
27
|
+
import { buildRouteTree, treeToRoutes, normalizeSegment, createLazyElement, useParams } from 'next-vite-router';
|
|
28
|
+
import { Link, useRoutes } from 'react-router-dom';
|
|
29
|
+
|
|
30
|
+
const pagesGlob = import.meta.glob(${JSON.stringify(g)});
|
|
31
|
+
const layoutsGlob = import.meta.glob(${JSON.stringify(p)}, { eager: true });
|
|
32
|
+
const notFoundsGlob = import.meta.glob(${JSON.stringify(y)});
|
|
33
|
+
|
|
34
|
+
// Keep the raw root-relative glob keys. extractRoutePath (via buildRouteTree)
|
|
35
|
+
// is the single place that strips the pagesDir prefix; stripping it here as
|
|
36
|
+
// well would double-remove it and let the prefix regex match a same-named
|
|
37
|
+
// nested folder (e.g. pagesDir "app" turning "app/docs/app" into "/").
|
|
38
|
+
const pages = pagesGlob;
|
|
39
|
+
const layouts = layoutsGlob;
|
|
40
|
+
const notFounds = notFoundsGlob;
|
|
41
|
+
|
|
42
|
+
export function generateRoutes() {
|
|
43
|
+
const tree = buildRouteTree(pages, layouts, notFounds, ${JSON.stringify(r)});
|
|
44
|
+
const routes = treeToRoutes(tree, true);
|
|
45
|
+
return routes;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function Router() {
|
|
49
|
+
routes ??= generateRoutes();
|
|
50
|
+
return useRoutes(routes);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let routes;
|
|
54
|
+
|
|
55
|
+
if (import.meta.hot) {
|
|
56
|
+
import.meta.hot.accept(() => {
|
|
57
|
+
routes = undefined;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { Link, useRoutes };
|
|
62
|
+
export { useParams };
|
|
63
|
+
`}}}}exports.nextViteRouter=O;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Plugin as Plugin_2 } from 'vite';
|
|
2
|
+
|
|
3
|
+
export declare function nextViteRouter(options?: NextViteRouterPluginOptions): Plugin_2;
|
|
4
|
+
|
|
5
|
+
export declare type NextViteRouterPluginOptions = {
|
|
6
|
+
pagesDir?: string;
|
|
7
|
+
pageFile?: string;
|
|
8
|
+
layoutFile?: string;
|
|
9
|
+
notFoundFile?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { }
|
package/dist/plugin.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import P from "node:fs";
|
|
2
|
-
import
|
|
3
|
-
import { c as
|
|
4
|
-
function
|
|
2
|
+
import f from "node:path";
|
|
3
|
+
import { c as v, e as S, p as T, r as k } from "./tree-DfnssFxU.js";
|
|
4
|
+
function w(n) {
|
|
5
5
|
const a = n.map(k).filter(Boolean);
|
|
6
6
|
return a.length > 0 ? `/${a.join("/")}` : "/";
|
|
7
7
|
}
|
|
8
|
-
function
|
|
8
|
+
function G(n) {
|
|
9
9
|
const a = [
|
|
10
10
|
["pagesDir", n.pagesDir],
|
|
11
11
|
["pageFile", n.pageFile],
|
|
12
12
|
["layoutFile", n.layoutFile],
|
|
13
13
|
["notFoundFile", n.notFoundFile]
|
|
14
14
|
];
|
|
15
|
-
for (const [e,
|
|
16
|
-
if (!
|
|
15
|
+
for (const [e, t] of a)
|
|
16
|
+
if (!t.trim())
|
|
17
17
|
throw new Error(`nextViteRouter option "${e}" must not be empty.`);
|
|
18
|
-
const
|
|
19
|
-
([, e],
|
|
18
|
+
const s = a.slice(1), i = s.find(
|
|
19
|
+
([, e], t) => s.slice(t + 1).some(([, r]) => r === e)
|
|
20
20
|
);
|
|
21
21
|
if (i)
|
|
22
22
|
throw new Error(
|
|
@@ -24,54 +24,50 @@ function w(n) {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
function O(n) {
|
|
27
|
-
return n.replace(/\\/g, "/").replace(/^\/|\/$/g, "");
|
|
27
|
+
return n.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/|\/$/g, "");
|
|
28
28
|
}
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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: [] });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
29
|
+
function j(n, a, s) {
|
|
30
|
+
const i = a.pageFile ?? "page.tsx", e = /* @__PURE__ */ new Map();
|
|
31
|
+
for (const t of R(n, i, s)) {
|
|
32
|
+
const r = S(t, a), l = T(r), d = l.filter(
|
|
33
|
+
(u) => u.kind === "dynamic" || u.kind === "catchAll"
|
|
34
|
+
).map((u) => ({
|
|
35
|
+
name: u.name,
|
|
36
|
+
optional: u.kind === "catchAll" && u.optional
|
|
37
|
+
})), c = w(l);
|
|
38
|
+
e.set(c, { path: c, params: d });
|
|
39
|
+
const m = l[l.length - 1];
|
|
40
|
+
if ((m == null ? void 0 : m.kind) === "catchAll" && m.optional) {
|
|
41
|
+
const u = w(l.slice(0, -1));
|
|
42
|
+
e.has(u) || e.set(u, { path: u, params: [] });
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
|
-
return
|
|
45
|
+
return [...e.values()].sort((t, r) => t.path.localeCompare(r.path));
|
|
55
46
|
}
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
function
|
|
59
|
-
for (const
|
|
60
|
-
const
|
|
61
|
-
|
|
47
|
+
function R(n, a, s) {
|
|
48
|
+
const i = [];
|
|
49
|
+
function e(t) {
|
|
50
|
+
for (const r of P.readdirSync(t, { withFileTypes: !0 })) {
|
|
51
|
+
const l = f.join(t, r.name);
|
|
52
|
+
if (r.isDirectory())
|
|
53
|
+
e(l);
|
|
54
|
+
else if (r.isFile() && r.name === a) {
|
|
55
|
+
const d = f.relative(n, l).replace(/\\/g, "/");
|
|
56
|
+
i.push(`/${s}/${d}`);
|
|
57
|
+
}
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
|
-
return P.existsSync(n) &&
|
|
60
|
+
return P.existsSync(n) && e(n), i;
|
|
65
61
|
}
|
|
66
|
-
function
|
|
62
|
+
function D(n) {
|
|
67
63
|
const a = n.map(({ path: i }) => ` | ${JSON.stringify(i)}`).join(`
|
|
68
|
-
`),
|
|
69
|
-
const
|
|
70
|
-
...e.map(({ name:
|
|
64
|
+
`), s = n.map(({ path: i, params: e }) => {
|
|
65
|
+
const t = e.length > 0 ? [
|
|
66
|
+
...e.map(({ name: r, optional: l }) => ` ${r}${l ? "?" : ""}: string;`)
|
|
71
67
|
].join(`
|
|
72
68
|
`) : " [key: string]: never;";
|
|
73
69
|
return ` ${JSON.stringify(i)}: {
|
|
74
|
-
${
|
|
70
|
+
${t}
|
|
75
71
|
};`;
|
|
76
72
|
}).join(`
|
|
77
73
|
`);
|
|
@@ -87,7 +83,7 @@ ${a || ' | "/"'};
|
|
|
87
83
|
: never;
|
|
88
84
|
|
|
89
85
|
export interface RoutePathParams {
|
|
90
|
-
${
|
|
86
|
+
${s}
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
export const generateRoutes: () => RouteObject[];
|
|
@@ -97,89 +93,85 @@ ${r}
|
|
|
97
93
|
}
|
|
98
94
|
`;
|
|
99
95
|
}
|
|
100
|
-
function
|
|
96
|
+
function z(n = {}) {
|
|
101
97
|
const {
|
|
102
98
|
pagesDir: a = "src/app",
|
|
103
|
-
pageFile:
|
|
99
|
+
pageFile: s = "page.tsx",
|
|
104
100
|
layoutFile: i = "layout.tsx",
|
|
105
101
|
notFoundFile: e = "not-found.tsx"
|
|
106
102
|
} = n;
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
let
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
P.writeFileSync(
|
|
103
|
+
G({ pagesDir: a, pageFile: s, layoutFile: i, notFoundFile: e });
|
|
104
|
+
const t = O(a), r = { pagesDir: t, pageFile: s, layoutFile: i, notFoundFile: e }, l = "virtual:next-vite-router", d = "\0" + l;
|
|
105
|
+
let c = "", m = "", u, h;
|
|
106
|
+
function F() {
|
|
107
|
+
const o = D(j(c, r, t));
|
|
108
|
+
P.writeFileSync(m, o, "utf-8");
|
|
113
109
|
}
|
|
114
110
|
return {
|
|
115
111
|
name: "next-vite-router",
|
|
116
|
-
configResolved(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
),
|
|
112
|
+
configResolved(o) {
|
|
113
|
+
c = f.resolve(o.root, a), m = f.join(f.dirname(c), "virtual-next-vite-router.d.ts"), v(
|
|
114
|
+
R(c, s, t),
|
|
115
|
+
R(c, e, t),
|
|
116
|
+
r
|
|
117
|
+
), F();
|
|
122
118
|
},
|
|
123
|
-
configureServer(
|
|
124
|
-
if (
|
|
119
|
+
configureServer(o) {
|
|
120
|
+
if (u === o)
|
|
125
121
|
return;
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
if (
|
|
122
|
+
h == null || h(), u = o;
|
|
123
|
+
const g = (b) => {
|
|
124
|
+
if (f.relative(c, b).startsWith("..") || ![s, i, e].includes(f.basename(b)))
|
|
129
125
|
return;
|
|
130
126
|
try {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
v(
|
|
128
|
+
R(c, s, t),
|
|
129
|
+
R(c, e, t),
|
|
130
|
+
r
|
|
135
131
|
);
|
|
136
|
-
} catch (
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
} catch (x) {
|
|
133
|
+
o.config.logger.error(
|
|
134
|
+
x instanceof Error ? x.message : String(x)
|
|
139
135
|
);
|
|
140
136
|
return;
|
|
141
137
|
}
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
p = void 0,
|
|
138
|
+
F();
|
|
139
|
+
const $ = o.moduleGraph.getModuleById(d);
|
|
140
|
+
$ && (o.moduleGraph.invalidateModule($), p && clearTimeout(p), p = setTimeout(() => {
|
|
141
|
+
p = void 0, o.ws.send({ type: "full-reload", path: "*" });
|
|
146
142
|
}, 0));
|
|
147
143
|
};
|
|
148
144
|
let p;
|
|
149
|
-
const
|
|
150
|
-
p && (clearTimeout(p), p = void 0),
|
|
145
|
+
const y = () => {
|
|
146
|
+
p && (clearTimeout(p), p = void 0), o.watcher.off("add", g), o.watcher.off("unlink", g), o.watcher.off("close", y), u === o && (u = void 0, h = void 0);
|
|
151
147
|
};
|
|
152
|
-
|
|
148
|
+
o.watcher.on("add", g), o.watcher.on("unlink", g), o.watcher.once("close", y), h = y;
|
|
153
149
|
},
|
|
154
|
-
resolveId(
|
|
155
|
-
if (
|
|
150
|
+
resolveId(o) {
|
|
151
|
+
if (o === l)
|
|
156
152
|
return d;
|
|
157
153
|
},
|
|
158
|
-
load(
|
|
159
|
-
if (
|
|
160
|
-
const
|
|
154
|
+
load(o) {
|
|
155
|
+
if (o === d) {
|
|
156
|
+
const g = `/${t}/**/${s}`, p = `/${t}/**/${i}`, y = `/${t}/**/${e}`;
|
|
161
157
|
return `
|
|
162
158
|
import { buildRouteTree, treeToRoutes, normalizeSegment, createLazyElement, useParams } from 'next-vite-router';
|
|
163
159
|
import { Link, useRoutes } from 'react-router-dom';
|
|
164
160
|
|
|
165
|
-
const pagesGlob = import.meta.glob(${JSON.stringify(
|
|
161
|
+
const pagesGlob = import.meta.glob(${JSON.stringify(g)});
|
|
166
162
|
const layoutsGlob = import.meta.glob(${JSON.stringify(p)}, { eager: true });
|
|
167
|
-
const notFoundsGlob = import.meta.glob(${JSON.stringify(
|
|
168
|
-
|
|
169
|
-
const transformPath = (path) => path.replace(${JSON.stringify(x)}, '');
|
|
163
|
+
const notFoundsGlob = import.meta.glob(${JSON.stringify(y)});
|
|
170
164
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const notFounds =
|
|
178
|
-
Object.entries(notFoundsGlob).map(([key, value]) => [transformPath(key), value])
|
|
179
|
-
);
|
|
165
|
+
// Keep the raw root-relative glob keys. extractRoutePath (via buildRouteTree)
|
|
166
|
+
// is the single place that strips the pagesDir prefix; stripping it here as
|
|
167
|
+
// well would double-remove it and let the prefix regex match a same-named
|
|
168
|
+
// nested folder (e.g. pagesDir "app" turning "app/docs/app" into "/").
|
|
169
|
+
const pages = pagesGlob;
|
|
170
|
+
const layouts = layoutsGlob;
|
|
171
|
+
const notFounds = notFoundsGlob;
|
|
180
172
|
|
|
181
173
|
export function generateRoutes() {
|
|
182
|
-
const tree = buildRouteTree(pages, layouts, notFounds, ${JSON.stringify(
|
|
174
|
+
const tree = buildRouteTree(pages, layouts, notFounds, ${JSON.stringify(r)});
|
|
183
175
|
const routes = treeToRoutes(tree, true);
|
|
184
176
|
return routes;
|
|
185
177
|
}
|
|
@@ -205,5 +197,5 @@ export { useParams };
|
|
|
205
197
|
};
|
|
206
198
|
}
|
|
207
199
|
export {
|
|
208
|
-
|
|
200
|
+
z as nextViteRouter
|
|
209
201
|
};
|
|
@@ -27,9 +27,9 @@ function m(t) {
|
|
|
27
27
|
return o;
|
|
28
28
|
}
|
|
29
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("|");
|
|
30
|
+
const n = t.replace(/\\/g, "/"), e = s == null ? void 0 : s.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/|\/$/g, ""), r = e == null ? void 0 : e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), i = [o, c, a].map((l) => l.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
31
31
|
return n.replace(
|
|
32
|
-
e ? new RegExp(
|
|
32
|
+
e ? new RegExp(`^.*?(?:^|/)${r}/`) : /^.*?(?:^|\/)(?:app|pages)\//,
|
|
33
33
|
""
|
|
34
34
|
).replace(/\\/g, "/").replace(new RegExp(`/?(?:${i})$`), "");
|
|
35
35
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";function w(t){return/^\([^)]+\)$/.test(t)?"":/^\[\[\.\.\.(.+)\]\]$/.test(t)||/^\[\.\.\.(.+)\]$/.test(t)?"*":/^\[([^\]]+)\]$/.test(t)?t.replace(/^\[([^\]]+)\]$/,":$1"):t}const R=/^[A-Za-z_][A-Za-z0-9_]*$/;function $(t){return t.kind==="group"?"":t.kind==="dynamic"?`:${t.name}`:t.kind==="catchAll"?"*":t.name}function d(t){const s=t?t.split("/").filter(Boolean):[],o=[],c=new Set;for(const a of s){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;if(n)o.push({kind:"group",name:n[1]});else if(l){if(!R.test(l.name))throw new Error(`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.`);if(c.has(l.name))throw new Error(`Duplicate route parameter "${l.name}" in route "${t||"/"}".`);c.add(l.name),o.push(l)}else o.push({kind:"static",name:a})}return o}function u(t,{pagesDir:s,pageFile:o="page.tsx",layoutFile:c="layout.tsx",notFoundFile:a="not-found.tsx"}={}){const n=t.replace(/\\/g,"/"),e=s==null?void 0:s.replace(/\\/g,"/").replace(/^\.\//,"").replace(/^\/|\/$/g,""),r=e==null?void 0:e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=[o,c,a].map(l=>l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|");return n.replace(e?new RegExp(`^.*?(?:^|/)${r}/`):/^.*?(?:^|\/)(?:app|pages)\//,"").replace(/\\/g,"/").replace(new RegExp(`/?(?:${i})$`),"")}function g(t){return t.kind==="group"?"":t.kind==="catchAll"?"*":t.kind==="dynamic"?":param":$(t)}function P(t,s){const o=u(t,s),c=d(o),a=c.map(g).filter(Boolean),n=[a.length>0?`/${a.join("/")}`:"/"],e=c[c.length-1];if((e==null?void 0:e.kind)==="catchAll"&&e.optional){const r=a.slice(0,-1);n.push(r.length>0?`/${r.join("/")}`:"/")}return n}function j(t){return/(^|\/)(:param|\*)($|\/)/.test(t)?"pattern":"literal"}function E(t,s,o){const c=t.flatMap(n=>{const e=u(n,o),r=d(e),i=r[r.length-1];if((i==null?void 0:i.kind)!=="catchAll")return[];const l=r.map(g).filter(Boolean),h=l.length>0?`/${l.join("/")}`:"/",m=l.slice(0,-1),A=m.length>0?`/${m.join("/")}`:"/";return[{filePath:n,paths:new Set([h,A])}]}),a=new Map;for(const n of s)for(const e of P(n,o))for(const r of c)if(r.paths.has(e)){const i=`${e}\0${r.filePath}\0${n}`;a.set(i,{routePath:e,pageFile:r.filePath,notFoundFile:n})}if(a.size>0)throw new Error([...a.values()].sort((n,e)=>n.routePath.localeCompare(e.routePath)).map(({routePath:n,pageFile:e,notFoundFile:r})=>`Conflicting fallback routes "${n}" detected:
|
|
2
|
+
catch-all page: ${e}
|
|
3
|
+
not-found file: ${r}
|
|
4
|
+
A not-found route and a catch-all page cannot coexist at the same route level.`).join(`
|
|
5
|
+
|
|
6
|
+
`))}function p(t,s){const o=new Map;for(const a of t)for(const n of P(a,s)){const e=o.get(n)??[];e.push({kind:j(n),filePath:a}),o.set(n,e)}const c=[...o.entries()].filter(([,a])=>a.length>1).sort(([a],[n])=>a.localeCompare(n));if(c.length>0)throw new Error(c.map(([a,n])=>`${n[0].kind==="pattern"?"Ambiguous route pattern":"Duplicate route"} "${a}" detected:
|
|
7
|
+
${n.sort((e,r)=>e.filePath.localeCompare(r.filePath)).map(e=>` ${e.filePath}`).join(`
|
|
8
|
+
`)}`).join(`
|
|
9
|
+
|
|
10
|
+
`))}function k(t,s,o){p(t,o),p(s,o),E(t,s,o)}function f(t,s,o,c,a){const n=u(s,a),e=n?n.split("/"):[];let r=t,i="";e.forEach((l,h)=>{i=i?`${i}/${l}`:l,r.children.has(l)||r.children.set(l,{segment:l,fullPath:i,children:new Map}),r=r.children.get(l),h===e.length-1&&(r[c]=o)}),e.length===0&&(t[c]=o)}function b(t,s,o,c){k(Object.keys(t),Object.keys(o),c);const a={segment:"",fullPath:"",children:new Map};return Object.entries(s).forEach(([n,e])=>{f(a,n,e.default,"layout",c)}),Object.entries(t).forEach(([n,e])=>{f(a,n,e,"page",c)}),Object.entries(o).forEach(([n,e])=>{f(a,n,e,"notFound",c)}),a}exports.assertNoDuplicateRoutes=p;exports.assertNoRouteConflicts=k;exports.buildRouteTree=b;exports.extractRoutePath=u;exports.normalizeSegment=w;exports.parseRouteSegments=d;exports.routeSegmentToPath=$;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-vite-router",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Next.js-like file-based routing for React + Vite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,12 +11,24 @@
|
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
-
"import":
|
|
15
|
-
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./dist/index.d.cts",
|
|
20
|
+
"default": "./dist/index.cjs"
|
|
21
|
+
}
|
|
16
22
|
},
|
|
17
23
|
"./plugin": {
|
|
18
|
-
"import":
|
|
19
|
-
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/plugin.d.ts",
|
|
26
|
+
"default": "./dist/plugin.js"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/plugin.d.cts",
|
|
30
|
+
"default": "./dist/plugin.cjs"
|
|
31
|
+
}
|
|
20
32
|
}
|
|
21
33
|
},
|
|
22
34
|
"files": [
|
|
@@ -36,7 +48,7 @@
|
|
|
36
48
|
"author": "Rlzee",
|
|
37
49
|
"repository": {
|
|
38
50
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/Rlzee/next-vite-router"
|
|
51
|
+
"url": "git+https://github.com/Rlzee/next-vite-router.git"
|
|
40
52
|
},
|
|
41
53
|
"homepage": "https://github.com/Rlzee/next-vite-router#readme",
|
|
42
54
|
"license": "MIT",
|
|
@@ -62,7 +74,8 @@
|
|
|
62
74
|
},
|
|
63
75
|
"scripts": {
|
|
64
76
|
"dev": "vite build --watch",
|
|
65
|
-
"build": "vite build && vite build -c vite.config.cli.ts",
|
|
77
|
+
"build": "vite build && vite build -c vite.config.cli.ts && node -e \"import fs from 'node:fs'; for (const f of ['index','plugin']) fs.copyFileSync('dist/'+f+'.d.ts','dist/'+f+'.d.cts')\"",
|
|
78
|
+
"test": "pnpm run test:params && pnpm run test:generator && pnpm run test:routes && pnpm run test:local && pnpm run test:compatibility",
|
|
66
79
|
"test:local": "node test/test-package.js",
|
|
67
80
|
"test:routes": "node test/test-route-types.js",
|
|
68
81
|
"test:generator": "node --test test/routes.test.js",
|