@kimesh/router-generator 0.2.8 → 0.2.9
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/index.d.mts +119 -1
- package/dist/index.mjs +401 -67
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -417,6 +417,15 @@ interface LayerRouteNode extends RouteNode {
|
|
|
417
417
|
*/
|
|
418
418
|
declare function collectLayerRoutes(sources: LayerRouteSource[], config: ResolvedRouterConfig): Promise<LayerRoutesResult>;
|
|
419
419
|
//#endregion
|
|
420
|
+
//#region src/route-merger.d.ts
|
|
421
|
+
/**
|
|
422
|
+
* Merge app routes with layer routes
|
|
423
|
+
* App routes (priority 0) take precedence over layer routes
|
|
424
|
+
* Layer routes are nested under the host's root layout if one exists
|
|
425
|
+
* If app has a layout route for a path, layer routes become its children
|
|
426
|
+
*/
|
|
427
|
+
declare function mergeRoutes(appRoutes: RouteNode[], layerRoutes: RouteNode[], _config?: ResolvedRouterConfig): RouteNode[];
|
|
428
|
+
//#endregion
|
|
420
429
|
//#region src/layout-resolver.d.ts
|
|
421
430
|
/**
|
|
422
431
|
* Resolved layout information
|
|
@@ -501,4 +510,113 @@ declare function generateLayoutStructure(chain: LayoutChain): {
|
|
|
501
510
|
children?: any[];
|
|
502
511
|
} | null;
|
|
503
512
|
//#endregion
|
|
504
|
-
|
|
513
|
+
//#region src/middleware/types.d.ts
|
|
514
|
+
/**
|
|
515
|
+
* @kimesh/router-generator - Middleware Types (Build-time)
|
|
516
|
+
*/
|
|
517
|
+
/**
|
|
518
|
+
* Middleware file metadata discovered during scanning
|
|
519
|
+
*/
|
|
520
|
+
interface MiddlewareFile {
|
|
521
|
+
/** Middleware name (without extension and .global suffix) */
|
|
522
|
+
name: string;
|
|
523
|
+
/** Original filename with extension */
|
|
524
|
+
fileName: string;
|
|
525
|
+
/** Absolute path to the middleware file */
|
|
526
|
+
filePath: string;
|
|
527
|
+
/** Whether this is a global middleware (`.global.ts` suffix) */
|
|
528
|
+
isGlobal: boolean;
|
|
529
|
+
/** Optional priority extracted from filename (e.g., `01.auth.global.ts` -> 1) */
|
|
530
|
+
priority?: number;
|
|
531
|
+
/** Source layer name (for multi-layer support) */
|
|
532
|
+
layer?: string;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Middleware scanning options
|
|
536
|
+
*/
|
|
537
|
+
interface MiddlewareScanOptions {
|
|
538
|
+
/** Root directory of the project */
|
|
539
|
+
root: string;
|
|
540
|
+
/** Middleware directory path (usually `src/middleware`) */
|
|
541
|
+
middlewareDir: string;
|
|
542
|
+
/** File extensions to scan */
|
|
543
|
+
extensions?: string[];
|
|
544
|
+
/** Ordering strategy for middleware */
|
|
545
|
+
orderStrategy?: "alphabetical" | "explicit" | "none";
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Layer middleware configuration
|
|
549
|
+
*/
|
|
550
|
+
interface LayerMiddlewareConfig {
|
|
551
|
+
/** Layer name */
|
|
552
|
+
layerName: string;
|
|
553
|
+
/** Layer path */
|
|
554
|
+
layerPath: string;
|
|
555
|
+
/** Middleware directory within the layer */
|
|
556
|
+
middlewareDir: string;
|
|
557
|
+
/** Layer priority (lower = higher priority) */
|
|
558
|
+
priority: number;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Result of middleware scanning
|
|
562
|
+
*/
|
|
563
|
+
interface MiddlewareScanResult {
|
|
564
|
+
/** All discovered middleware files */
|
|
565
|
+
middleware: MiddlewareFile[];
|
|
566
|
+
/** Global middleware (sorted by priority/order) */
|
|
567
|
+
globalMiddleware: MiddlewareFile[];
|
|
568
|
+
/** Named middleware (non-global) */
|
|
569
|
+
namedMiddleware: MiddlewareFile[];
|
|
570
|
+
}
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/middleware/scanner.d.ts
|
|
573
|
+
/**
|
|
574
|
+
* Scan a single middleware directory
|
|
575
|
+
*/
|
|
576
|
+
declare function scanMiddlewareDir(middlewareDir: string, options?: {
|
|
577
|
+
extensions?: string[];
|
|
578
|
+
layer?: string;
|
|
579
|
+
}): Promise<MiddlewareFile[]>;
|
|
580
|
+
/**
|
|
581
|
+
* Scan middleware files from the application
|
|
582
|
+
*/
|
|
583
|
+
declare function scanMiddlewareFiles(options: MiddlewareScanOptions): Promise<MiddlewareScanResult>;
|
|
584
|
+
/**
|
|
585
|
+
* Scan middleware from multiple layers and merge them
|
|
586
|
+
* Layer priority determines override behavior (lower = higher priority)
|
|
587
|
+
*/
|
|
588
|
+
declare function scanLayerMiddleware(layers: LayerMiddlewareConfig[], options?: {
|
|
589
|
+
extensions?: string[];
|
|
590
|
+
orderStrategy?: "alphabetical" | "explicit" | "none";
|
|
591
|
+
}): Promise<MiddlewareScanResult>;
|
|
592
|
+
/**
|
|
593
|
+
* Create a middleware scanner with configuration
|
|
594
|
+
*/
|
|
595
|
+
declare class MiddlewareScanner {
|
|
596
|
+
private options;
|
|
597
|
+
constructor(options: MiddlewareScanOptions);
|
|
598
|
+
/**
|
|
599
|
+
* Scan the configured middleware directory
|
|
600
|
+
*/
|
|
601
|
+
scan(): Promise<MiddlewareScanResult>;
|
|
602
|
+
/**
|
|
603
|
+
* Check if middleware directory exists
|
|
604
|
+
*/
|
|
605
|
+
exists(): boolean;
|
|
606
|
+
}
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region src/middleware/codegen.d.ts
|
|
609
|
+
/**
|
|
610
|
+
* Generate middleware registry code
|
|
611
|
+
*/
|
|
612
|
+
declare function generateMiddlewareRegistry(result: MiddlewareScanResult): string;
|
|
613
|
+
/**
|
|
614
|
+
* Generate middleware type declarations
|
|
615
|
+
*/
|
|
616
|
+
declare function generateMiddlewareTypes(result: MiddlewareScanResult): string;
|
|
617
|
+
/**
|
|
618
|
+
* Generate middleware module content for a specific middleware file
|
|
619
|
+
*/
|
|
620
|
+
declare function generateMiddlewareModule(file: MiddlewareFile): string;
|
|
621
|
+
//#endregion
|
|
622
|
+
export { type ExtendedParsedRoute, type GeneratedOutput, type KimeshRouterConfig, type KimeshRouterPluginOptions, type LayerMiddlewareConfig, type LayerRouteConfig, type LayerRouteNode, type LayerRouteSource, type LayerRoutesResult, type LayoutChain, LayoutResolver, type MiddlewareFile, type MiddlewareScanOptions, type MiddlewareScanResult, MiddlewareScanner, type ParsedFileName, type ParsedRoute, type ResolvedLayout, type ResolvedRouterConfig, type RouteNode, type RouteNodeType, type RouteParam, RouteScanner, RouteTreeBuilder, type TreeNode, buildGlobPattern, buildRoutePath, buildRouteTree, collectLayerRoutes, createCatchAllParam, createDynamicParam, createEmptyParsedRoute, createScanner, createTreeBuilder, extractDynamicParam, generateLayoutStructure, generateMiddlewareModule, generateMiddlewareRegistry, generateMiddlewareTypes, generateRouteTypes, generateRoutes, isDotNotationRoute, isRootDir, kimeshRouterGenerator, mergeRoutes, parseRouteFile, processDirectoryPath, processPathSegment, scanLayerMiddleware, scanMiddlewareDir, scanMiddlewareFiles, unescapeBrackets };
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,50 @@
|
|
|
1
1
|
import * as path from "pathe";
|
|
2
|
+
import { basename, extname, join } from "pathe";
|
|
2
3
|
import * as fs from "node:fs";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
3
5
|
import { watch } from "chokidar";
|
|
4
6
|
import fg from "fast-glob";
|
|
5
7
|
import { parse } from "@vue/compiler-sfc";
|
|
6
8
|
import { parseSync } from "oxc-parser";
|
|
7
9
|
import { consola } from "consola";
|
|
10
|
+
import { readdir } from "node:fs/promises";
|
|
8
11
|
|
|
12
|
+
//#region rolldown:runtime
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
15
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
16
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
17
|
+
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
18
|
+
var __exportAll = (all, symbols) => {
|
|
19
|
+
let target = {};
|
|
20
|
+
for (var name in all) {
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (symbols) {
|
|
27
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
28
|
+
}
|
|
29
|
+
return target;
|
|
30
|
+
};
|
|
31
|
+
var __copyProps = (to, from, except, desc) => {
|
|
32
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
33
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
34
|
+
key = keys[i];
|
|
35
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
36
|
+
__defProp(to, key, {
|
|
37
|
+
get: ((k) => from[k]).bind(null, key),
|
|
38
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return to;
|
|
44
|
+
};
|
|
45
|
+
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
9
48
|
//#region src/parser.ts
|
|
10
49
|
/**
|
|
11
50
|
* @kimesh/router-generator - OXC-Powered Route Parser
|
|
@@ -1637,6 +1676,83 @@ function shouldBeLazy(filePath, config) {
|
|
|
1637
1676
|
return importMode === "async";
|
|
1638
1677
|
}
|
|
1639
1678
|
|
|
1679
|
+
//#endregion
|
|
1680
|
+
//#region src/route-merger.ts
|
|
1681
|
+
var route_merger_exports = /* @__PURE__ */ __exportAll({ mergeRoutes: () => mergeRoutes });
|
|
1682
|
+
/**
|
|
1683
|
+
* Merge app routes with layer routes
|
|
1684
|
+
* App routes (priority 0) take precedence over layer routes
|
|
1685
|
+
* Layer routes are nested under the host's root layout if one exists
|
|
1686
|
+
* If app has a layout route for a path, layer routes become its children
|
|
1687
|
+
*/
|
|
1688
|
+
function mergeRoutes(appRoutes, layerRoutes, _config) {
|
|
1689
|
+
const rootLayout = findRootLayout(appRoutes);
|
|
1690
|
+
if (rootLayout && rootLayout.children) return mergeWithRootLayout(appRoutes, layerRoutes, rootLayout);
|
|
1691
|
+
return mergeWithoutRootLayout(appRoutes, layerRoutes);
|
|
1692
|
+
}
|
|
1693
|
+
function findRootLayout(routes) {
|
|
1694
|
+
return routes.find((r) => r.routePath === "/" && r.type === "layout") || null;
|
|
1695
|
+
}
|
|
1696
|
+
function mergeWithRootLayout(appRoutes, layerRoutes, rootLayout) {
|
|
1697
|
+
const layoutRoutesByPath = buildLayoutRoutesMap(rootLayout.children);
|
|
1698
|
+
for (const layerRoute of layerRoutes) {
|
|
1699
|
+
const relativePath = normalizeRoutePath(layerRoute.routePath);
|
|
1700
|
+
const hostLayoutRoute = layoutRoutesByPath.get(relativePath);
|
|
1701
|
+
if (hostLayoutRoute) nestLayerUnderHost(layerRoute, hostLayoutRoute);
|
|
1702
|
+
else addLayerAsSibling(layerRoute, relativePath, rootLayout, layoutRoutesByPath);
|
|
1703
|
+
}
|
|
1704
|
+
sortRouteChildren(rootLayout.children);
|
|
1705
|
+
return appRoutes;
|
|
1706
|
+
}
|
|
1707
|
+
function buildLayoutRoutesMap(children) {
|
|
1708
|
+
const map = /* @__PURE__ */ new Map();
|
|
1709
|
+
for (const child of children) if (child.routePath && !child.routePath.includes(":")) {
|
|
1710
|
+
const normalizedPath = normalizeRoutePath(child.routePath);
|
|
1711
|
+
map.set(normalizedPath, child);
|
|
1712
|
+
}
|
|
1713
|
+
return map;
|
|
1714
|
+
}
|
|
1715
|
+
function normalizeRoutePath(routePath) {
|
|
1716
|
+
return routePath.startsWith("/") ? routePath.slice(1) : routePath;
|
|
1717
|
+
}
|
|
1718
|
+
function nestLayerUnderHost(layerRoute, hostLayoutRoute) {
|
|
1719
|
+
hostLayoutRoute.children = hostLayoutRoute.children || [];
|
|
1720
|
+
hostLayoutRoute.type = "layout";
|
|
1721
|
+
const layerWrapper = {
|
|
1722
|
+
...layerRoute,
|
|
1723
|
+
routePath: "",
|
|
1724
|
+
parent: hostLayoutRoute
|
|
1725
|
+
};
|
|
1726
|
+
hostLayoutRoute.children.push(layerWrapper);
|
|
1727
|
+
}
|
|
1728
|
+
function addLayerAsSibling(layerRoute, relativePath, rootLayout, layoutRoutesByPath) {
|
|
1729
|
+
const nestedRoute = {
|
|
1730
|
+
...layerRoute,
|
|
1731
|
+
routePath: relativePath,
|
|
1732
|
+
parent: rootLayout
|
|
1733
|
+
};
|
|
1734
|
+
rootLayout.children.push(nestedRoute);
|
|
1735
|
+
layoutRoutesByPath.set(relativePath, nestedRoute);
|
|
1736
|
+
}
|
|
1737
|
+
function sortRouteChildren(children) {
|
|
1738
|
+
children.sort((a, b) => {
|
|
1739
|
+
if (a.routePath === "") return -1;
|
|
1740
|
+
if (b.routePath === "") return 1;
|
|
1741
|
+
return a.routePath.localeCompare(b.routePath);
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
function mergeWithoutRootLayout(appRoutes, layerRoutes) {
|
|
1745
|
+
const merged = [...appRoutes];
|
|
1746
|
+
const usedPaths = new Set(appRoutes.map((r) => r.routePath));
|
|
1747
|
+
for (const layerRoute of layerRoutes) if (!usedPaths.has(layerRoute.routePath)) {
|
|
1748
|
+
merged.push(layerRoute);
|
|
1749
|
+
usedPaths.add(layerRoute.routePath);
|
|
1750
|
+
}
|
|
1751
|
+
merged.sort((a, b) => a.routePath.localeCompare(b.routePath));
|
|
1752
|
+
return merged;
|
|
1753
|
+
}
|
|
1754
|
+
var init_route_merger = __esmMin((() => {}));
|
|
1755
|
+
|
|
1640
1756
|
//#endregion
|
|
1641
1757
|
//#region src/plugin.ts
|
|
1642
1758
|
const VIRTUAL_ROUTES_ID = "\0virtual:kimesh-routes";
|
|
@@ -1743,7 +1859,7 @@ function kimeshRouterGenerator(options = {}) {
|
|
|
1743
1859
|
priority: l.priority
|
|
1744
1860
|
})), config);
|
|
1745
1861
|
log("Collected layer routes:", layerResult.routes.length);
|
|
1746
|
-
routes = mergeRoutes(appRoutes, layerResult.routes, config);
|
|
1862
|
+
routes = mergeRoutes$1(appRoutes, layerResult.routes, config);
|
|
1747
1863
|
} else routes = appRoutes;
|
|
1748
1864
|
log("Final merged routes:", routes.length, routes.map((r) => r.routePath));
|
|
1749
1865
|
await writeGeneratedFiles();
|
|
@@ -1799,75 +1915,14 @@ function kimeshRouterGenerator(options = {}) {
|
|
|
1799
1915
|
* Layer routes are nested under the host's root layout if one exists
|
|
1800
1916
|
* If app has a layout route for a path, layer routes become its children
|
|
1801
1917
|
*/
|
|
1802
|
-
function mergeRoutes(appRoutes, layerRoutes, _config) {
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
return mergeWithoutRootLayout(appRoutes, layerRoutes);
|
|
1806
|
-
}
|
|
1807
|
-
function findRootLayout(routes) {
|
|
1808
|
-
return routes.find((r) => r.routePath === "/" && r.type === "layout") || null;
|
|
1809
|
-
}
|
|
1810
|
-
function mergeWithRootLayout(appRoutes, layerRoutes, rootLayout) {
|
|
1811
|
-
const layoutRoutesByPath = buildLayoutRoutesMap(rootLayout.children);
|
|
1812
|
-
for (const layerRoute of layerRoutes) {
|
|
1813
|
-
const relativePath = normalizeRoutePath(layerRoute.routePath);
|
|
1814
|
-
const hostLayoutRoute = layoutRoutesByPath.get(relativePath);
|
|
1815
|
-
if (hostLayoutRoute) nestLayerUnderHost(layerRoute, hostLayoutRoute);
|
|
1816
|
-
else addLayerAsSibling(layerRoute, relativePath, rootLayout, layoutRoutesByPath);
|
|
1817
|
-
}
|
|
1818
|
-
sortRouteChildren(rootLayout.children);
|
|
1819
|
-
return appRoutes;
|
|
1820
|
-
}
|
|
1821
|
-
function buildLayoutRoutesMap(children) {
|
|
1822
|
-
const map = /* @__PURE__ */ new Map();
|
|
1823
|
-
for (const child of children) if (child.routePath && !child.routePath.includes(":")) {
|
|
1824
|
-
const normalizedPath = normalizeRoutePath(child.routePath);
|
|
1825
|
-
map.set(normalizedPath, child);
|
|
1826
|
-
}
|
|
1827
|
-
return map;
|
|
1828
|
-
}
|
|
1829
|
-
function normalizeRoutePath(routePath) {
|
|
1830
|
-
return routePath.startsWith("/") ? routePath.slice(1) : routePath;
|
|
1831
|
-
}
|
|
1832
|
-
function nestLayerUnderHost(layerRoute, hostLayoutRoute) {
|
|
1833
|
-
hostLayoutRoute.children = hostLayoutRoute.children || [];
|
|
1834
|
-
hostLayoutRoute.type = "layout";
|
|
1835
|
-
const layerWrapper = {
|
|
1836
|
-
...layerRoute,
|
|
1837
|
-
routePath: "",
|
|
1838
|
-
parent: hostLayoutRoute
|
|
1839
|
-
};
|
|
1840
|
-
hostLayoutRoute.children.push(layerWrapper);
|
|
1841
|
-
}
|
|
1842
|
-
function addLayerAsSibling(layerRoute, relativePath, rootLayout, layoutRoutesByPath) {
|
|
1843
|
-
const nestedRoute = {
|
|
1844
|
-
...layerRoute,
|
|
1845
|
-
routePath: relativePath,
|
|
1846
|
-
parent: rootLayout
|
|
1847
|
-
};
|
|
1848
|
-
rootLayout.children.push(nestedRoute);
|
|
1849
|
-
layoutRoutesByPath.set(relativePath, nestedRoute);
|
|
1850
|
-
}
|
|
1851
|
-
function sortRouteChildren(children) {
|
|
1852
|
-
children.sort((a, b) => {
|
|
1853
|
-
if (a.routePath === "") return -1;
|
|
1854
|
-
if (b.routePath === "") return 1;
|
|
1855
|
-
return a.routePath.localeCompare(b.routePath);
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
function mergeWithoutRootLayout(appRoutes, layerRoutes) {
|
|
1859
|
-
const merged = [...appRoutes];
|
|
1860
|
-
const usedPaths = new Set(appRoutes.map((r) => r.routePath));
|
|
1861
|
-
for (const layerRoute of layerRoutes) if (!usedPaths.has(layerRoute.routePath)) {
|
|
1862
|
-
merged.push(layerRoute);
|
|
1863
|
-
usedPaths.add(layerRoute.routePath);
|
|
1864
|
-
}
|
|
1865
|
-
merged.sort((a, b) => a.routePath.localeCompare(b.routePath));
|
|
1866
|
-
return merged;
|
|
1918
|
+
function mergeRoutes$1(appRoutes, layerRoutes, _config) {
|
|
1919
|
+
const { mergeRoutes: mergeRoutesImpl } = (init_route_merger(), __toCommonJS(route_merger_exports));
|
|
1920
|
+
return mergeRoutesImpl(appRoutes, layerRoutes, _config);
|
|
1867
1921
|
}
|
|
1868
1922
|
|
|
1869
1923
|
//#endregion
|
|
1870
1924
|
//#region src/layout-resolver.ts
|
|
1925
|
+
init_route_merger();
|
|
1871
1926
|
/**
|
|
1872
1927
|
* @kimesh/router-generator - Layout Resolver
|
|
1873
1928
|
*
|
|
@@ -2025,4 +2080,283 @@ function generateLayoutStructure(chain) {
|
|
|
2025
2080
|
}
|
|
2026
2081
|
|
|
2027
2082
|
//#endregion
|
|
2028
|
-
|
|
2083
|
+
//#region src/middleware/scanner.ts
|
|
2084
|
+
/**
|
|
2085
|
+
* @kimesh/router-generator - Middleware Scanner
|
|
2086
|
+
*
|
|
2087
|
+
* Scans middleware directories for middleware files following conventions:
|
|
2088
|
+
* - `*.ts` or `*.js` files in `src/middleware/`
|
|
2089
|
+
* - `.global.ts` suffix for global middleware
|
|
2090
|
+
* - Numeric prefix for priority (e.g., `01.auth.global.ts`)
|
|
2091
|
+
*/
|
|
2092
|
+
const DEFAULT_EXTENSIONS = [
|
|
2093
|
+
".ts",
|
|
2094
|
+
".js",
|
|
2095
|
+
".mjs",
|
|
2096
|
+
".cjs"
|
|
2097
|
+
];
|
|
2098
|
+
/**
|
|
2099
|
+
* Extract priority from filename
|
|
2100
|
+
* Supports format: "01.auth.global.ts" -> priority 1
|
|
2101
|
+
*/
|
|
2102
|
+
function extractPriority(name) {
|
|
2103
|
+
const match = name.match(/^(\d+)\./);
|
|
2104
|
+
return match ? parseInt(match[1], 10) : void 0;
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Parse middleware filename into metadata
|
|
2108
|
+
*/
|
|
2109
|
+
function parseMiddlewareFile(filePath, fileName, layer) {
|
|
2110
|
+
const nameWithoutExt = basename(fileName, extname(fileName));
|
|
2111
|
+
const isGlobal = nameWithoutExt.endsWith(".global");
|
|
2112
|
+
const nameWithoutGlobal = isGlobal ? nameWithoutExt.slice(0, -7) : nameWithoutExt;
|
|
2113
|
+
const priority = extractPriority(nameWithoutGlobal);
|
|
2114
|
+
return {
|
|
2115
|
+
name: priority !== void 0 ? nameWithoutGlobal.replace(/^\d+\./, "") : nameWithoutGlobal,
|
|
2116
|
+
fileName,
|
|
2117
|
+
filePath,
|
|
2118
|
+
isGlobal,
|
|
2119
|
+
priority,
|
|
2120
|
+
layer
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* Scan a single middleware directory
|
|
2125
|
+
*/
|
|
2126
|
+
async function scanMiddlewareDir(middlewareDir, options = {}) {
|
|
2127
|
+
const extensions = options.extensions || DEFAULT_EXTENSIONS;
|
|
2128
|
+
const middlewareFiles = [];
|
|
2129
|
+
if (!existsSync(middlewareDir)) return [];
|
|
2130
|
+
try {
|
|
2131
|
+
const files = await readdir(middlewareDir, { withFileTypes: true });
|
|
2132
|
+
for (const file of files) {
|
|
2133
|
+
if (!file.isFile()) continue;
|
|
2134
|
+
const ext = extname(file.name);
|
|
2135
|
+
if (!extensions.includes(ext)) continue;
|
|
2136
|
+
const parsed = parseMiddlewareFile(join(middlewareDir, file.name), file.name, options.layer);
|
|
2137
|
+
middlewareFiles.push(parsed);
|
|
2138
|
+
}
|
|
2139
|
+
} catch (error) {
|
|
2140
|
+
console.warn(`[Kimesh] Could not scan middleware directory: ${middlewareDir}`, error);
|
|
2141
|
+
}
|
|
2142
|
+
return middlewareFiles;
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Sort middleware files according to the specified strategy
|
|
2146
|
+
*/
|
|
2147
|
+
function sortMiddleware(files, strategy) {
|
|
2148
|
+
if (strategy === "none") return files;
|
|
2149
|
+
return [...files].sort((a, b) => {
|
|
2150
|
+
if (a.isGlobal !== b.isGlobal) return a.isGlobal ? -1 : 1;
|
|
2151
|
+
if (strategy === "explicit") {
|
|
2152
|
+
const aPriority = a.priority ?? Infinity;
|
|
2153
|
+
const bPriority = b.priority ?? Infinity;
|
|
2154
|
+
if (aPriority !== bPriority) return aPriority - bPriority;
|
|
2155
|
+
}
|
|
2156
|
+
return a.name.localeCompare(b.name);
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* Scan middleware files from the application
|
|
2161
|
+
*/
|
|
2162
|
+
async function scanMiddlewareFiles(options) {
|
|
2163
|
+
const strategy = options.orderStrategy || "alphabetical";
|
|
2164
|
+
const extensions = options.extensions || DEFAULT_EXTENSIONS;
|
|
2165
|
+
const sorted = sortMiddleware(await scanMiddlewareDir(options.middlewareDir, { extensions }), strategy);
|
|
2166
|
+
return {
|
|
2167
|
+
middleware: sorted,
|
|
2168
|
+
globalMiddleware: sorted.filter((m) => m.isGlobal),
|
|
2169
|
+
namedMiddleware: sorted.filter((m) => !m.isGlobal)
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Scan middleware from multiple layers and merge them
|
|
2174
|
+
* Layer priority determines override behavior (lower = higher priority)
|
|
2175
|
+
*/
|
|
2176
|
+
async function scanLayerMiddleware(layers, options = {}) {
|
|
2177
|
+
const strategy = options.orderStrategy || "alphabetical";
|
|
2178
|
+
const extensions = options.extensions || DEFAULT_EXTENSIONS;
|
|
2179
|
+
const allMiddleware = [];
|
|
2180
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
2181
|
+
const sortedLayers = [...layers].sort((a, b) => b.priority - a.priority);
|
|
2182
|
+
for (const layer of sortedLayers) {
|
|
2183
|
+
const files = await scanMiddlewareDir(layer.middlewareDir, {
|
|
2184
|
+
extensions,
|
|
2185
|
+
layer: layer.layerName
|
|
2186
|
+
});
|
|
2187
|
+
for (const file of files) {
|
|
2188
|
+
const key = `${file.name}:${file.isGlobal}`;
|
|
2189
|
+
seenNames.set(key, file);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
allMiddleware.push(...seenNames.values());
|
|
2193
|
+
const sorted = sortMiddleware(allMiddleware, strategy);
|
|
2194
|
+
return {
|
|
2195
|
+
middleware: sorted,
|
|
2196
|
+
globalMiddleware: sorted.filter((m) => m.isGlobal),
|
|
2197
|
+
namedMiddleware: sorted.filter((m) => !m.isGlobal)
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Create a middleware scanner with configuration
|
|
2202
|
+
*/
|
|
2203
|
+
var MiddlewareScanner = class {
|
|
2204
|
+
options;
|
|
2205
|
+
constructor(options) {
|
|
2206
|
+
this.options = {
|
|
2207
|
+
extensions: DEFAULT_EXTENSIONS,
|
|
2208
|
+
orderStrategy: "alphabetical",
|
|
2209
|
+
...options
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
/**
|
|
2213
|
+
* Scan the configured middleware directory
|
|
2214
|
+
*/
|
|
2215
|
+
async scan() {
|
|
2216
|
+
return scanMiddlewareFiles(this.options);
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Check if middleware directory exists
|
|
2220
|
+
*/
|
|
2221
|
+
exists() {
|
|
2222
|
+
return existsSync(this.options.middlewareDir);
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
|
|
2226
|
+
//#endregion
|
|
2227
|
+
//#region src/middleware/codegen.ts
|
|
2228
|
+
/**
|
|
2229
|
+
* Generate a safe variable name from middleware name
|
|
2230
|
+
*/
|
|
2231
|
+
function toSafeVariableName(name) {
|
|
2232
|
+
return name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
2233
|
+
}
|
|
2234
|
+
/**
|
|
2235
|
+
* Normalize file path for import (forward slashes, no backslashes, no .ts extension)
|
|
2236
|
+
*/
|
|
2237
|
+
function normalizeImportPath(filePath) {
|
|
2238
|
+
return filePath.replace(/\\/g, "/").replace(/\.ts$/, "");
|
|
2239
|
+
}
|
|
2240
|
+
/**
|
|
2241
|
+
* Generate middleware registry code
|
|
2242
|
+
*/
|
|
2243
|
+
function generateMiddlewareRegistry(result) {
|
|
2244
|
+
const { globalMiddleware, namedMiddleware, middleware } = result;
|
|
2245
|
+
if (middleware.length === 0) return generateEmptyRegistry();
|
|
2246
|
+
const lines = [
|
|
2247
|
+
"// Auto-generated by @kimesh/router-generator",
|
|
2248
|
+
"// DO NOT EDIT - This file is regenerated when middleware files change",
|
|
2249
|
+
""
|
|
2250
|
+
];
|
|
2251
|
+
if (globalMiddleware.length > 0) {
|
|
2252
|
+
lines.push("// Global middleware imports (eager loading)");
|
|
2253
|
+
for (const mw of globalMiddleware) {
|
|
2254
|
+
const varName = `global_${toSafeVariableName(mw.name)}`;
|
|
2255
|
+
lines.push(`import ${varName} from '${normalizeImportPath(mw.filePath)}';`);
|
|
2256
|
+
}
|
|
2257
|
+
lines.push("");
|
|
2258
|
+
}
|
|
2259
|
+
lines.push("// Global middleware (executed on every navigation)");
|
|
2260
|
+
if (globalMiddleware.length > 0) {
|
|
2261
|
+
const globalNames = globalMiddleware.map((mw) => `global_${toSafeVariableName(mw.name)}`);
|
|
2262
|
+
lines.push(`export const globalMiddleware = [${globalNames.join(", ")}];`);
|
|
2263
|
+
} else lines.push("export const globalMiddleware = [];");
|
|
2264
|
+
lines.push("");
|
|
2265
|
+
lines.push("// Named middleware (lazy loaded when needed)");
|
|
2266
|
+
if (namedMiddleware.length > 0) {
|
|
2267
|
+
const namedEntries = namedMiddleware.map((mw) => {
|
|
2268
|
+
const importPath = normalizeImportPath(mw.filePath);
|
|
2269
|
+
return ` '${mw.name}': () => import('${importPath}').then(m => m.default || m)`;
|
|
2270
|
+
});
|
|
2271
|
+
lines.push(`export const namedMiddleware = {`);
|
|
2272
|
+
lines.push(namedEntries.join(",\n"));
|
|
2273
|
+
lines.push("};");
|
|
2274
|
+
} else lines.push("export const namedMiddleware = {};");
|
|
2275
|
+
lines.push("");
|
|
2276
|
+
const allNames = middleware.map((m) => `'${m.name}'`);
|
|
2277
|
+
lines.push("// Middleware names (for type safety)");
|
|
2278
|
+
lines.push(`export const middlewareNames = [${allNames.join(", ")}] as const;`);
|
|
2279
|
+
lines.push("export type MiddlewareName = typeof middlewareNames[number];");
|
|
2280
|
+
lines.push("");
|
|
2281
|
+
lines.push("// Helper functions");
|
|
2282
|
+
lines.push(`export function getGlobalMiddleware() {`);
|
|
2283
|
+
lines.push(` return globalMiddleware;`);
|
|
2284
|
+
lines.push(`}`);
|
|
2285
|
+
lines.push("");
|
|
2286
|
+
lines.push(`export function getNamedMiddleware(name: string) {`);
|
|
2287
|
+
lines.push(` return namedMiddleware[name as keyof typeof namedMiddleware];`);
|
|
2288
|
+
lines.push(`}`);
|
|
2289
|
+
lines.push("");
|
|
2290
|
+
lines.push(`export function hasMiddleware(name: string): boolean {`);
|
|
2291
|
+
lines.push(` return middlewareNames.includes(name as MiddlewareName);`);
|
|
2292
|
+
lines.push(`}`);
|
|
2293
|
+
return lines.join("\n");
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Generate empty middleware registry
|
|
2297
|
+
*/
|
|
2298
|
+
function generateEmptyRegistry() {
|
|
2299
|
+
return `// Auto-generated by @kimesh/router-generator
|
|
2300
|
+
// No middleware files found
|
|
2301
|
+
|
|
2302
|
+
export const globalMiddleware = [];
|
|
2303
|
+
export const namedMiddleware = {};
|
|
2304
|
+
export const middlewareNames = [] as const;
|
|
2305
|
+
export type MiddlewareName = never;
|
|
2306
|
+
|
|
2307
|
+
export function getGlobalMiddleware() {
|
|
2308
|
+
return [];
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
export function getNamedMiddleware(_name: string) {
|
|
2312
|
+
return undefined;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
export function hasMiddleware(_name: string): boolean {
|
|
2316
|
+
return false;
|
|
2317
|
+
}
|
|
2318
|
+
`;
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Generate middleware type declarations
|
|
2322
|
+
*/
|
|
2323
|
+
function generateMiddlewareTypes(result) {
|
|
2324
|
+
const { middleware } = result;
|
|
2325
|
+
const lines = [
|
|
2326
|
+
"// Auto-generated middleware type declarations",
|
|
2327
|
+
"// DO NOT EDIT - This file is regenerated when middleware files change",
|
|
2328
|
+
"",
|
|
2329
|
+
"import type { RouteMiddleware } from \"@kimesh/router-runtime\";",
|
|
2330
|
+
""
|
|
2331
|
+
];
|
|
2332
|
+
if (middleware.length > 0) {
|
|
2333
|
+
const names = middleware.map((m) => `'${m.name}'`).join(" | ");
|
|
2334
|
+
lines.push(`export type KnownMiddleware = ${names};`);
|
|
2335
|
+
} else lines.push("export type KnownMiddleware = never;");
|
|
2336
|
+
lines.push("");
|
|
2337
|
+
lines.push("declare module \"vue-router\" {");
|
|
2338
|
+
lines.push(" interface RouteMeta {");
|
|
2339
|
+
lines.push(" middleware?: KnownMiddleware | KnownMiddleware[] | RouteMiddleware | RouteMiddleware[];");
|
|
2340
|
+
lines.push(" }");
|
|
2341
|
+
lines.push("}");
|
|
2342
|
+
lines.push("");
|
|
2343
|
+
lines.push("declare module \"@kimesh/router-runtime\" {");
|
|
2344
|
+
lines.push(" interface KimeshMiddlewareNames {");
|
|
2345
|
+
for (const mw of middleware) lines.push(` '${mw.name}': true;`);
|
|
2346
|
+
lines.push(" }");
|
|
2347
|
+
lines.push("}");
|
|
2348
|
+
return lines.join("\n");
|
|
2349
|
+
}
|
|
2350
|
+
/**
|
|
2351
|
+
* Generate middleware module content for a specific middleware file
|
|
2352
|
+
*/
|
|
2353
|
+
function generateMiddlewareModule(file) {
|
|
2354
|
+
return `// Middleware: ${file.name}
|
|
2355
|
+
// Auto-generated export wrapper
|
|
2356
|
+
|
|
2357
|
+
export { default } from '${normalizeImportPath(file.filePath)}';
|
|
2358
|
+
`;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
//#endregion
|
|
2362
|
+
export { LayoutResolver, MiddlewareScanner, RouteScanner, RouteTreeBuilder, buildGlobPattern, buildRoutePath, buildRouteTree, collectLayerRoutes, createCatchAllParam, createDynamicParam, createEmptyParsedRoute, createScanner, createTreeBuilder, extractDynamicParam, generateLayoutStructure, generateMiddlewareModule, generateMiddlewareRegistry, generateMiddlewareTypes, generateRouteTypes, generateRoutes, isDotNotationRoute, isRootDir, kimeshRouterGenerator, mergeRoutes, parseRouteFile, processDirectoryPath, processPathSegment, scanLayerMiddleware, scanMiddlewareDir, scanMiddlewareFiles, unescapeBrackets };
|