@jay-framework/stack-route-scanner 0.9.0 → 0.11.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/dist/index.d.mts +37 -1
- package/dist/index.js +104 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -13,14 +13,50 @@ type JayRoute = {
|
|
|
13
13
|
rawRoute: string;
|
|
14
14
|
jayHtmlPath: string;
|
|
15
15
|
compPath: string;
|
|
16
|
+
/**
|
|
17
|
+
* For static override routes, inferred params from sibling dynamic routes.
|
|
18
|
+
* e.g., /products/ceramic-flower-vase has inferredParams: { slug: 'ceramic-flower-vase' }
|
|
19
|
+
* when /products/[slug] exists as a sibling.
|
|
20
|
+
*/
|
|
21
|
+
inferredParams?: Record<string, string>;
|
|
16
22
|
};
|
|
17
23
|
type JayRoutes = JayRoute[];
|
|
18
24
|
interface ScanFilesOptions {
|
|
19
25
|
jayHtmlFilename: string;
|
|
20
26
|
compFilename: string;
|
|
21
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Sort routes by priority so that more specific routes match first.
|
|
30
|
+
* Static routes come before dynamic routes at the same position.
|
|
31
|
+
*/
|
|
32
|
+
declare function sortRoutesByPriority(routes: JayRoutes): JayRoutes;
|
|
33
|
+
/**
|
|
34
|
+
* Result of param inference for logging/debugging.
|
|
35
|
+
*/
|
|
36
|
+
interface ParamInferenceResult {
|
|
37
|
+
staticRoute: string;
|
|
38
|
+
dynamicRoute: string;
|
|
39
|
+
inferredParams: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Infer params for static routes based on sibling dynamic routes.
|
|
43
|
+
*
|
|
44
|
+
* For each fully-static route, find a sibling dynamic route and map
|
|
45
|
+
* the static segment values to the param names from the dynamic route.
|
|
46
|
+
*
|
|
47
|
+
* Example:
|
|
48
|
+
* /products/ceramic-flower-vase (static)
|
|
49
|
+
* /products/[slug] (dynamic sibling)
|
|
50
|
+
* → inferredParams: { slug: 'ceramic-flower-vase' }
|
|
51
|
+
*
|
|
52
|
+
* Returns the routes with inferred params added, plus an inference log.
|
|
53
|
+
*/
|
|
54
|
+
declare function inferParamsForStaticRoutes(routes: JayRoutes): {
|
|
55
|
+
routes: JayRoutes;
|
|
56
|
+
inferenceLog: ParamInferenceResult[];
|
|
57
|
+
};
|
|
22
58
|
declare function scanRoutes(baseDir: string, options: ScanFilesOptions): Promise<JayRoutes>;
|
|
23
59
|
|
|
24
60
|
declare function routeToExpressRoute(route: JayRoute): string;
|
|
25
61
|
|
|
26
|
-
export { type JayRoute, type JayRouteParam, JayRouteParamType, type JayRouteSegment, type JayRoutes, type ScanFilesOptions, routeToExpressRoute, scanRoutes };
|
|
62
|
+
export { type JayRoute, type JayRouteParam, JayRouteParamType, type JayRouteSegment, type JayRoutes, type ParamInferenceResult, type ScanFilesOptions, inferParamsForStaticRoutes, routeToExpressRoute, scanRoutes, sortRoutesByPriority };
|
package/dist/index.js
CHANGED
|
@@ -41,9 +41,110 @@ async function scanDirectory(BASE_DIR, directory, options) {
|
|
|
41
41
|
}
|
|
42
42
|
return routes;
|
|
43
43
|
}
|
|
44
|
+
function getSegmentPriority(segment) {
|
|
45
|
+
if (typeof segment === "string")
|
|
46
|
+
return 0;
|
|
47
|
+
switch (segment.type) {
|
|
48
|
+
case 0:
|
|
49
|
+
return 1;
|
|
50
|
+
case 2:
|
|
51
|
+
return 2;
|
|
52
|
+
case 1:
|
|
53
|
+
return 3;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function compareRoutes(a, b) {
|
|
57
|
+
const maxLen = Math.max(a.segments.length, b.segments.length);
|
|
58
|
+
for (let i = 0; i < maxLen; i++) {
|
|
59
|
+
const segA = a.segments[i];
|
|
60
|
+
const segB = b.segments[i];
|
|
61
|
+
if (segA === void 0)
|
|
62
|
+
return 1;
|
|
63
|
+
if (segB === void 0)
|
|
64
|
+
return -1;
|
|
65
|
+
const priorityA = getSegmentPriority(segA);
|
|
66
|
+
const priorityB = getSegmentPriority(segB);
|
|
67
|
+
if (priorityA !== priorityB)
|
|
68
|
+
return priorityA - priorityB;
|
|
69
|
+
if (typeof segA === "string" && typeof segB === "string") {
|
|
70
|
+
const cmp = segA.localeCompare(segB);
|
|
71
|
+
if (cmp !== 0)
|
|
72
|
+
return cmp;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
function sortRoutesByPriority(routes) {
|
|
78
|
+
return [...routes].sort(compareRoutes);
|
|
79
|
+
}
|
|
80
|
+
function hasDynamicSegments(route) {
|
|
81
|
+
return route.segments.some((seg) => typeof seg !== "string");
|
|
82
|
+
}
|
|
83
|
+
function isFullyStaticRoute(route) {
|
|
84
|
+
return route.segments.every((seg) => typeof seg === "string");
|
|
85
|
+
}
|
|
86
|
+
function dynamicRouteCouldMatch(staticRoute, dynamicRoute) {
|
|
87
|
+
if (staticRoute.segments.length !== dynamicRoute.segments.length)
|
|
88
|
+
return false;
|
|
89
|
+
if (staticRoute.segments.length === 0)
|
|
90
|
+
return false;
|
|
91
|
+
for (let i = 0; i < staticRoute.segments.length; i++) {
|
|
92
|
+
const staticSeg = staticRoute.segments[i];
|
|
93
|
+
const dynSeg = dynamicRoute.segments[i];
|
|
94
|
+
if (typeof staticSeg !== "string")
|
|
95
|
+
return false;
|
|
96
|
+
if (typeof dynSeg === "string") {
|
|
97
|
+
if (staticSeg !== dynSeg)
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
function inferParamsForStaticRoutes(routes) {
|
|
104
|
+
const inferenceLog = [];
|
|
105
|
+
const dynamicRoutes = routes.filter(hasDynamicSegments);
|
|
106
|
+
const enrichedRoutes = routes.map((route) => {
|
|
107
|
+
if (!isFullyStaticRoute(route))
|
|
108
|
+
return route;
|
|
109
|
+
if (route.segments.length === 0)
|
|
110
|
+
return route;
|
|
111
|
+
const dynamicSibling = dynamicRoutes.find((dyn) => dynamicRouteCouldMatch(route, dyn));
|
|
112
|
+
if (!dynamicSibling)
|
|
113
|
+
return route;
|
|
114
|
+
const inferredParams = {};
|
|
115
|
+
for (let i = 0; i < route.segments.length; i++) {
|
|
116
|
+
const staticSeg = route.segments[i];
|
|
117
|
+
const dynSeg = dynamicSibling.segments[i];
|
|
118
|
+
if (typeof staticSeg === "string" && typeof dynSeg !== "string") {
|
|
119
|
+
inferredParams[dynSeg.name] = staticSeg;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (Object.keys(inferredParams).length === 0)
|
|
123
|
+
return route;
|
|
124
|
+
inferenceLog.push({
|
|
125
|
+
staticRoute: route.rawRoute,
|
|
126
|
+
dynamicRoute: dynamicSibling.rawRoute,
|
|
127
|
+
inferredParams
|
|
128
|
+
});
|
|
129
|
+
return { ...route, inferredParams };
|
|
130
|
+
});
|
|
131
|
+
return { routes: enrichedRoutes, inferenceLog };
|
|
132
|
+
}
|
|
44
133
|
async function scanRoutes(baseDir, options) {
|
|
45
134
|
const BASE_DIR = path.resolve(baseDir);
|
|
46
|
-
|
|
135
|
+
const routes = await scanDirectory(BASE_DIR, BASE_DIR, options);
|
|
136
|
+
const sortedRoutes = sortRoutesByPriority(routes);
|
|
137
|
+
const { routes: enrichedRoutes, inferenceLog } = inferParamsForStaticRoutes(sortedRoutes);
|
|
138
|
+
if (inferenceLog.length > 0) {
|
|
139
|
+
console.log("[route-scanner] Inferred params for static override routes:");
|
|
140
|
+
for (const entry of inferenceLog) {
|
|
141
|
+
console.log(
|
|
142
|
+
` ${entry.staticRoute} → params from ${entry.dynamicRoute}:`,
|
|
143
|
+
entry.inferredParams
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return enrichedRoutes;
|
|
47
148
|
}
|
|
48
149
|
function routeToExpressRoute(route) {
|
|
49
150
|
return "/" + route.segments.map((segment) => {
|
|
@@ -60,5 +161,7 @@ function routeToExpressRoute(route) {
|
|
|
60
161
|
}).join("/");
|
|
61
162
|
}
|
|
62
163
|
exports.JayRouteParamType = JayRouteParamType;
|
|
164
|
+
exports.inferParamsForStaticRoutes = inferParamsForStaticRoutes;
|
|
63
165
|
exports.routeToExpressRoute = routeToExpressRoute;
|
|
64
166
|
exports.scanRoutes = scanRoutes;
|
|
167
|
+
exports.sortRoutesByPriority = sortRoutesByPriority;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-route-scanner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.mts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test:watch": "vitest"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@jay-framework/dev-environment": "^0.
|
|
23
|
+
"@jay-framework/dev-environment": "^0.11.0",
|
|
24
24
|
"@types/node": "^20.11.5",
|
|
25
25
|
"nodemon": "^3.0.3",
|
|
26
26
|
"replace-in-file": "^7.1.0",
|