@jay-framework/jay-stack-cli 0.23.0 → 0.24.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/agent-kit-template/developer/component-refs.md +129 -12
- package/agent-kit-template/plugin/add-menu-guide.md +17 -17
- package/dist/index.d.ts +4 -1
- package/dist/index.js +317 -221
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -7,8 +7,8 @@ import path from "path";
|
|
|
7
7
|
import fs, { promises } from "fs";
|
|
8
8
|
import YAML from "yaml";
|
|
9
9
|
import { getLogger, setDevLogger, createDevLogger } from "@jay-framework/logger";
|
|
10
|
-
import {
|
|
11
|
-
import { scanPlugins, listContracts, materializeContracts, SetupNeedsAnswerError, discoverPluginsWithSetup,
|
|
10
|
+
import { parseJayFile, JAY_IMPORT_RESOLVER, generateElementDefinitionFile, parseContract, generateElementFile, generateServerElementFile, htmlElementTagNameMap, loadLinkedContract, getLinkedContractDir } from "@jay-framework/compiler-jay-html";
|
|
11
|
+
import { scanPlugins, listContracts, materializeContracts, SetupNeedsAnswerError, discoverPluginsWithSetup, sortPluginsByDependencies, discoverPluginsWithInit, executePluginSetup, executePluginServerInits, runInitCallbacks } from "@jay-framework/stack-server-runtime";
|
|
12
12
|
import { listContracts as listContracts2, materializeContracts as materializeContracts2 } from "@jay-framework/stack-server-runtime";
|
|
13
13
|
import { Command } from "commander";
|
|
14
14
|
import chalk from "chalk";
|
|
@@ -19,7 +19,7 @@ import { createRequire } from "module";
|
|
|
19
19
|
import { glob } from "glob";
|
|
20
20
|
import fsSync from "node:fs";
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
|
-
import {
|
|
22
|
+
import { select, confirm, input } from "@inquirer/prompts";
|
|
23
23
|
const DEFAULT_CONFIG = {
|
|
24
24
|
devServer: {
|
|
25
25
|
portRange: [3e3, 3100],
|
|
@@ -41,7 +41,8 @@ function loadConfig() {
|
|
|
41
41
|
devServer: {
|
|
42
42
|
...DEFAULT_CONFIG.devServer,
|
|
43
43
|
...userConfig.devServer
|
|
44
|
-
}
|
|
44
|
+
},
|
|
45
|
+
site: userConfig.site
|
|
45
46
|
};
|
|
46
47
|
} catch (error) {
|
|
47
48
|
getLogger().warn(`Failed to parse .jay YAML config file, using defaults: ${error}`);
|
|
@@ -56,7 +57,8 @@ function getConfigWithDefaults(config) {
|
|
|
56
57
|
componentsBase: config.devServer?.componentsBase || DEFAULT_CONFIG.devServer.componentsBase,
|
|
57
58
|
publicFolder: config.devServer?.publicFolder || DEFAULT_CONFIG.devServer.publicFolder,
|
|
58
59
|
configBase: config.devServer?.configBase || DEFAULT_CONFIG.devServer.configBase
|
|
59
|
-
}
|
|
60
|
+
},
|
|
61
|
+
site: config.site
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
function updateConfig(updates) {
|
|
@@ -69,6 +71,10 @@ function updateConfig(updates) {
|
|
|
69
71
|
devServer: {
|
|
70
72
|
...existingConfig.devServer,
|
|
71
73
|
...updates.devServer
|
|
74
|
+
},
|
|
75
|
+
site: {
|
|
76
|
+
...existingConfig.site,
|
|
77
|
+
...updates.site
|
|
72
78
|
}
|
|
73
79
|
};
|
|
74
80
|
const yamlContent = YAML.stringify(updatedConfig, { indent: 2 });
|
|
@@ -146,6 +152,13 @@ async function startDevServer(options = {}) {
|
|
|
146
152
|
});
|
|
147
153
|
app.use(server);
|
|
148
154
|
const publicPath = path.resolve(resolvedConfig.devServer.publicFolder);
|
|
155
|
+
if (!fs.existsSync(path.join(publicPath, "sitemap.xml"))) {
|
|
156
|
+
app.get("/sitemap.xml", (_req, res) => {
|
|
157
|
+
res.type("application/xml").send(
|
|
158
|
+
'<?xml version="1.0" encoding="UTF-8"?>\n<!-- Sitemap is generated by the production server from the route manifest. -->\n<!-- Run jay-stack build && jay-stack serve to see the full sitemap. -->\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />\n'
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
149
162
|
if (fs.existsSync(publicPath)) {
|
|
150
163
|
app.use(express.static(publicPath));
|
|
151
164
|
} else {
|
|
@@ -264,9 +277,11 @@ async function resolveProductionContext(projectPath, versionOverride) {
|
|
|
264
277
|
const resolvedPath = path$1.resolve(projectPath || process.cwd());
|
|
265
278
|
const jayConfigPath = path$1.join(resolvedPath, ".jay");
|
|
266
279
|
let pagesBase = "./src/pages";
|
|
280
|
+
let siteBaseUrl;
|
|
267
281
|
try {
|
|
268
282
|
const jayConfig = YAML.parse(await fs$1.readFile(jayConfigPath, "utf-8"));
|
|
269
283
|
pagesBase = jayConfig?.devServer?.pagesBase || pagesBase;
|
|
284
|
+
siteBaseUrl = jayConfig?.site?.baseUrl;
|
|
270
285
|
} catch {
|
|
271
286
|
}
|
|
272
287
|
const version = versionOverride || await resolveVersionFromPackageJson(resolvedPath);
|
|
@@ -275,7 +290,8 @@ async function resolveProductionContext(projectPath, versionOverride) {
|
|
|
275
290
|
pagesRoot: path$1.resolve(resolvedPath, pagesBase),
|
|
276
291
|
buildRoot: path$1.join(resolvedPath, "build"),
|
|
277
292
|
version,
|
|
278
|
-
tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json")
|
|
293
|
+
tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json"),
|
|
294
|
+
siteBaseUrl
|
|
279
295
|
};
|
|
280
296
|
}
|
|
281
297
|
async function resolveVersionFromPackageJson(projectRoot) {
|
|
@@ -305,7 +321,8 @@ async function runBuild(projectPath, options) {
|
|
|
305
321
|
buildRoot: ctx.buildRoot,
|
|
306
322
|
concurrency: 4,
|
|
307
323
|
tsConfigFilePath: ctx.tsConfigFilePath,
|
|
308
|
-
minify: options.minify
|
|
324
|
+
minify: options.minify,
|
|
325
|
+
siteBaseUrl: ctx.siteBaseUrl
|
|
309
326
|
});
|
|
310
327
|
}
|
|
311
328
|
async function runServe(projectPath, options) {
|
|
@@ -319,7 +336,8 @@ async function runServe(projectPath, options) {
|
|
|
319
336
|
port: parseInt(options.port, 10),
|
|
320
337
|
projectRoot: ctx.resolvedPath,
|
|
321
338
|
pagesRoot: ctx.pagesRoot,
|
|
322
|
-
tsConfigFilePath: ctx.tsConfigFilePath
|
|
339
|
+
tsConfigFilePath: ctx.tsConfigFilePath,
|
|
340
|
+
siteBaseUrl: ctx.siteBaseUrl
|
|
323
341
|
});
|
|
324
342
|
} else {
|
|
325
343
|
const { startMainServer } = await import("@jay-framework/production-server");
|
|
@@ -358,7 +376,8 @@ async function runRebuild(projectPath, options) {
|
|
|
358
376
|
buildRoot: ctx.buildRoot,
|
|
359
377
|
version: ctx.version,
|
|
360
378
|
target,
|
|
361
|
-
tsConfigFilePath: ctx.tsConfigFilePath
|
|
379
|
+
tsConfigFilePath: ctx.tsConfigFilePath,
|
|
380
|
+
siteBaseUrl: ctx.siteBaseUrl
|
|
362
381
|
});
|
|
363
382
|
if (result.errors.length > 0) {
|
|
364
383
|
for (const err of result.errors) {
|
|
@@ -464,17 +483,13 @@ function collectLocalInterfaces(sourceFile) {
|
|
|
464
483
|
function collectContractImportedTypes(sourceFile) {
|
|
465
484
|
const contractTypes = /* @__PURE__ */ new Set();
|
|
466
485
|
for (const statement of sourceFile.statements) {
|
|
467
|
-
if (!u.isImportDeclaration(statement))
|
|
468
|
-
continue;
|
|
486
|
+
if (!u.isImportDeclaration(statement)) continue;
|
|
469
487
|
const moduleSpecifier = statement.moduleSpecifier;
|
|
470
|
-
if (!u.isStringLiteral(moduleSpecifier))
|
|
471
|
-
continue;
|
|
488
|
+
if (!u.isStringLiteral(moduleSpecifier)) continue;
|
|
472
489
|
const modulePath = moduleSpecifier.text;
|
|
473
|
-
if (!modulePath.includes(".jay-contract"))
|
|
474
|
-
continue;
|
|
490
|
+
if (!modulePath.includes(".jay-contract")) continue;
|
|
475
491
|
const importClause = statement.importClause;
|
|
476
|
-
if (!importClause)
|
|
477
|
-
continue;
|
|
492
|
+
if (!importClause) continue;
|
|
478
493
|
const namedBindings = importClause.namedBindings;
|
|
479
494
|
if (namedBindings && u.isNamedImports(namedBindings)) {
|
|
480
495
|
for (const element of namedBindings.elements) {
|
|
@@ -510,8 +525,7 @@ function visitNode(node, result) {
|
|
|
510
525
|
}
|
|
511
526
|
if (node.arguments.length > 0) {
|
|
512
527
|
const arg = node.arguments[0];
|
|
513
|
-
if (u.isIdentifier(arg))
|
|
514
|
-
;
|
|
528
|
+
if (u.isIdentifier(arg)) ;
|
|
515
529
|
}
|
|
516
530
|
}
|
|
517
531
|
}
|
|
@@ -533,10 +547,8 @@ function extractTypeNames(typeNode) {
|
|
|
533
547
|
return [];
|
|
534
548
|
}
|
|
535
549
|
function checkPropsConsistency(propsTypeName, localInterfaces, contractImportedTypes, contract, contractName, contractPath, sourcePath, errors, warnings) {
|
|
536
|
-
if (FRAMEWORK_PROP_TYPES.has(propsTypeName))
|
|
537
|
-
|
|
538
|
-
if (contractImportedTypes.has(propsTypeName))
|
|
539
|
-
return;
|
|
550
|
+
if (FRAMEWORK_PROP_TYPES.has(propsTypeName)) return;
|
|
551
|
+
if (contractImportedTypes.has(propsTypeName)) return;
|
|
540
552
|
const prefix = `[${contractName}]`;
|
|
541
553
|
const iface = localInterfaces.get(propsTypeName);
|
|
542
554
|
if (!iface) {
|
|
@@ -551,8 +563,7 @@ function checkPropsConsistency(propsTypeName, localInterfaces, contractImportedT
|
|
|
551
563
|
return;
|
|
552
564
|
}
|
|
553
565
|
const ownProperties = iface.properties;
|
|
554
|
-
if (ownProperties.length === 0)
|
|
555
|
-
return;
|
|
566
|
+
if (ownProperties.length === 0) return;
|
|
556
567
|
if (!contract.props || contract.props.length === 0) {
|
|
557
568
|
errors.push({
|
|
558
569
|
type: "contract-invalid",
|
|
@@ -596,10 +607,8 @@ function checkParamsConsistency(paramsTypeNames, localInterfaces, contractImport
|
|
|
596
607
|
suggestion: `Add a params section to the contract (e.g., params: { slug: string })`
|
|
597
608
|
});
|
|
598
609
|
for (const typeName of paramsTypeNames) {
|
|
599
|
-
if (FRAMEWORK_PROP_TYPES.has(typeName))
|
|
600
|
-
|
|
601
|
-
if (contractImportedTypes.has(typeName))
|
|
602
|
-
continue;
|
|
610
|
+
if (FRAMEWORK_PROP_TYPES.has(typeName)) continue;
|
|
611
|
+
if (contractImportedTypes.has(typeName)) continue;
|
|
603
612
|
const iface = localInterfaces.get(typeName);
|
|
604
613
|
if (iface) {
|
|
605
614
|
const ownProps = iface.properties;
|
|
@@ -612,13 +621,10 @@ function checkParamsConsistency(paramsTypeNames, localInterfaces, contractImport
|
|
|
612
621
|
return;
|
|
613
622
|
}
|
|
614
623
|
for (const typeName of paramsTypeNames) {
|
|
615
|
-
if (FRAMEWORK_PROP_TYPES.has(typeName))
|
|
616
|
-
|
|
617
|
-
if (contractImportedTypes.has(typeName))
|
|
618
|
-
continue;
|
|
624
|
+
if (FRAMEWORK_PROP_TYPES.has(typeName)) continue;
|
|
625
|
+
if (contractImportedTypes.has(typeName)) continue;
|
|
619
626
|
const iface = localInterfaces.get(typeName);
|
|
620
|
-
if (!iface)
|
|
621
|
-
continue;
|
|
627
|
+
if (!iface) continue;
|
|
622
628
|
const ownProperties = iface.properties;
|
|
623
629
|
const contractParamNames = new Set(contract.params.map((p) => p.name));
|
|
624
630
|
for (const prop of ownProperties) {
|
|
@@ -710,16 +716,13 @@ function requiredString(obj, field, itemPath, errors, code) {
|
|
|
710
716
|
}
|
|
711
717
|
function optionalString(obj, field) {
|
|
712
718
|
const value = obj[field];
|
|
713
|
-
if (value === void 0)
|
|
714
|
-
|
|
715
|
-
if (typeof value !== "string")
|
|
716
|
-
return void 0;
|
|
719
|
+
if (value === void 0) return void 0;
|
|
720
|
+
if (typeof value !== "string") return void 0;
|
|
717
721
|
const trimmed = value.trim();
|
|
718
722
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
719
723
|
}
|
|
720
724
|
function validateInteraction(raw, itemPath, errors) {
|
|
721
|
-
if (raw === void 0)
|
|
722
|
-
return void 0;
|
|
725
|
+
if (raw === void 0) return void 0;
|
|
723
726
|
if (!isRecord$1(raw)) {
|
|
724
727
|
errors.push({
|
|
725
728
|
path: itemPath,
|
|
@@ -743,8 +746,7 @@ function validateInteraction(raw, itemPath, errors) {
|
|
|
743
746
|
};
|
|
744
747
|
}
|
|
745
748
|
function validatePresentation(raw, itemPath, errors) {
|
|
746
|
-
if (raw === void 0)
|
|
747
|
-
return void 0;
|
|
749
|
+
if (raw === void 0) return void 0;
|
|
748
750
|
if (!isRecord$1(raw)) {
|
|
749
751
|
errors.push({
|
|
750
752
|
path: itemPath,
|
|
@@ -764,14 +766,12 @@ function validatePresentation(raw, itemPath, errors) {
|
|
|
764
766
|
}
|
|
765
767
|
if (type === "image") {
|
|
766
768
|
const src = requiredString(raw, "src", itemPath, errors, "presentation-missing-src");
|
|
767
|
-
if (!src)
|
|
768
|
-
return void 0;
|
|
769
|
+
if (!src) return void 0;
|
|
769
770
|
return { type: "image", src };
|
|
770
771
|
}
|
|
771
772
|
if (type === "gif") {
|
|
772
773
|
const src = requiredString(raw, "src", itemPath, errors, "presentation-missing-src");
|
|
773
|
-
if (!src)
|
|
774
|
-
return void 0;
|
|
774
|
+
if (!src) return void 0;
|
|
775
775
|
return {
|
|
776
776
|
type: "gif",
|
|
777
777
|
src,
|
|
@@ -816,8 +816,7 @@ function validatePresentation(raw, itemPath, errors) {
|
|
|
816
816
|
}
|
|
817
817
|
const BROWSE_SIZES = /* @__PURE__ */ new Set(["large", "medium", "small"]);
|
|
818
818
|
function validateBrowse(raw, itemPath, errors) {
|
|
819
|
-
if (raw === void 0)
|
|
820
|
-
return void 0;
|
|
819
|
+
if (raw === void 0) return void 0;
|
|
821
820
|
if (!isRecord$1(raw)) {
|
|
822
821
|
errors.push({
|
|
823
822
|
path: itemPath,
|
|
@@ -841,8 +840,7 @@ function validateBrowse(raw, itemPath, errors) {
|
|
|
841
840
|
return { size: sizeRaw };
|
|
842
841
|
}
|
|
843
842
|
function validateFolderPath(raw, itemPath, errors) {
|
|
844
|
-
if (raw === void 0)
|
|
845
|
-
return void 0;
|
|
843
|
+
if (raw === void 0) return void 0;
|
|
846
844
|
if (!Array.isArray(raw)) {
|
|
847
845
|
errors.push({
|
|
848
846
|
path: `${itemPath}.folderPath`,
|
|
@@ -980,8 +978,7 @@ function validateAddMenuCatalogFile(raw, sourcePath) {
|
|
|
980
978
|
raw.items.forEach((entry, index) => {
|
|
981
979
|
const result = validateAddMenuItem(entry, `${sourcePath}.items[${index}]`);
|
|
982
980
|
errors.push(...result.errors);
|
|
983
|
-
if (result.item)
|
|
984
|
-
items.push(result.item);
|
|
981
|
+
if (result.item) items.push(result.item);
|
|
985
982
|
});
|
|
986
983
|
if (items.length === 0 && errors.length > 0) {
|
|
987
984
|
return { file: null, errors };
|
|
@@ -989,11 +986,9 @@ function validateAddMenuCatalogFile(raw, sourcePath) {
|
|
|
989
986
|
return { file: { items }, errors };
|
|
990
987
|
}
|
|
991
988
|
function normalizeAddMenuPresentation(item) {
|
|
992
|
-
if (item.presentation)
|
|
993
|
-
return item.presentation;
|
|
989
|
+
if (item.presentation) return item.presentation;
|
|
994
990
|
const thumbnail = item.thumbnail?.trim();
|
|
995
|
-
if (!thumbnail)
|
|
996
|
-
return void 0;
|
|
991
|
+
if (!thumbnail) return void 0;
|
|
997
992
|
if (/\.gif$/i.test(thumbnail)) {
|
|
998
993
|
return { type: "gif", src: thumbnail };
|
|
999
994
|
}
|
|
@@ -1004,15 +999,12 @@ function normalizeAddMenuBrowseSize(item) {
|
|
|
1004
999
|
}
|
|
1005
1000
|
function hasSingleRootDiv(html) {
|
|
1006
1001
|
const trimmed = html.trim();
|
|
1007
|
-
if (!trimmed.startsWith("<div"))
|
|
1008
|
-
return false;
|
|
1002
|
+
if (!trimmed.startsWith("<div")) return false;
|
|
1009
1003
|
const openMatch = trimmed.match(/^<div\b[^>]*>/i);
|
|
1010
|
-
if (!openMatch)
|
|
1011
|
-
return false;
|
|
1004
|
+
if (!openMatch) return false;
|
|
1012
1005
|
const afterOpen = trimmed.slice(openMatch[0].length);
|
|
1013
1006
|
const closeIdx = afterOpen.lastIndexOf("</div>");
|
|
1014
|
-
if (closeIdx < 0)
|
|
1015
|
-
return false;
|
|
1007
|
+
if (closeIdx < 0) return false;
|
|
1016
1008
|
const tail = afterOpen.slice(closeIdx + "</div>".length).trim();
|
|
1017
1009
|
return tail.length === 0;
|
|
1018
1010
|
}
|
|
@@ -1088,8 +1080,7 @@ function lintHtmlFragment(item, sourcePath) {
|
|
|
1088
1080
|
}
|
|
1089
1081
|
function lintGifPoster(item, sourcePath) {
|
|
1090
1082
|
const presentation = normalizeAddMenuPresentation(item);
|
|
1091
|
-
if (presentation?.type !== "gif" || presentation.poster?.trim())
|
|
1092
|
-
return [];
|
|
1083
|
+
if (presentation?.type !== "gif" || presentation.poster?.trim()) return [];
|
|
1093
1084
|
return [
|
|
1094
1085
|
catalogWarning(
|
|
1095
1086
|
"gif-missing-poster",
|
|
@@ -1100,10 +1091,8 @@ function lintGifPoster(item, sourcePath) {
|
|
|
1100
1091
|
];
|
|
1101
1092
|
}
|
|
1102
1093
|
function lintBrowseLargeWithoutPresentation(item, sourcePath) {
|
|
1103
|
-
if (normalizeAddMenuBrowseSize(item) !== "large")
|
|
1104
|
-
|
|
1105
|
-
if (normalizeAddMenuPresentation(item))
|
|
1106
|
-
return [];
|
|
1094
|
+
if (normalizeAddMenuBrowseSize(item) !== "large") return [];
|
|
1095
|
+
if (normalizeAddMenuPresentation(item)) return [];
|
|
1107
1096
|
return [
|
|
1108
1097
|
catalogWarning(
|
|
1109
1098
|
"browse-large-without-presentation",
|
|
@@ -1176,8 +1165,7 @@ function resolveModulePath$1(basePath) {
|
|
|
1176
1165
|
return void 0;
|
|
1177
1166
|
}
|
|
1178
1167
|
function collectTypeScriptFiles(dir, depth = 0) {
|
|
1179
|
-
if (depth > 4)
|
|
1180
|
-
return [];
|
|
1168
|
+
if (depth > 4) return [];
|
|
1181
1169
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1182
1170
|
const files = [];
|
|
1183
1171
|
for (const entry of entries) {
|
|
@@ -1196,8 +1184,7 @@ function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
|
|
|
1196
1184
|
}
|
|
1197
1185
|
const searchRoots = isNpmPackage ? [path.join(pluginPath, "lib"), path.join(pluginPath, "dist")] : [pluginPath];
|
|
1198
1186
|
for (const root of searchRoots) {
|
|
1199
|
-
if (!fs.existsSync(root))
|
|
1200
|
-
continue;
|
|
1187
|
+
if (!fs.existsSync(root)) continue;
|
|
1201
1188
|
for (const file of collectTypeScriptFiles(root)) {
|
|
1202
1189
|
const content = fs.readFileSync(file, "utf-8");
|
|
1203
1190
|
const definesHandler = new RegExp(
|
|
@@ -1217,8 +1204,7 @@ function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
|
|
|
1217
1204
|
if (reExportMatch) {
|
|
1218
1205
|
const importSpec = reExportMatch[1].replace(/\.js$/, "");
|
|
1219
1206
|
const resolved = resolveModulePath$1(path.resolve(path.dirname(file), importSpec));
|
|
1220
|
-
if (resolved)
|
|
1221
|
-
return resolved;
|
|
1207
|
+
if (resolved) return resolved;
|
|
1222
1208
|
}
|
|
1223
1209
|
}
|
|
1224
1210
|
}
|
|
@@ -1228,8 +1214,7 @@ function extractBalancedBlock(source, openBraceIndex) {
|
|
|
1228
1214
|
let depth = 0;
|
|
1229
1215
|
for (let index = openBraceIndex; index < source.length; index++) {
|
|
1230
1216
|
const char = source[index];
|
|
1231
|
-
if (char === "{")
|
|
1232
|
-
depth++;
|
|
1217
|
+
if (char === "{") depth++;
|
|
1233
1218
|
else if (char === "}") {
|
|
1234
1219
|
depth--;
|
|
1235
1220
|
if (depth === 0) {
|
|
@@ -1249,8 +1234,7 @@ function findFunctionBodyOpenBrace(source, searchFrom) {
|
|
|
1249
1234
|
if (parenMatch?.index !== void 0) {
|
|
1250
1235
|
candidates.push(searchFrom + parenMatch.index + parenMatch[0].length - 1);
|
|
1251
1236
|
}
|
|
1252
|
-
if (candidates.length === 0)
|
|
1253
|
-
return -1;
|
|
1237
|
+
if (candidates.length === 0) return -1;
|
|
1254
1238
|
return Math.min(...candidates);
|
|
1255
1239
|
}
|
|
1256
1240
|
function extractFunctionBody(source, functionName) {
|
|
@@ -1267,11 +1251,9 @@ function extractFunctionBody(source, functionName) {
|
|
|
1267
1251
|
];
|
|
1268
1252
|
for (const pattern of patterns) {
|
|
1269
1253
|
const match = pattern.exec(source);
|
|
1270
|
-
if (!match)
|
|
1271
|
-
continue;
|
|
1254
|
+
if (!match) continue;
|
|
1272
1255
|
const braceIndex = findFunctionBodyOpenBrace(source, match.index);
|
|
1273
|
-
if (braceIndex === -1)
|
|
1274
|
-
continue;
|
|
1256
|
+
if (braceIndex === -1) continue;
|
|
1275
1257
|
return extractBalancedBlock(source, braceIndex);
|
|
1276
1258
|
}
|
|
1277
1259
|
return null;
|
|
@@ -1284,11 +1266,9 @@ function extractDefaultExportFunctionBody(source) {
|
|
|
1284
1266
|
];
|
|
1285
1267
|
for (const pattern of patterns) {
|
|
1286
1268
|
const match = pattern.exec(source);
|
|
1287
|
-
if (!match)
|
|
1288
|
-
continue;
|
|
1269
|
+
if (!match) continue;
|
|
1289
1270
|
const braceIndex = findFunctionBodyOpenBrace(source, match.index);
|
|
1290
|
-
if (braceIndex === -1)
|
|
1291
|
-
continue;
|
|
1271
|
+
if (braceIndex === -1) continue;
|
|
1292
1272
|
return extractBalancedBlock(source, braceIndex);
|
|
1293
1273
|
}
|
|
1294
1274
|
return null;
|
|
@@ -1298,8 +1278,7 @@ function handlerBodyWritesAddMenuCatalog(body) {
|
|
|
1298
1278
|
}
|
|
1299
1279
|
function resolveSetupHandlerFunctionBody(pluginPath, handlerRef, isNpmPackage) {
|
|
1300
1280
|
const sourceFile = resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage);
|
|
1301
|
-
if (!sourceFile)
|
|
1302
|
-
return null;
|
|
1281
|
+
if (!sourceFile) return null;
|
|
1303
1282
|
const source = fs.readFileSync(sourceFile, "utf-8");
|
|
1304
1283
|
if (isRelativeHandlerRef(handlerRef)) {
|
|
1305
1284
|
return extractDefaultExportFunctionBody(source) ?? extractFunctionBody(source, "setup") ?? extractFunctionBody(source, handlerRef);
|
|
@@ -1307,8 +1286,7 @@ function resolveSetupHandlerFunctionBody(pluginPath, handlerRef, isNpmPackage) {
|
|
|
1307
1286
|
return extractFunctionBody(source, handlerRef);
|
|
1308
1287
|
}
|
|
1309
1288
|
function suggestionForCode(code) {
|
|
1310
|
-
if (!code)
|
|
1311
|
-
return `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
|
|
1289
|
+
if (!code) return `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
|
|
1312
1290
|
return ADD_MENU_VALIDATION_SUGGESTIONS[code] ?? `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
|
|
1313
1291
|
}
|
|
1314
1292
|
function mapSchemaError$1(error, catalogPath) {
|
|
@@ -1338,8 +1316,7 @@ function pluginShipsAddMenuCatalog(context) {
|
|
|
1338
1316
|
);
|
|
1339
1317
|
}
|
|
1340
1318
|
function validateAddMenuAgentKitHandler(context, result) {
|
|
1341
|
-
if (!pluginShipsAddMenuCatalog(context))
|
|
1342
|
-
return;
|
|
1319
|
+
if (!pluginShipsAddMenuCatalog(context)) return;
|
|
1343
1320
|
const agentKitHandler = context.manifest.agentkit;
|
|
1344
1321
|
if (!agentKitHandler) {
|
|
1345
1322
|
result.warnings.push({
|
|
@@ -1351,15 +1328,13 @@ function validateAddMenuAgentKitHandler(context, result) {
|
|
|
1351
1328
|
});
|
|
1352
1329
|
}
|
|
1353
1330
|
const setupHandler = typeof context.manifest.setup === "string" ? context.manifest.setup : void 0;
|
|
1354
|
-
if (!setupHandler)
|
|
1355
|
-
return;
|
|
1331
|
+
if (!setupHandler) return;
|
|
1356
1332
|
const setupBody = resolveSetupHandlerFunctionBody(
|
|
1357
1333
|
context.pluginPath,
|
|
1358
1334
|
setupHandler,
|
|
1359
1335
|
context.isNpmPackage
|
|
1360
1336
|
);
|
|
1361
|
-
if (!setupBody || !handlerBodyWritesAddMenuCatalog(setupBody))
|
|
1362
|
-
return;
|
|
1337
|
+
if (!setupBody || !handlerBodyWritesAddMenuCatalog(setupBody)) return;
|
|
1363
1338
|
result.warnings.push({
|
|
1364
1339
|
type: "add-menu-catalog",
|
|
1365
1340
|
code: "add-menu-legacy-setup-handler",
|
|
@@ -1401,8 +1376,7 @@ async function validateAddMenuCatalog(context, result) {
|
|
|
1401
1376
|
validateAddMenuAgentKitHandler(context, result);
|
|
1402
1377
|
for (const relPath of ADD_MENU_CATALOG_REL_PATHS) {
|
|
1403
1378
|
const catalogPath = path.join(context.pluginPath, relPath);
|
|
1404
|
-
if (!fs.existsSync(catalogPath))
|
|
1405
|
-
continue;
|
|
1379
|
+
if (!fs.existsSync(catalogPath)) continue;
|
|
1406
1380
|
await validateAddMenuCatalogFileAtPath(catalogPath, relPath, result);
|
|
1407
1381
|
}
|
|
1408
1382
|
}
|
|
@@ -2026,16 +2000,14 @@ async function validateSchema(context, result) {
|
|
|
2026
2000
|
}
|
|
2027
2001
|
function checkExportExists(exportName, context) {
|
|
2028
2002
|
const packageJsonPath = path.join(context.pluginPath, "package.json");
|
|
2029
|
-
if (!fs.existsSync(packageJsonPath))
|
|
2030
|
-
return true;
|
|
2003
|
+
if (!fs.existsSync(packageJsonPath)) return true;
|
|
2031
2004
|
let mainPath;
|
|
2032
2005
|
try {
|
|
2033
2006
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
2034
2007
|
if (packageJson.exports?.["."]) {
|
|
2035
2008
|
const entry = packageJson.exports["."];
|
|
2036
2009
|
const entryPath = typeof entry === "string" ? entry : entry.default || entry.import;
|
|
2037
|
-
if (entryPath)
|
|
2038
|
-
mainPath = path.join(context.pluginPath, entryPath);
|
|
2010
|
+
if (entryPath) mainPath = path.join(context.pluginPath, entryPath);
|
|
2039
2011
|
}
|
|
2040
2012
|
if (!mainPath && packageJson.main) {
|
|
2041
2013
|
mainPath = path.join(context.pluginPath, packageJson.main);
|
|
@@ -2043,8 +2015,7 @@ function checkExportExists(exportName, context) {
|
|
|
2043
2015
|
} catch {
|
|
2044
2016
|
return true;
|
|
2045
2017
|
}
|
|
2046
|
-
if (!mainPath || !fs.existsSync(mainPath))
|
|
2047
|
-
return true;
|
|
2018
|
+
if (!mainPath || !fs.existsSync(mainPath)) return true;
|
|
2048
2019
|
try {
|
|
2049
2020
|
const content = fs.readFileSync(mainPath, "utf-8");
|
|
2050
2021
|
const patterns = [
|
|
@@ -2104,8 +2075,7 @@ function resolveContractFile(contractSpec, context) {
|
|
|
2104
2075
|
const resolvedPath = typeof exportValue === "string" ? exportValue : exportValue.default || exportValue.import || exportValue.require;
|
|
2105
2076
|
if (resolvedPath) {
|
|
2106
2077
|
const fullPath = path.join(context.pluginPath, resolvedPath);
|
|
2107
|
-
if (fs.existsSync(fullPath))
|
|
2108
|
-
return fullPath;
|
|
2078
|
+
if (fs.existsSync(fullPath)) return fullPath;
|
|
2109
2079
|
}
|
|
2110
2080
|
}
|
|
2111
2081
|
}
|
|
@@ -2114,8 +2084,7 @@ function resolveContractFile(contractSpec, context) {
|
|
|
2114
2084
|
}
|
|
2115
2085
|
for (const dir of ["dist", "lib", ""]) {
|
|
2116
2086
|
const candidate = path.join(context.pluginPath, dir, contractSpec);
|
|
2117
|
-
if (fs.existsSync(candidate))
|
|
2118
|
-
return candidate;
|
|
2087
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
2119
2088
|
}
|
|
2120
2089
|
return void 0;
|
|
2121
2090
|
} else {
|
|
@@ -2203,8 +2172,7 @@ function hasExportModifier(node) {
|
|
|
2203
2172
|
function resolveModulePath(basePath) {
|
|
2204
2173
|
for (const ext of ["", ".ts", ".js", "/index.ts", "/index.js"]) {
|
|
2205
2174
|
const candidate = basePath + ext;
|
|
2206
|
-
if (fs.existsSync(candidate))
|
|
2207
|
-
return candidate;
|
|
2175
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
2208
2176
|
}
|
|
2209
2177
|
return void 0;
|
|
2210
2178
|
}
|
|
@@ -2214,10 +2182,8 @@ function resolveComponentSourcePath(componentName, context) {
|
|
|
2214
2182
|
const entryFile = resolveModulePath(entryBase);
|
|
2215
2183
|
const libEntryFile = !entryFile ? resolveModulePath(path.join(context.pluginPath, "lib", modulePath)) : void 0;
|
|
2216
2184
|
const sourceEntry = entryFile || libEntryFile;
|
|
2217
|
-
if (!sourceEntry)
|
|
2218
|
-
|
|
2219
|
-
if (!sourceEntry.endsWith(".ts"))
|
|
2220
|
-
return void 0;
|
|
2185
|
+
if (!sourceEntry) return void 0;
|
|
2186
|
+
if (!sourceEntry.endsWith(".ts")) return void 0;
|
|
2221
2187
|
let sourceCode;
|
|
2222
2188
|
try {
|
|
2223
2189
|
sourceCode = fs.readFileSync(sourceEntry, "utf-8");
|
|
@@ -2233,12 +2199,9 @@ function resolveComponentSourcePath(componentName, context) {
|
|
|
2233
2199
|
);
|
|
2234
2200
|
const starReexportModules = [];
|
|
2235
2201
|
for (const statement of sourceFile.statements) {
|
|
2236
|
-
if (!u.isExportDeclaration(statement))
|
|
2237
|
-
|
|
2238
|
-
if (!statement.moduleSpecifier)
|
|
2239
|
-
continue;
|
|
2240
|
-
if (!u.isStringLiteral(statement.moduleSpecifier))
|
|
2241
|
-
continue;
|
|
2202
|
+
if (!u.isExportDeclaration(statement)) continue;
|
|
2203
|
+
if (!statement.moduleSpecifier) continue;
|
|
2204
|
+
if (!u.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
2242
2205
|
const moduleSpec = statement.moduleSpecifier.text;
|
|
2243
2206
|
const exportClause = statement.exportClause;
|
|
2244
2207
|
if (!exportClause) {
|
|
@@ -2256,12 +2219,10 @@ function resolveComponentSourcePath(componentName, context) {
|
|
|
2256
2219
|
}
|
|
2257
2220
|
}
|
|
2258
2221
|
for (const moduleSpec of starReexportModules) {
|
|
2259
|
-
if (!moduleSpec.startsWith("."))
|
|
2260
|
-
continue;
|
|
2222
|
+
if (!moduleSpec.startsWith(".")) continue;
|
|
2261
2223
|
const resolvedBase = path.resolve(path.dirname(sourceEntry), moduleSpec);
|
|
2262
2224
|
const resolvedPath = resolveModulePath(resolvedBase);
|
|
2263
|
-
if (!resolvedPath || !resolvedPath.endsWith(".ts"))
|
|
2264
|
-
continue;
|
|
2225
|
+
if (!resolvedPath || !resolvedPath.endsWith(".ts")) continue;
|
|
2265
2226
|
try {
|
|
2266
2227
|
const modSource = fs.readFileSync(resolvedPath, "utf-8");
|
|
2267
2228
|
const modFile = u.createSourceFile(
|
|
@@ -2294,16 +2255,12 @@ function resolveContractPath(contract, context) {
|
|
|
2294
2255
|
}
|
|
2295
2256
|
async function checkComponentContractConsistency(contract, context, result) {
|
|
2296
2257
|
const componentName = contract.component;
|
|
2297
|
-
if (!componentName)
|
|
2298
|
-
return;
|
|
2258
|
+
if (!componentName) return;
|
|
2299
2259
|
const sourcePath = resolveComponentSourcePath(componentName, context);
|
|
2300
|
-
if (!sourcePath)
|
|
2301
|
-
|
|
2302
|
-
if (!sourcePath.endsWith(".ts"))
|
|
2303
|
-
return;
|
|
2260
|
+
if (!sourcePath) return;
|
|
2261
|
+
if (!sourcePath.endsWith(".ts")) return;
|
|
2304
2262
|
const contractPath = resolveContractPath(contract, context);
|
|
2305
|
-
if (!contractPath)
|
|
2306
|
-
return;
|
|
2263
|
+
if (!contractPath) return;
|
|
2307
2264
|
let contractContent;
|
|
2308
2265
|
try {
|
|
2309
2266
|
contractContent = await fs.promises.readFile(contractPath, "utf-8");
|
|
@@ -2311,8 +2268,7 @@ async function checkComponentContractConsistency(contract, context, result) {
|
|
|
2311
2268
|
return;
|
|
2312
2269
|
}
|
|
2313
2270
|
const parsed = parseContract(contractContent, path.basename(contractPath));
|
|
2314
|
-
if (parsed.validations.length > 0)
|
|
2315
|
-
return;
|
|
2271
|
+
if (parsed.validations.length > 0) return;
|
|
2316
2272
|
let sourceCode;
|
|
2317
2273
|
try {
|
|
2318
2274
|
sourceCode = await fs.promises.readFile(sourcePath, "utf-8");
|
|
@@ -2429,10 +2385,99 @@ async function validatePackageJson(context, result) {
|
|
|
2429
2385
|
});
|
|
2430
2386
|
}
|
|
2431
2387
|
}
|
|
2388
|
+
function isBareFunctionExport(exportName, context) {
|
|
2389
|
+
const sourcePath = resolveExportSourceFile(exportName, context);
|
|
2390
|
+
if (!sourcePath) return false;
|
|
2391
|
+
let sourceCode;
|
|
2392
|
+
try {
|
|
2393
|
+
sourceCode = fs.readFileSync(sourcePath, "utf-8");
|
|
2394
|
+
} catch {
|
|
2395
|
+
return false;
|
|
2396
|
+
}
|
|
2397
|
+
const sourceFile = u.createSourceFile(
|
|
2398
|
+
sourcePath,
|
|
2399
|
+
sourceCode,
|
|
2400
|
+
u.ScriptTarget.Latest,
|
|
2401
|
+
true,
|
|
2402
|
+
u.ScriptKind.TS
|
|
2403
|
+
);
|
|
2404
|
+
for (const statement of sourceFile.statements) {
|
|
2405
|
+
if (u.isFunctionDeclaration(statement) && hasExportModifier(statement) && statement.name?.text === exportName) {
|
|
2406
|
+
return true;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
return false;
|
|
2410
|
+
}
|
|
2411
|
+
function resolveModulePathWithJsToTs(basePath) {
|
|
2412
|
+
const result = resolveModulePath(basePath);
|
|
2413
|
+
if (result) return result;
|
|
2414
|
+
if (basePath.endsWith(".js")) {
|
|
2415
|
+
return resolveModulePath(basePath.slice(0, -3) + ".ts");
|
|
2416
|
+
}
|
|
2417
|
+
return void 0;
|
|
2418
|
+
}
|
|
2419
|
+
function resolveExportSourceFile(exportName, context) {
|
|
2420
|
+
const modulePath = context.manifest.module || "index";
|
|
2421
|
+
const entryBase = path.join(context.pluginPath, modulePath);
|
|
2422
|
+
const libEntryBase = path.join(context.pluginPath, "lib", modulePath);
|
|
2423
|
+
const sourceEntry = resolveModulePath(entryBase) || resolveModulePath(libEntryBase);
|
|
2424
|
+
if (!sourceEntry || !sourceEntry.endsWith(".ts")) return void 0;
|
|
2425
|
+
return followExportChain(exportName, sourceEntry);
|
|
2426
|
+
}
|
|
2427
|
+
function followExportChain(exportName, filePath) {
|
|
2428
|
+
let sourceCode;
|
|
2429
|
+
try {
|
|
2430
|
+
sourceCode = fs.readFileSync(filePath, "utf-8");
|
|
2431
|
+
} catch {
|
|
2432
|
+
return void 0;
|
|
2433
|
+
}
|
|
2434
|
+
const sourceFile = u.createSourceFile(
|
|
2435
|
+
filePath,
|
|
2436
|
+
sourceCode,
|
|
2437
|
+
u.ScriptTarget.Latest,
|
|
2438
|
+
true,
|
|
2439
|
+
u.ScriptKind.TS
|
|
2440
|
+
);
|
|
2441
|
+
const starReexportModules = [];
|
|
2442
|
+
for (const statement of sourceFile.statements) {
|
|
2443
|
+
if (u.isExportDeclaration(statement) && statement.moduleSpecifier) {
|
|
2444
|
+
if (!u.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
2445
|
+
const moduleSpec = statement.moduleSpecifier.text;
|
|
2446
|
+
if (!statement.exportClause) {
|
|
2447
|
+
starReexportModules.push(moduleSpec);
|
|
2448
|
+
continue;
|
|
2449
|
+
}
|
|
2450
|
+
if (u.isNamedExports(statement.exportClause)) {
|
|
2451
|
+
for (const element of statement.exportClause.elements) {
|
|
2452
|
+
if (element.name.text === exportName) {
|
|
2453
|
+
const resolvedBase = path.resolve(path.dirname(filePath), moduleSpec);
|
|
2454
|
+
return resolveModulePathWithJsToTs(resolvedBase);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
if (u.isFunctionDeclaration(statement) && hasExportModifier(statement)) {
|
|
2460
|
+
if (statement.name?.text === exportName) return filePath;
|
|
2461
|
+
}
|
|
2462
|
+
if (u.isVariableStatement(statement) && hasExportModifier(statement)) {
|
|
2463
|
+
for (const decl of statement.declarationList.declarations) {
|
|
2464
|
+
if (u.isIdentifier(decl.name) && decl.name.text === exportName) return filePath;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
for (const moduleSpec of starReexportModules) {
|
|
2469
|
+
if (!moduleSpec.startsWith(".")) continue;
|
|
2470
|
+
const resolvedBase = path.resolve(path.dirname(filePath), moduleSpec);
|
|
2471
|
+
const resolved = resolveModulePathWithJsToTs(resolvedBase);
|
|
2472
|
+
if (!resolved) continue;
|
|
2473
|
+
const found = followExportChain(exportName, resolved);
|
|
2474
|
+
if (found) return found;
|
|
2475
|
+
}
|
|
2476
|
+
return void 0;
|
|
2477
|
+
}
|
|
2432
2478
|
async function validateDynamicContracts(context, result) {
|
|
2433
2479
|
const { dynamic_contracts } = context.manifest;
|
|
2434
|
-
if (!dynamic_contracts)
|
|
2435
|
-
return;
|
|
2480
|
+
if (!dynamic_contracts) return;
|
|
2436
2481
|
const dynamicConfigs = Array.isArray(dynamic_contracts) ? dynamic_contracts : [dynamic_contracts];
|
|
2437
2482
|
for (const config of dynamicConfigs) {
|
|
2438
2483
|
const prefix = config.prefix || "(unknown)";
|
|
@@ -2456,6 +2501,13 @@ async function validateDynamicContracts(context, result) {
|
|
|
2456
2501
|
suggestion: `Create generator file at ${generatorPath}.ts`
|
|
2457
2502
|
});
|
|
2458
2503
|
}
|
|
2504
|
+
} else if (isBareFunctionExport(config.generator, context)) {
|
|
2505
|
+
result.errors.push({
|
|
2506
|
+
type: "export-mismatch",
|
|
2507
|
+
message: `Generator "${config.generator}" for ${prefix} is a bare function — it must be a DynamicContractGenerator object`,
|
|
2508
|
+
location: "plugin.yaml dynamic_contracts",
|
|
2509
|
+
suggestion: `Use makeContractGenerator().generateWith(...) from @jay-framework/fullstack-component instead of exporting a plain function`
|
|
2510
|
+
});
|
|
2459
2511
|
}
|
|
2460
2512
|
}
|
|
2461
2513
|
if (config.component) {
|
|
@@ -2511,8 +2563,7 @@ function extractExpressions(text) {
|
|
|
2511
2563
|
function extractTagPath(expr) {
|
|
2512
2564
|
let cleaned = expr.replace(/^!/, "").trim();
|
|
2513
2565
|
cleaned = cleaned.split(/\s*[!=]==?\s*/)[0].trim();
|
|
2514
|
-
if (cleaned === "." || cleaned === "")
|
|
2515
|
-
return null;
|
|
2566
|
+
if (cleaned === "." || cleaned === "") return null;
|
|
2516
2567
|
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(cleaned)) {
|
|
2517
2568
|
return cleaned;
|
|
2518
2569
|
}
|
|
@@ -2611,8 +2662,7 @@ function collectUsedTags(jayHtml) {
|
|
|
2611
2662
|
const ifVal = element.getAttribute?.("if");
|
|
2612
2663
|
if (ifVal) {
|
|
2613
2664
|
const ifPath = extractTagPath(ifVal);
|
|
2614
|
-
if (ifPath)
|
|
2615
|
-
resolvePath(ifPath, scopes);
|
|
2665
|
+
if (ifPath) resolvePath(ifPath, scopes);
|
|
2616
2666
|
}
|
|
2617
2667
|
const refVal = element.getAttribute?.("ref");
|
|
2618
2668
|
if (refVal) {
|
|
@@ -2620,12 +2670,10 @@ function collectUsedTags(jayHtml) {
|
|
|
2620
2670
|
}
|
|
2621
2671
|
const attrs = element.attributes ?? {};
|
|
2622
2672
|
for (const [name, value] of Object.entries(attrs)) {
|
|
2623
|
-
if (SKIP_ATTRS.has(name))
|
|
2624
|
-
continue;
|
|
2673
|
+
if (SKIP_ATTRS.has(name)) continue;
|
|
2625
2674
|
for (const expr of extractExpressions(value)) {
|
|
2626
2675
|
const p = extractTagPath(expr);
|
|
2627
|
-
if (p)
|
|
2628
|
-
resolvePath(p, scopes);
|
|
2676
|
+
if (p) resolvePath(p, scopes);
|
|
2629
2677
|
}
|
|
2630
2678
|
}
|
|
2631
2679
|
for (const child of element.childNodes ?? []) {
|
|
@@ -2633,8 +2681,7 @@ function collectUsedTags(jayHtml) {
|
|
|
2633
2681
|
const text = child.rawText ?? child.text ?? "";
|
|
2634
2682
|
for (const expr of extractExpressions(text)) {
|
|
2635
2683
|
const p = extractTagPath(expr);
|
|
2636
|
-
if (p)
|
|
2637
|
-
resolvePath(p, childScopes);
|
|
2684
|
+
if (p) resolvePath(p, childScopes);
|
|
2638
2685
|
}
|
|
2639
2686
|
} else if (child.nodeType === 1) {
|
|
2640
2687
|
walkElement(child, childScopes);
|
|
@@ -2647,14 +2694,12 @@ function collectUsedTags(jayHtml) {
|
|
|
2647
2694
|
function analyzeTagCoverage(jayHtml, file) {
|
|
2648
2695
|
const imports = jayHtml.headlessImports;
|
|
2649
2696
|
const withContracts = imports.filter((imp) => imp.contract);
|
|
2650
|
-
if (withContracts.length === 0)
|
|
2651
|
-
return null;
|
|
2697
|
+
if (withContracts.length === 0) return null;
|
|
2652
2698
|
const usedTagsMap = collectUsedTags(jayHtml);
|
|
2653
2699
|
const contracts = [];
|
|
2654
2700
|
for (let i = 0; i < imports.length; i++) {
|
|
2655
2701
|
const imp = imports[i];
|
|
2656
|
-
if (!imp.contract)
|
|
2657
|
-
continue;
|
|
2702
|
+
if (!imp.contract) continue;
|
|
2658
2703
|
const allTags = flattenContractTags(imp.contract.tags);
|
|
2659
2704
|
const usedSet = usedTagsMap.get(i) ?? /* @__PURE__ */ new Set();
|
|
2660
2705
|
const expanded = new Set(usedSet);
|
|
@@ -2683,12 +2728,9 @@ function resolveContractTag(contract, tagPath) {
|
|
|
2683
2728
|
let tags = contract.tags;
|
|
2684
2729
|
for (let i = 0; i < segments.length; i++) {
|
|
2685
2730
|
const tag = tags.find((t) => t.tag === segments[i]);
|
|
2686
|
-
if (!tag)
|
|
2687
|
-
|
|
2688
|
-
if (
|
|
2689
|
-
return tag;
|
|
2690
|
-
if (!tag.tags)
|
|
2691
|
-
return void 0;
|
|
2731
|
+
if (!tag) return void 0;
|
|
2732
|
+
if (i === segments.length - 1) return tag;
|
|
2733
|
+
if (!tag.tags) return void 0;
|
|
2692
2734
|
tags = tag.tags;
|
|
2693
2735
|
}
|
|
2694
2736
|
return void 0;
|
|
@@ -2720,17 +2762,13 @@ function checkRefElementTypes(jayHtml, file) {
|
|
|
2720
2762
|
importIndex = scope.importIndex;
|
|
2721
2763
|
tagPath = scope.prefix ? `${scope.prefix}.${refPath}` : refPath;
|
|
2722
2764
|
}
|
|
2723
|
-
if (importIndex === void 0)
|
|
2724
|
-
return;
|
|
2765
|
+
if (importIndex === void 0) return;
|
|
2725
2766
|
const imp = imports[importIndex];
|
|
2726
|
-
if (!imp.contract)
|
|
2727
|
-
return;
|
|
2767
|
+
if (!imp.contract) return;
|
|
2728
2768
|
const contractTag = resolveContractTag(imp.contract, tagPath);
|
|
2729
|
-
if (!contractTag || !contractTag.elementType)
|
|
2730
|
-
return;
|
|
2769
|
+
if (!contractTag || !contractTag.elementType) return;
|
|
2731
2770
|
const contractTypes = contractTag.elementType;
|
|
2732
|
-
if (contractTypes.includes("HTMLElement"))
|
|
2733
|
-
return;
|
|
2771
|
+
if (contractTypes.includes("HTMLElement")) return;
|
|
2734
2772
|
if (!contractTypes.includes(ref.actualType)) {
|
|
2735
2773
|
const label = imp.key ? `${imp.key}.${tagPath}` : tagPath;
|
|
2736
2774
|
warnings.push(
|
|
@@ -2799,6 +2837,65 @@ function checkRefElementTypes(jayHtml, file) {
|
|
|
2799
2837
|
walkElement(jayHtml.body, []);
|
|
2800
2838
|
return warnings;
|
|
2801
2839
|
}
|
|
2840
|
+
function checkPageComponentExport(jayHtmlPath) {
|
|
2841
|
+
const dirname = path.dirname(jayHtmlPath);
|
|
2842
|
+
const compPath = path.join(dirname, "page.ts");
|
|
2843
|
+
if (!fs.existsSync(compPath)) return null;
|
|
2844
|
+
let content;
|
|
2845
|
+
try {
|
|
2846
|
+
content = fs.readFileSync(compPath, "utf-8");
|
|
2847
|
+
} catch {
|
|
2848
|
+
return null;
|
|
2849
|
+
}
|
|
2850
|
+
const exportName = "page";
|
|
2851
|
+
const patterns = [
|
|
2852
|
+
new RegExp(`export\\s*\\{[^}]*\\b${exportName}\\b[^}]*\\}`, "m"),
|
|
2853
|
+
new RegExp(`export\\s+(?:async\\s+)?function\\s+${exportName}\\b`),
|
|
2854
|
+
new RegExp(`export\\s+(?:const|let|var)\\s+${exportName}\\b`)
|
|
2855
|
+
];
|
|
2856
|
+
if (patterns.some((p) => p.test(content))) return null;
|
|
2857
|
+
return `${path.relative(dirname, compPath)} exists but does not export "${exportName}". Remove the file or add the export.`;
|
|
2858
|
+
}
|
|
2859
|
+
const DOCUMENT_ACCESS_PATTERNS = [
|
|
2860
|
+
/document\.getElementById\b/,
|
|
2861
|
+
/document\.querySelector\b/,
|
|
2862
|
+
/document\.querySelectorAll\b/,
|
|
2863
|
+
/document\.getElementsBy\w+/,
|
|
2864
|
+
/document\.createElement\b/,
|
|
2865
|
+
/document\.body\.appendChild\b/,
|
|
2866
|
+
/document\.addEventListener\b/
|
|
2867
|
+
];
|
|
2868
|
+
const DOM_SUPPRESS_COMMENT = "jay-dom: allow";
|
|
2869
|
+
function checkDirectDocumentAccess(jayHtmlPath) {
|
|
2870
|
+
const dirname = path.dirname(jayHtmlPath);
|
|
2871
|
+
const basename = path.basename(jayHtmlPath, JAY_EXTENSION);
|
|
2872
|
+
const candidates = [path.join(dirname, `${basename}.ts`), path.join(dirname, "page.ts")];
|
|
2873
|
+
const compPath = candidates.find((p) => fs.existsSync(p));
|
|
2874
|
+
if (!compPath) return [];
|
|
2875
|
+
const compName = path.basename(compPath);
|
|
2876
|
+
let content;
|
|
2877
|
+
try {
|
|
2878
|
+
content = fs.readFileSync(compPath, "utf-8");
|
|
2879
|
+
} catch {
|
|
2880
|
+
return [];
|
|
2881
|
+
}
|
|
2882
|
+
const warnings = [];
|
|
2883
|
+
const lines = content.split("\n");
|
|
2884
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2885
|
+
const line = lines[i];
|
|
2886
|
+
if (line.includes(DOM_SUPPRESS_COMMENT)) continue;
|
|
2887
|
+
for (const pattern of DOCUMENT_ACCESS_PATTERNS) {
|
|
2888
|
+
const match = pattern.exec(line);
|
|
2889
|
+
if (match) {
|
|
2890
|
+
warnings.push(
|
|
2891
|
+
`${compName}:${i + 1} — Direct DOM access "${match[0]}" — use Jay refs instead. Suppress with // ${DOM_SUPPRESS_COMMENT} on the same line. See agent-kit/developer/component-refs.md`
|
|
2892
|
+
);
|
|
2893
|
+
break;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
return warnings;
|
|
2898
|
+
}
|
|
2802
2899
|
const PARSE_PARAM = /^\[(\[)?(\.\.\.)?([^\]]+)\]?\]$/;
|
|
2803
2900
|
function extractRouteParams(filePath, pagesBase) {
|
|
2804
2901
|
const relative = path.relative(pagesBase, filePath);
|
|
@@ -2840,8 +2937,7 @@ function checkRouteParams(parsedFile, filePath, pagesBase) {
|
|
|
2840
2937
|
collectParams(imp.contract.params);
|
|
2841
2938
|
}
|
|
2842
2939
|
}
|
|
2843
|
-
if (requiredParams.size === 0)
|
|
2844
|
-
return [];
|
|
2940
|
+
if (requiredParams.size === 0) return [];
|
|
2845
2941
|
const routeParams = extractRouteParams(filePath, pagesBase);
|
|
2846
2942
|
const headlessProps = extractHeadlessPropsParamNames(parsedFile);
|
|
2847
2943
|
const availableParams = /* @__PURE__ */ new Set([...routeParams, ...headlessProps]);
|
|
@@ -2857,11 +2953,9 @@ function checkRouteParams(parsedFile, filePath, pagesBase) {
|
|
|
2857
2953
|
}
|
|
2858
2954
|
function checkRouteToContractParams(parsedFile, filePath, pagesBase) {
|
|
2859
2955
|
const routeParams = extractRouteParams(filePath, pagesBase);
|
|
2860
|
-
if (routeParams.size === 0)
|
|
2861
|
-
return [];
|
|
2956
|
+
if (routeParams.size === 0) return [];
|
|
2862
2957
|
const hasAnyContract = !!parsedFile.contract || parsedFile.headlessImports.some((imp) => !!imp.contract);
|
|
2863
|
-
if (!hasAnyContract)
|
|
2864
|
-
return [];
|
|
2958
|
+
if (!hasAnyContract) return [];
|
|
2865
2959
|
const declaredParams = /* @__PURE__ */ new Set();
|
|
2866
2960
|
if (parsedFile.contract?.params) {
|
|
2867
2961
|
for (const p of parsedFile.contract.params) {
|
|
@@ -2913,17 +3007,14 @@ function resolveBindingPhase(bindingPath, jayHtml) {
|
|
|
2913
3007
|
const keyedImport = jayHtml.headlessImports.find((i) => i.key === root && i.contract);
|
|
2914
3008
|
if (keyedImport?.contract) {
|
|
2915
3009
|
const tagPath = segments.slice(1).join(".");
|
|
2916
|
-
if (!tagPath)
|
|
2917
|
-
return void 0;
|
|
3010
|
+
if (!tagPath) return void 0;
|
|
2918
3011
|
const tag = resolveContractTag(keyedImport.contract, tagPath);
|
|
2919
|
-
if (!tag)
|
|
2920
|
-
return void 0;
|
|
3012
|
+
if (!tag) return void 0;
|
|
2921
3013
|
return tag.phase || "slow";
|
|
2922
3014
|
}
|
|
2923
3015
|
if (jayHtml.contract) {
|
|
2924
3016
|
const tag = resolveContractTag(jayHtml.contract, bindingPath);
|
|
2925
|
-
if (!tag)
|
|
2926
|
-
return void 0;
|
|
3017
|
+
if (!tag) return void 0;
|
|
2927
3018
|
return tag.phase || "slow";
|
|
2928
3019
|
}
|
|
2929
3020
|
return void 0;
|
|
@@ -2974,15 +3065,12 @@ function checkHeadlessInstanceProps(jayHtml, file) {
|
|
|
2974
3065
|
}
|
|
2975
3066
|
for (const contractProp of contract.props) {
|
|
2976
3067
|
const attrValue = lowerAttrs[contractProp.name.toLowerCase()];
|
|
2977
|
-
if (!attrValue)
|
|
2978
|
-
continue;
|
|
3068
|
+
if (!attrValue) continue;
|
|
2979
3069
|
const bindingMatch = attrValue.match(/^\{(.+)\}$/);
|
|
2980
|
-
if (!bindingMatch)
|
|
2981
|
-
continue;
|
|
3070
|
+
if (!bindingMatch) continue;
|
|
2982
3071
|
const bindingPath = bindingMatch[1];
|
|
2983
3072
|
const sourcePhase = resolveBindingPhase(bindingPath, jayHtml);
|
|
2984
|
-
if (!sourcePhase)
|
|
2985
|
-
continue;
|
|
3073
|
+
if (!sourcePhase) continue;
|
|
2986
3074
|
const propPhase = contractProp.phase ?? "slow";
|
|
2987
3075
|
const sourceOrder = PHASE_ORDER[sourcePhase] ?? 0;
|
|
2988
3076
|
const propOrder = PHASE_ORDER[propPhase] ?? 0;
|
|
@@ -3020,8 +3108,7 @@ function resolveLinkedTags(tags, contractDir) {
|
|
|
3020
3108
|
});
|
|
3021
3109
|
}
|
|
3022
3110
|
function resolveContractLinks(contract, contractPath) {
|
|
3023
|
-
if (!contractPath)
|
|
3024
|
-
return contract;
|
|
3111
|
+
if (!contractPath) return contract;
|
|
3025
3112
|
const contractDir = path.dirname(contractPath);
|
|
3026
3113
|
return { ...contract, tags: resolveLinkedTags(contract.tags, contractDir) };
|
|
3027
3114
|
}
|
|
@@ -3029,8 +3116,7 @@ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
|
|
|
3029
3116
|
const scannedPlugins = await scanPlugins({ projectRoot, includeDevDeps: true });
|
|
3030
3117
|
const loadedValidators = [];
|
|
3031
3118
|
for (const [, plugin] of scannedPlugins) {
|
|
3032
|
-
if (!plugin.manifest.validators)
|
|
3033
|
-
continue;
|
|
3119
|
+
if (!plugin.manifest.validators) continue;
|
|
3034
3120
|
for (const validatorDef of plugin.manifest.validators) {
|
|
3035
3121
|
const source = `${plugin.name}/${validatorDef.name}`;
|
|
3036
3122
|
let validatorFn;
|
|
@@ -3232,6 +3318,18 @@ async function validateJayFiles(options = {}) {
|
|
|
3232
3318
|
message: '<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.'
|
|
3233
3319
|
});
|
|
3234
3320
|
}
|
|
3321
|
+
const pageExportError = checkPageComponentExport(jayFile);
|
|
3322
|
+
if (pageExportError) {
|
|
3323
|
+
errors.push({
|
|
3324
|
+
file: relativePath,
|
|
3325
|
+
message: pageExportError,
|
|
3326
|
+
stage: "generate"
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
const domWarnings = checkDirectDocumentAccess(jayFile);
|
|
3330
|
+
for (const msg of domWarnings) {
|
|
3331
|
+
warnings.push({ file: relativePath, message: msg });
|
|
3332
|
+
}
|
|
3235
3333
|
const routeParamWarnings = checkRouteParams(parsedFile.val, jayFile, scanDir);
|
|
3236
3334
|
for (const msg of routeParamWarnings) {
|
|
3237
3335
|
warnings.push({ file: relativePath, message: msg });
|
|
@@ -3296,6 +3394,21 @@ async function validateJayFiles(options = {}) {
|
|
|
3296
3394
|
}
|
|
3297
3395
|
}
|
|
3298
3396
|
}
|
|
3397
|
+
const robotsTxtPath = path.resolve(projectRoot, "public/robots.txt");
|
|
3398
|
+
if (!fs.existsSync(robotsTxtPath)) {
|
|
3399
|
+
warnings.push({
|
|
3400
|
+
file: "public/robots.txt",
|
|
3401
|
+
message: "public/robots.txt not found — search engines may crawl pages you don't intend to expose.",
|
|
3402
|
+
suggestion: "Create public/robots.txt with: User-agent: *\nAllow: /\nSitemap: https://your-domain.com/sitemap.xml"
|
|
3403
|
+
});
|
|
3404
|
+
}
|
|
3405
|
+
if (!config.site?.baseUrl) {
|
|
3406
|
+
warnings.push({
|
|
3407
|
+
file: ".jay",
|
|
3408
|
+
message: "site.baseUrl not configured — sitemap.xml will not be generated in production.",
|
|
3409
|
+
suggestion: "Add to .jay config: site:\n baseUrl: https://your-domain.com"
|
|
3410
|
+
});
|
|
3411
|
+
}
|
|
3299
3412
|
const pluginValidators = await runPluginValidators(projectRoot, parsedFiles, errors, warnings);
|
|
3300
3413
|
return {
|
|
3301
3414
|
valid: errors.length === 0,
|
|
@@ -3609,8 +3722,7 @@ async function ensureAgentKitDocs(projectRoot, _force, mode) {
|
|
|
3609
3722
|
const sharedDirs = ["contracts"];
|
|
3610
3723
|
for (const dir of sharedDirs) {
|
|
3611
3724
|
const srcDir = path$1.join(templateDir, dir);
|
|
3612
|
-
if (!fsSync.existsSync(srcDir))
|
|
3613
|
-
continue;
|
|
3725
|
+
if (!fsSync.existsSync(srcDir)) continue;
|
|
3614
3726
|
await copyDirRecursive(srcDir, path$1.join(agentKitDir, dir));
|
|
3615
3727
|
getLogger().info(chalk.gray(` Created agent-kit/${dir}/`));
|
|
3616
3728
|
}
|
|
@@ -3635,8 +3747,7 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
|
|
|
3635
3747
|
const copiedPerRole = /* @__PURE__ */ new Map();
|
|
3636
3748
|
for (const [, plugin] of plugins) {
|
|
3637
3749
|
const pluginAgentKitDir = path$1.join(plugin.pluginPath, "agent-kit");
|
|
3638
|
-
if (!fsSync.existsSync(pluginAgentKitDir))
|
|
3639
|
-
continue;
|
|
3750
|
+
if (!fsSync.existsSync(pluginAgentKitDir)) continue;
|
|
3640
3751
|
for (const role of roles) {
|
|
3641
3752
|
const roleSourceDir = path$1.join(pluginAgentKitDir, role);
|
|
3642
3753
|
let files;
|
|
@@ -3647,8 +3758,7 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
|
|
|
3647
3758
|
} catch {
|
|
3648
3759
|
continue;
|
|
3649
3760
|
}
|
|
3650
|
-
if (files.length === 0)
|
|
3651
|
-
continue;
|
|
3761
|
+
if (files.length === 0) continue;
|
|
3652
3762
|
const roleOutputDir = path$1.join(agentKitDir, role);
|
|
3653
3763
|
await fs$1.mkdir(roleOutputDir, { recursive: true });
|
|
3654
3764
|
for (const filename of files) {
|
|
@@ -3671,8 +3781,7 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
|
|
|
3671
3781
|
}
|
|
3672
3782
|
} catch {
|
|
3673
3783
|
}
|
|
3674
|
-
if (!copiedPerRole.has(role))
|
|
3675
|
-
copiedPerRole.set(role, []);
|
|
3784
|
+
if (!copiedPerRole.has(role)) copiedPerRole.set(role, []);
|
|
3676
3785
|
copiedPerRole.get(role).push({ filename, pluginName: plugin.name, description });
|
|
3677
3786
|
getLogger().info(
|
|
3678
3787
|
chalk.gray(
|
|
@@ -3684,8 +3793,7 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
|
|
|
3684
3793
|
}
|
|
3685
3794
|
for (const [role, entries] of copiedPerRole) {
|
|
3686
3795
|
const instructionsPath = path$1.join(agentKitDir, role, "INSTRUCTIONS.md");
|
|
3687
|
-
if (!fsSync.existsSync(instructionsPath))
|
|
3688
|
-
continue;
|
|
3796
|
+
if (!fsSync.existsSync(instructionsPath)) continue;
|
|
3689
3797
|
const lines = [
|
|
3690
3798
|
"",
|
|
3691
3799
|
"## Plugin-Contributed Guides",
|
|
@@ -3707,8 +3815,7 @@ async function generatePluginAgentKit(projectRoot, options, initErrors, viteServ
|
|
|
3707
3815
|
verbose: options.verbose,
|
|
3708
3816
|
pluginFilter: options.plugin
|
|
3709
3817
|
});
|
|
3710
|
-
if (plugins.length === 0)
|
|
3711
|
-
return;
|
|
3818
|
+
if (plugins.length === 0) return;
|
|
3712
3819
|
const logger = getLogger();
|
|
3713
3820
|
logger.important("");
|
|
3714
3821
|
logger.important(chalk.bold("Generating plugin agent-kit data..."));
|
|
@@ -3937,20 +4044,17 @@ function createAnswersFilePrompt(answers, pluginName) {
|
|
|
3937
4044
|
return {
|
|
3938
4045
|
async input(options) {
|
|
3939
4046
|
const value = answers[options.key];
|
|
3940
|
-
if (value !== void 0)
|
|
3941
|
-
return value;
|
|
4047
|
+
if (value !== void 0) return value;
|
|
3942
4048
|
throw new SetupNeedsAnswerError(pluginName, options.key, "input", options.message);
|
|
3943
4049
|
},
|
|
3944
4050
|
async confirm(options) {
|
|
3945
4051
|
const value = answers[options.key];
|
|
3946
|
-
if (value !== void 0)
|
|
3947
|
-
return value === "true" || value === "yes";
|
|
4052
|
+
if (value !== void 0) return value === "true" || value === "yes";
|
|
3948
4053
|
throw new SetupNeedsAnswerError(pluginName, options.key, "confirm", options.message);
|
|
3949
4054
|
},
|
|
3950
4055
|
async select(options) {
|
|
3951
4056
|
const value = answers[options.key];
|
|
3952
|
-
if (value !== void 0)
|
|
3953
|
-
return value;
|
|
4057
|
+
if (value !== void 0) return value;
|
|
3954
4058
|
throw new SetupNeedsAnswerError(
|
|
3955
4059
|
pluginName,
|
|
3956
4060
|
options.key,
|
|
@@ -4111,12 +4215,9 @@ async function runSetup(pluginFilter, options, projectRoot) {
|
|
|
4111
4215
|
}
|
|
4112
4216
|
await runProjectInit(projectRoot, viteServer);
|
|
4113
4217
|
const parts = [];
|
|
4114
|
-
if (configured > 0)
|
|
4115
|
-
|
|
4116
|
-
if (
|
|
4117
|
-
parts.push(`${needsConfig} needs config`);
|
|
4118
|
-
if (errors > 0)
|
|
4119
|
-
parts.push(`${errors} error(s)`);
|
|
4218
|
+
if (configured > 0) parts.push(`${configured} configured`);
|
|
4219
|
+
if (needsConfig > 0) parts.push(`${needsConfig} needs config`);
|
|
4220
|
+
if (errors > 0) parts.push(`${errors} error(s)`);
|
|
4120
4221
|
logger.important(`Setup complete: ${parts.join(", ")}`);
|
|
4121
4222
|
if (errors > 0) {
|
|
4122
4223
|
process.exit(1);
|
|
@@ -4135,8 +4236,7 @@ async function runSetup(pluginFilter, options, projectRoot) {
|
|
|
4135
4236
|
}
|
|
4136
4237
|
async function initPlugin(pluginName, allPluginsWithInit, viteServer, logger) {
|
|
4137
4238
|
const pluginInit = allPluginsWithInit.filter((p) => p.name === pluginName);
|
|
4138
|
-
if (pluginInit.length === 0)
|
|
4139
|
-
return;
|
|
4239
|
+
if (pluginInit.length === 0) return;
|
|
4140
4240
|
const initErrors = await executePluginServerInits(pluginInit, viteServer, false, true);
|
|
4141
4241
|
if (initErrors.size > 0) {
|
|
4142
4242
|
for (const [, err] of initErrors) {
|
|
@@ -4276,8 +4376,7 @@ function printCommandList(commands) {
|
|
|
4276
4376
|
logger.important("\nAvailable plugin commands:\n");
|
|
4277
4377
|
const byPlugin = /* @__PURE__ */ new Map();
|
|
4278
4378
|
for (const cmd of commands) {
|
|
4279
|
-
if (!byPlugin.has(cmd.pluginName))
|
|
4280
|
-
byPlugin.set(cmd.pluginName, []);
|
|
4379
|
+
if (!byPlugin.has(cmd.pluginName)) byPlugin.set(cmd.pluginName, []);
|
|
4281
4380
|
byPlugin.get(cmd.pluginName).push(cmd);
|
|
4282
4381
|
}
|
|
4283
4382
|
for (const [pluginName, cmds] of byPlugin) {
|
|
@@ -4333,8 +4432,7 @@ program.command("build").description("Build production artifacts").option("-p, -
|
|
|
4333
4432
|
await runBuild(options.path, options);
|
|
4334
4433
|
} catch (error) {
|
|
4335
4434
|
getLogger().error(chalk.red("Build failed:") + " " + error.message);
|
|
4336
|
-
if (error.stack)
|
|
4337
|
-
getLogger().error(error.stack);
|
|
4435
|
+
if (error.stack) getLogger().error(error.stack);
|
|
4338
4436
|
process.exit(1);
|
|
4339
4437
|
}
|
|
4340
4438
|
});
|
|
@@ -4343,8 +4441,7 @@ program.command("serve").description("Start production server").option("-p, --pa
|
|
|
4343
4441
|
await runServe(options.path, options);
|
|
4344
4442
|
} catch (error) {
|
|
4345
4443
|
getLogger().error(chalk.red("Server failed:") + " " + error.message);
|
|
4346
|
-
if (error.stack)
|
|
4347
|
-
getLogger().error(error.stack);
|
|
4444
|
+
if (error.stack) getLogger().error(error.stack);
|
|
4348
4445
|
process.exit(1);
|
|
4349
4446
|
}
|
|
4350
4447
|
});
|
|
@@ -4353,8 +4450,7 @@ program.command("rebuild").description("Rebuild instances by contract, route, or
|
|
|
4353
4450
|
await runRebuild(options.path, options);
|
|
4354
4451
|
} catch (error) {
|
|
4355
4452
|
getLogger().error(chalk.red("Rebuild failed:") + " " + error.message);
|
|
4356
|
-
if (error.stack)
|
|
4357
|
-
getLogger().error(error.stack);
|
|
4453
|
+
if (error.stack) getLogger().error(error.stack);
|
|
4358
4454
|
process.exit(1);
|
|
4359
4455
|
}
|
|
4360
4456
|
});
|