@jay-framework/stack-route-scanner 0.21.0 → 0.22.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 +1 -1
- package/dist/index.mjs +50 -23
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -18,7 +18,7 @@ type JayRoute = {
|
|
|
18
18
|
/** NPM package name for plugin routes. Used by build to generate portable module paths. */
|
|
19
19
|
packageName?: string;
|
|
20
20
|
/**
|
|
21
|
-
* Explicit params declared via
|
|
21
|
+
* Explicit params declared via YAML body in headless component script tags (DL#156).
|
|
22
22
|
* Used by static override routes to provide param values.
|
|
23
23
|
* e.g., /products/ceramic-flower-vase declares { slug: 'ceramic-flower-vase' }.
|
|
24
24
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -41,9 +41,27 @@ async function scanDirectory(BASE_DIR, directory, options) {
|
|
|
41
41
|
routes = [...routes, ...await scanDirectory(BASE_DIR, fullPath, options)];
|
|
42
42
|
} else if (item.name === options.jayHtmlFilename) {
|
|
43
43
|
const route = convertToRoutePath(BASE_DIR, fullPath, options);
|
|
44
|
-
const { params, validations } = await
|
|
44
|
+
const { params, validations } = await parseHeadlessProps(fullPath);
|
|
45
45
|
if (params) {
|
|
46
|
-
route.
|
|
46
|
+
const hasDynamicSegments = route.segments.some((s) => typeof s !== "string");
|
|
47
|
+
if (hasDynamicSegments) {
|
|
48
|
+
const dynamicParamNames = new Set(
|
|
49
|
+
route.segments.filter(
|
|
50
|
+
(s) => typeof s !== "string"
|
|
51
|
+
).map((s) => s.name)
|
|
52
|
+
);
|
|
53
|
+
const routeParams = {};
|
|
54
|
+
for (const [k, v] of Object.entries(params)) {
|
|
55
|
+
if (dynamicParamNames.has(k)) {
|
|
56
|
+
routeParams[k] = v;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (Object.keys(routeParams).length > 0) {
|
|
60
|
+
route.inferredParams = routeParams;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
route.inferredParams = params;
|
|
64
|
+
}
|
|
47
65
|
}
|
|
48
66
|
if (validations.length > 0) {
|
|
49
67
|
for (const v of validations) {
|
|
@@ -98,7 +116,7 @@ function dedentYaml(text) {
|
|
|
98
116
|
const minIndent = Math.min(...lines.map((l) => l.match(/^\s*/)?.[0].length ?? 0));
|
|
99
117
|
return lines.map((l) => l.slice(minIndent)).join("\n");
|
|
100
118
|
}
|
|
101
|
-
async function
|
|
119
|
+
async function parseHeadlessProps(jayHtmlPath) {
|
|
102
120
|
let content;
|
|
103
121
|
try {
|
|
104
122
|
content = await promises.readFile(jayHtmlPath, "utf-8");
|
|
@@ -110,28 +128,37 @@ async function parseJayParams(jayHtmlPath) {
|
|
|
110
128
|
blockTextElements: { script: true, style: true }
|
|
111
129
|
});
|
|
112
130
|
const head = root.querySelector("head");
|
|
131
|
+
const validations = [];
|
|
113
132
|
const paramScripts = (head ?? root).querySelectorAll('script[type="application/jay-params"]');
|
|
114
|
-
if (paramScripts.length
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
params: void 0,
|
|
119
|
-
validations: [
|
|
120
|
-
'Multiple <script type="application/jay-params"> tags found — expected at most one'
|
|
121
|
-
]
|
|
122
|
-
};
|
|
133
|
+
if (paramScripts.length > 0) {
|
|
134
|
+
validations.push(
|
|
135
|
+
'<script type="application/jay-params"> is deprecated. Move the values into the YAML body of the headless component that uses them. See agent-kit/developer/routing.md for details.'
|
|
136
|
+
);
|
|
123
137
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
138
|
+
const headlessScripts = (head ?? root).querySelectorAll(
|
|
139
|
+
'script[type="application/jay-headless"]'
|
|
140
|
+
);
|
|
141
|
+
let merged;
|
|
142
|
+
for (const script of headlessScripts) {
|
|
143
|
+
const body = dedentYaml(script.textContent ?? "");
|
|
144
|
+
if (!body)
|
|
145
|
+
continue;
|
|
146
|
+
try {
|
|
147
|
+
const parsed = YAML.parse(body);
|
|
148
|
+
if (parsed && typeof parsed === "object") {
|
|
149
|
+
if (!merged)
|
|
150
|
+
merged = {};
|
|
151
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
152
|
+
merged[k] = String(v);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
validations.push(
|
|
157
|
+
`Failed to parse headless component props YAML: ${e.message}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
134
160
|
}
|
|
161
|
+
return { params: merged, validations };
|
|
135
162
|
}
|
|
136
163
|
function parseRouteSegments(routePath) {
|
|
137
164
|
return routePath.split("/").filter((s) => s.length > 0).map((segment) => {
|
|
@@ -160,7 +187,7 @@ async function scanRoutes(baseDir, options) {
|
|
|
160
187
|
const sortedRoutes = sortRoutesByPriority(routes);
|
|
161
188
|
const routesWithParams = sortedRoutes.filter((r) => r.inferredParams);
|
|
162
189
|
if (routesWithParams.length > 0) {
|
|
163
|
-
getLogger().info("[route-scanner] Routes with explicit params (
|
|
190
|
+
getLogger().info("[route-scanner] Routes with explicit params (headless props):");
|
|
164
191
|
for (const route of routesWithParams) {
|
|
165
192
|
getLogger().info(` ${route.rawRoute} → ${JSON.stringify(route.inferredParams)}`);
|
|
166
193
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-route-scanner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.mts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"readme.md"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@jay-framework/logger": "^0.
|
|
12
|
+
"@jay-framework/logger": "^0.22.0",
|
|
13
13
|
"node-html-parser": "^6.1.12",
|
|
14
14
|
"yaml": "^2.3.4"
|
|
15
15
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"test:watch": "vitest"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@jay-framework/dev-environment": "^0.
|
|
28
|
+
"@jay-framework/dev-environment": "^0.22.0",
|
|
29
29
|
"@types/node": "^20.11.5",
|
|
30
30
|
"nodemon": "^3.0.3",
|
|
31
31
|
"replace-in-file": "^7.1.0",
|