@ubean/routing 0.1.7 → 0.1.8
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/dist/generator/index.d.ts +11 -1
- package/dist/generator/index.js +92 -21
- package/package.json +7 -7
|
@@ -18,6 +18,15 @@ import { ScanResult, ScannedLayout, ScannedPageRoute } from "../types.js";
|
|
|
18
18
|
interface GeneratorOptions {
|
|
19
19
|
/** Project root directory (absolute). */
|
|
20
20
|
cwd: string;
|
|
21
|
+
/**
|
|
22
|
+
* Source directory (absolute, defaults to `<cwd>/src`).
|
|
23
|
+
*
|
|
24
|
+
* Used by the default `getImportPath` / `getLayoutImportPath` to compute
|
|
25
|
+
* import specifiers relative to `srcDir` (the conventional target of the
|
|
26
|
+
* `@/` path alias). When the alias differs, supply custom `getImportPath` /
|
|
27
|
+
* `getLayoutImportPath` callbacks.
|
|
28
|
+
*/
|
|
29
|
+
srcDir?: string;
|
|
21
30
|
/** Output directory for `routes.ts` / `imports.ts` (absolute or relative to `cwd`). */
|
|
22
31
|
outDir: string;
|
|
23
32
|
/** Path for `typed-router.d.ts` (absolute or relative to `cwd`). Default: `<outDir>/typed-router.d.ts`. */
|
|
@@ -41,7 +50,8 @@ interface GeneratorOptions {
|
|
|
41
50
|
/**
|
|
42
51
|
* Compute the import specifier for a page file. Receives the absolute
|
|
43
52
|
* `fullPath` of the page; should return a module specifier usable from
|
|
44
|
-
* `imports.ts`. Defaults to a
|
|
53
|
+
* `imports.ts`. Defaults to a `srcDir`-relative path prefixed with `@/`
|
|
54
|
+
* (e.g. `@/pages/about.vue`), matching the common `@/` → `src/` alias.
|
|
45
55
|
*/
|
|
46
56
|
getImportPath?: (page: ScannedPageRoute) => string;
|
|
47
57
|
/** Same as `getImportPath` but for layouts. */
|
package/dist/generator/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var RouteFileGenerator = class {
|
|
|
9
9
|
const dtsPath = options.dtsPath ?? joinPosix(options.outDir, "typed-router.d.ts");
|
|
10
10
|
this.opts = {
|
|
11
11
|
cwd: options.cwd,
|
|
12
|
+
srcDir: options.srcDir ?? joinPosix(options.cwd, "src"),
|
|
12
13
|
outDir: options.outDir,
|
|
13
14
|
dtsPath,
|
|
14
15
|
generateRoutes: options.generateRoutes ?? true,
|
|
@@ -52,23 +53,23 @@ ${scanResult.pages.map((p) => this.renderRouteRecord(p)).join(",\n")}
|
|
|
52
53
|
}
|
|
53
54
|
renderRouteRecord(page) {
|
|
54
55
|
const parts = [];
|
|
55
|
-
parts.push(`
|
|
56
|
-
parts.push(`
|
|
57
|
-
const
|
|
58
|
-
parts.push(`
|
|
56
|
+
parts.push(` name: ${JSON.stringify(page.name)}`);
|
|
57
|
+
parts.push(` path: ${JSON.stringify(page.route)}`);
|
|
58
|
+
const componentKey = page.isReuse && page.reuseTarget ? page.reuseTarget : page.name;
|
|
59
|
+
parts.push(` component: ${JSON.stringify(componentKey)}`);
|
|
59
60
|
if (page.layout !== void 0) {
|
|
60
61
|
const layoutVal = page.layout === false ? "false" : JSON.stringify(page.layout);
|
|
61
|
-
parts.push(`
|
|
62
|
+
parts.push(` layout: ${layoutVal}`);
|
|
62
63
|
}
|
|
63
|
-
if (page.isReuse) parts.push(`
|
|
64
|
+
if (page.isReuse) parts.push(` reuse: true`);
|
|
64
65
|
const meta = this.computeMeta(page);
|
|
65
|
-
if (meta && Object.keys(meta).length > 0) parts.push(`
|
|
66
|
-
if (page.pageMeta?.cache === true) parts.push(`
|
|
67
|
-
if (page.pageMeta?.requiresAuth === true) parts.push(`
|
|
66
|
+
if (meta && Object.keys(meta).length > 0) parts.push(` meta: ${JSON.stringify(meta)}`);
|
|
67
|
+
if (page.pageMeta?.cache === true) parts.push(` cache: true`);
|
|
68
|
+
if (page.pageMeta?.requiresAuth === true) parts.push(` requiresAuth: true`);
|
|
68
69
|
if (page.pageMeta?.middleware) {
|
|
69
70
|
const mw = page.pageMeta.middleware;
|
|
70
71
|
const mwVal = Array.isArray(mw) ? JSON.stringify(mw) : JSON.stringify(mw);
|
|
71
|
-
parts.push(`
|
|
72
|
+
parts.push(` middleware: ${mwVal}`);
|
|
72
73
|
}
|
|
73
74
|
return ` {\n${parts.join(",\n")}\n }`;
|
|
74
75
|
}
|
|
@@ -84,10 +85,39 @@ ${scanResult.pages.map((p) => this.renderRouteRecord(p)).join(",\n")}
|
|
|
84
85
|
const header = this.opts.headerComment;
|
|
85
86
|
const layouts = scanResult.layouts.map((l) => this.renderLayoutImport(l)).join(",\n");
|
|
86
87
|
const views = scanResult.pages.filter((p) => !p.isReuse).map((p) => this.renderViewImport(p)).join(",\n");
|
|
88
|
+
const layoutType = scanResult.layouts.length > 0 ? `'${scanResult.layouts.map((l) => l.name).join("' | '")}'` : "string";
|
|
89
|
+
const filePages = scanResult.pages.filter((p) => !p.isReuse);
|
|
90
|
+
const reusePages = scanResult.pages.filter((p) => p.isReuse);
|
|
87
91
|
return `${header}
|
|
88
92
|
|
|
89
|
-
export type LayoutKey = ${
|
|
90
|
-
|
|
93
|
+
export type LayoutKey = ${layoutType};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Route keys that have a corresponding source file (\`.vue\` / \`.md\`).
|
|
97
|
+
* Excludes reuse routes and builtin routes.
|
|
98
|
+
*
|
|
99
|
+
* The \`views\` map is keyed by this type — \`views[RouteFileKey]\` resolves
|
|
100
|
+
* to the actual component loader.
|
|
101
|
+
*/
|
|
102
|
+
export type RouteFileKey = ${filePages.length > 0 ? `'${filePages.map((p) => p.name).join("' | '")}'` : "never"};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Route keys declared via \`.reuse.ts\` / \`.reuse.vue\`.
|
|
106
|
+
* They reference another route's component via \`reuseTarget\`.
|
|
107
|
+
*/
|
|
108
|
+
export type RouteReuseKey = ${reusePages.length > 0 ? `'${reusePages.map((p) => p.name).join("' | '")}'` : "never"};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Builtin route keys (framework-provided, e.g. \`root\` / \`not_found\`).
|
|
112
|
+
* Currently unused — reserved for future builtin pages.
|
|
113
|
+
*/
|
|
114
|
+
export type BuiltinRouteKey = never;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* All page route names. Union of {@link RouteFileKey}, {@link RouteReuseKey}
|
|
118
|
+
* and {@link BuiltinRouteKey}.
|
|
119
|
+
*/
|
|
120
|
+
export type RouteKey = ${scanResult.pages.length > 0 ? `'${scanResult.pages.map((p) => p.name).join("' | '")}'` : "never"};
|
|
91
121
|
|
|
92
122
|
export type Lazy<T> = () => Promise<T>;
|
|
93
123
|
export type RawRouteComponent = import('vue').Component | Lazy<import('vue').Component>;
|
|
@@ -95,7 +125,14 @@ export type RawRouteComponent = import('vue').Component | Lazy<import('vue').Com
|
|
|
95
125
|
export interface RouteRecord {
|
|
96
126
|
name: RouteKey;
|
|
97
127
|
path: string;
|
|
98
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Key into the \`views\` map — resolve the actual component via
|
|
130
|
+
* \`views[route.component]\`.
|
|
131
|
+
*
|
|
132
|
+
* For reuse routes, this is the \`reuseTarget\`'s \`RouteFileKey\`
|
|
133
|
+
* (not the reuse route's own name), so the same component is reused.
|
|
134
|
+
*/
|
|
135
|
+
component: RouteFileKey;
|
|
99
136
|
layout?: LayoutKey | false;
|
|
100
137
|
reuse?: boolean;
|
|
101
138
|
meta?: Record<string, unknown>;
|
|
@@ -108,19 +145,19 @@ export const layouts: Record<LayoutKey, RawRouteComponent> = {
|
|
|
108
145
|
${layouts}
|
|
109
146
|
};
|
|
110
147
|
|
|
111
|
-
export const views: Record<
|
|
148
|
+
export const views: Record<RouteFileKey, RawRouteComponent> = {
|
|
112
149
|
${views}
|
|
113
150
|
};
|
|
114
151
|
`;
|
|
115
152
|
}
|
|
116
153
|
renderLayoutImport(layout) {
|
|
117
|
-
const importPath = this.opts.getLayoutImportPath?.(layout) ?? defaultLayoutImportPath(layout, this.opts.
|
|
154
|
+
const importPath = this.opts.getLayoutImportPath?.(layout) ?? defaultLayoutImportPath(layout, this.opts.srcDir);
|
|
118
155
|
if (typeof this.opts.layoutLazy === "function" ? this.opts.layoutLazy(layout) : this.opts.layoutLazy) return ` ${layout.name}: () => import(${JSON.stringify(importPath)})`;
|
|
119
156
|
const importName = `__layout_${layout.name}`;
|
|
120
157
|
return ` ${layout.name}: ${importName}, // import ${importName} from ${JSON.stringify(importPath)}`;
|
|
121
158
|
}
|
|
122
159
|
renderViewImport(page) {
|
|
123
|
-
const importPath = this.opts.getImportPath?.(page) ?? defaultPageImportPath(page, this.opts.
|
|
160
|
+
const importPath = this.opts.getImportPath?.(page) ?? defaultPageImportPath(page, this.opts.srcDir);
|
|
124
161
|
if (typeof this.opts.routeLazy === "function" ? this.opts.routeLazy(page) : this.opts.routeLazy) return ` ${page.name}: () => import(${JSON.stringify(importPath)})`;
|
|
125
162
|
return ` ${page.name}: ${page.name}, // import ${page.name} from ${JSON.stringify(importPath)}`;
|
|
126
163
|
}
|
|
@@ -158,6 +195,20 @@ ${pathMapEntries}
|
|
|
158
195
|
* Reuse route keys (pages using \`.reuse.vue\` suffix).
|
|
159
196
|
*/
|
|
160
197
|
export type ReuseRouteKey = ${reuseKeys};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Builtin route keys (framework-provided, e.g. \`root\` / \`not_found\`).
|
|
201
|
+
* Currently unused — reserved for future builtin pages.
|
|
202
|
+
*/
|
|
203
|
+
export type BuiltinRouteKey = never;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Route keys that have a corresponding source file (\`.vue\` / \`.md\`).
|
|
207
|
+
* Excludes reuse routes and builtin routes.
|
|
208
|
+
*
|
|
209
|
+
* Use this to index the \`views\` map for component lookup.
|
|
210
|
+
*/
|
|
211
|
+
export type RouteFileKey = Exclude<RouteKey, ReuseRouteKey | BuiltinRouteKey>;
|
|
161
212
|
}
|
|
162
213
|
|
|
163
214
|
declare module 'vue-router/auto-routes' {
|
|
@@ -184,7 +235,7 @@ declare module 'vue-router' {
|
|
|
184
235
|
}
|
|
185
236
|
}
|
|
186
237
|
|
|
187
|
-
export type { RouteLayoutKey, RoutePathMap, RouteKey, RoutePath, ReuseRouteKey } from '@ubean/routing';
|
|
238
|
+
export type { RouteLayoutKey, RoutePathMap, RouteKey, RoutePath, ReuseRouteKey, BuiltinRouteKey, RouteFileKey } from '@ubean/routing';
|
|
188
239
|
`;
|
|
189
240
|
}
|
|
190
241
|
/**
|
|
@@ -205,11 +256,31 @@ export type { RouteLayoutKey, RoutePathMap, RouteKey, RoutePath, ReuseRouteKey }
|
|
|
205
256
|
return `RouteRecordInfo<${JSON.stringify(page.name)}, ${JSON.stringify(page.route)}, ${paramsRaw}, ${paramsResolved}>`;
|
|
206
257
|
}
|
|
207
258
|
};
|
|
208
|
-
|
|
209
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Extensions stripped when computing the default import specifier.
|
|
261
|
+
*
|
|
262
|
+
* `.vue` / `.md` / `.mdx` are KEPT — Vite dispatches them to dedicated
|
|
263
|
+
* transforms (vue-loader / markdown) based on the extension, so the
|
|
264
|
+
* explicit suffix is required for correct resolution at runtime.
|
|
265
|
+
*
|
|
266
|
+
* `.ts` / `.tsx` / `.js` / `.jsx` are STRIPPED — TypeScript/Vite convention
|
|
267
|
+
* is to import script modules without their extension.
|
|
268
|
+
*/
|
|
269
|
+
const STRIPPABLE_IMPORT_EXT = /\.(tsx?|jsx?)$/;
|
|
270
|
+
/**
|
|
271
|
+
* Compute the default import specifier for a page.
|
|
272
|
+
*
|
|
273
|
+
* Uses `srcDir` (not `cwd`) as the relative base so that the resulting
|
|
274
|
+
* `@/<rel>` path matches the common `@/` → `src/` path alias. For example,
|
|
275
|
+
* a page at `<cwd>/src/pages/about.vue` becomes `@/pages/about.vue`
|
|
276
|
+
* (NOT `@/src/pages/about.vue`, which would double the `src/` prefix and
|
|
277
|
+
* fail to resolve under the standard alias).
|
|
278
|
+
*/
|
|
279
|
+
function defaultPageImportPath(page, srcDir) {
|
|
280
|
+
return `@/${relativePosix(srcDir, page.fullPath).replace(STRIPPABLE_IMPORT_EXT, "")}`;
|
|
210
281
|
}
|
|
211
|
-
function defaultLayoutImportPath(layout,
|
|
212
|
-
return `@/${relativePosix(
|
|
282
|
+
function defaultLayoutImportPath(layout, srcDir) {
|
|
283
|
+
return `@/${relativePosix(srcDir, layout.fullPath).replace(STRIPPABLE_IMPORT_EXT, "")}`;
|
|
213
284
|
}
|
|
214
285
|
/**
|
|
215
286
|
* 从 vue-router 风格路径中提取参数信息。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/routing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "File-based routing core for ubean (scanner, rou3 matcher, route name generator, define-page AST extractor, file generator)",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -30,20 +30,20 @@
|
|
|
30
30
|
"scule": "^1.3.0",
|
|
31
31
|
"tinyglobby": "^0.2.17",
|
|
32
32
|
"ufo": "1.6.4",
|
|
33
|
-
"@ubean/types": "0.1.
|
|
34
|
-
"@ubean/utils": "0.1.
|
|
33
|
+
"@ubean/types": "0.1.8",
|
|
34
|
+
"@ubean/utils": "0.1.8"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^26.1.
|
|
38
|
-
"typescript": "
|
|
37
|
+
"@types/node": "^26.1.2",
|
|
38
|
+
"typescript": "7.0.2",
|
|
39
39
|
"vite-plus": "0.2.6",
|
|
40
40
|
"vue": "^3.5.40",
|
|
41
41
|
"vue-router": "^5.2.0",
|
|
42
|
-
"@ubean/markdown": "0.1.
|
|
42
|
+
"@ubean/markdown": "0.1.8"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"vue-router": "^4.0.0",
|
|
46
|
-
"@ubean/markdown": "0.1.
|
|
46
|
+
"@ubean/markdown": "0.1.8"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
49
|
"@ubean/markdown": {
|