@wcstack/lint 1.32.0 → 1.33.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/README.ja.md +35 -3
- package/README.md +35 -3
- package/dist/cli.cjs +829 -431
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -96,6 +96,9 @@ var WcsDiagnosticCode = {
|
|
|
96
96
|
// 同名 tag / filter の後勝ち禁止(§5-3)。override:true が無い再定義もこの collision で表す。
|
|
97
97
|
ManifestTagCollision: "wcs/manifest-tag-collision",
|
|
98
98
|
ManifestFilterCollision: "wcs/manifest-filter-collision",
|
|
99
|
+
// 同名 state の stateSchema が複数の application artifact に宣言されている(§5-3 の
|
|
100
|
+
// application 版・D8)。勝者なし: その state は未宣言扱い(schema 検証は沈黙)。
|
|
101
|
+
ManifestStateCollision: "wcs/manifest-state-collision",
|
|
99
102
|
// 明示 override:true(§5-4)。衝突ではなく意図的な shadow の告知(info)。
|
|
100
103
|
ManifestOverride: "wcs/manifest-override",
|
|
101
104
|
// --- sidecar vs live declaration drift ---
|
|
@@ -168,7 +171,11 @@ var WcsDiagnosticCode = {
|
|
|
168
171
|
// router/auto があるのに <base href> がない(SPA の basename 誤導出)。
|
|
169
172
|
BaseHrefMissing: "wcs/base-href-missing",
|
|
170
173
|
// @wcstack/signals と /dom エントリの同一ページ混在(リアクティブコア二重化)。
|
|
171
|
-
SignalsDualEntry: "wcs/signals-dual-entry"
|
|
174
|
+
SignalsDualEntry: "wcs/signals-dual-entry",
|
|
175
|
+
// --- deprecations ---
|
|
176
|
+
// 名前付き State(`<wcs-state name>` / `path@name`)。v2 でマウント(`mount=` と接頭辞付きパス)に
|
|
177
|
+
// 置き換わる(docs/state-mount-design.md D16)。1.x では warning、v2 では parse error と同時に error。
|
|
178
|
+
NamedStateDeprecated: "wcs/named-state-deprecated"
|
|
172
179
|
};
|
|
173
180
|
function sortDiagnostics(diagnostics) {
|
|
174
181
|
const severityRank = { error: 0, warning: 1, info: 2 };
|
|
@@ -909,226 +916,6 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
|
|
|
909
916
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
910
917
|
}));
|
|
911
918
|
|
|
912
|
-
// src/language/htmlParse.ts
|
|
913
|
-
function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
|
|
914
|
-
const blocks = [];
|
|
915
|
-
let pos = 0;
|
|
916
|
-
const len = html.length;
|
|
917
|
-
while (pos < len) {
|
|
918
|
-
if (html.startsWith("<!--", pos)) {
|
|
919
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
920
|
-
if (commentEnd === -1) break;
|
|
921
|
-
pos = commentEnd + 3;
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
925
|
-
if (wcsMatch === null) {
|
|
926
|
-
pos++;
|
|
927
|
-
continue;
|
|
928
|
-
}
|
|
929
|
-
const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
|
|
930
|
-
pos = wcsMatch.end;
|
|
931
|
-
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
932
|
-
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
933
|
-
while (pos < wcsEnd) {
|
|
934
|
-
if (html.startsWith("<!--", pos)) {
|
|
935
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
936
|
-
if (commentEnd === -1) break;
|
|
937
|
-
pos = commentEnd + 3;
|
|
938
|
-
continue;
|
|
939
|
-
}
|
|
940
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
941
|
-
if (scriptMatch === null) {
|
|
942
|
-
pos++;
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
946
|
-
if (typeAttr?.toLowerCase() !== "module") {
|
|
947
|
-
pos = scriptMatch.end;
|
|
948
|
-
continue;
|
|
949
|
-
}
|
|
950
|
-
const contentStart = scriptMatch.end;
|
|
951
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
952
|
-
if (scriptCloseIdx === -1) {
|
|
953
|
-
pos = contentStart;
|
|
954
|
-
break;
|
|
955
|
-
}
|
|
956
|
-
const contentEnd = scriptCloseIdx;
|
|
957
|
-
blocks.push({
|
|
958
|
-
contentStart,
|
|
959
|
-
contentEnd,
|
|
960
|
-
content: html.slice(contentStart, contentEnd),
|
|
961
|
-
stateName
|
|
962
|
-
});
|
|
963
|
-
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
964
|
-
if (pos === 0) break;
|
|
965
|
-
}
|
|
966
|
-
pos = wcsEnd;
|
|
967
|
-
if (wcsCloseIdx !== -1) {
|
|
968
|
-
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
969
|
-
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
return blocks;
|
|
973
|
-
}
|
|
974
|
-
function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
975
|
-
const elements = [];
|
|
976
|
-
let pos = 0;
|
|
977
|
-
const len = html.length;
|
|
978
|
-
while (pos < len) {
|
|
979
|
-
if (html.startsWith("<!--", pos)) {
|
|
980
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
981
|
-
if (commentEnd === -1) break;
|
|
982
|
-
pos = commentEnd + 3;
|
|
983
|
-
continue;
|
|
984
|
-
}
|
|
985
|
-
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
986
|
-
if (wcsMatch === null) {
|
|
987
|
-
pos++;
|
|
988
|
-
continue;
|
|
989
|
-
}
|
|
990
|
-
const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
|
|
991
|
-
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
992
|
-
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
993
|
-
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
994
|
-
const tagStart = pos;
|
|
995
|
-
const tagEnd = wcsMatch.end;
|
|
996
|
-
pos = wcsMatch.end;
|
|
997
|
-
const scriptBlocks = [];
|
|
998
|
-
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
999
|
-
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
1000
|
-
while (pos < wcsEnd) {
|
|
1001
|
-
if (html.startsWith("<!--", pos)) {
|
|
1002
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1003
|
-
if (commentEnd === -1) break;
|
|
1004
|
-
pos = commentEnd + 3;
|
|
1005
|
-
continue;
|
|
1006
|
-
}
|
|
1007
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1008
|
-
if (scriptMatch === null) {
|
|
1009
|
-
pos++;
|
|
1010
|
-
continue;
|
|
1011
|
-
}
|
|
1012
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1013
|
-
if (typeAttr?.toLowerCase() !== "module") {
|
|
1014
|
-
pos = scriptMatch.end;
|
|
1015
|
-
continue;
|
|
1016
|
-
}
|
|
1017
|
-
const contentStart = scriptMatch.end;
|
|
1018
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1019
|
-
if (scriptCloseIdx === -1) {
|
|
1020
|
-
pos = contentStart;
|
|
1021
|
-
break;
|
|
1022
|
-
}
|
|
1023
|
-
scriptBlocks.push({
|
|
1024
|
-
contentStart,
|
|
1025
|
-
contentEnd: scriptCloseIdx,
|
|
1026
|
-
content: html.slice(contentStart, scriptCloseIdx),
|
|
1027
|
-
stateName
|
|
1028
|
-
});
|
|
1029
|
-
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1030
|
-
if (pos === 0) break;
|
|
1031
|
-
}
|
|
1032
|
-
elements.push({ stateName, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
1033
|
-
pos = wcsEnd;
|
|
1034
|
-
if (wcsCloseIdx !== -1) {
|
|
1035
|
-
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
1036
|
-
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
return elements;
|
|
1040
|
-
}
|
|
1041
|
-
function findScriptJsonById(html, id2) {
|
|
1042
|
-
let pos = 0;
|
|
1043
|
-
const len = html.length;
|
|
1044
|
-
while (pos < len) {
|
|
1045
|
-
if (html.startsWith("<!--", pos)) {
|
|
1046
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1047
|
-
if (commentEnd === -1) break;
|
|
1048
|
-
pos = commentEnd + 3;
|
|
1049
|
-
continue;
|
|
1050
|
-
}
|
|
1051
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1052
|
-
if (scriptMatch === null) {
|
|
1053
|
-
pos++;
|
|
1054
|
-
continue;
|
|
1055
|
-
}
|
|
1056
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1057
|
-
const idAttr = extractAttribute(scriptMatch.tagContent, "id");
|
|
1058
|
-
if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
|
|
1059
|
-
const contentStart = scriptMatch.end;
|
|
1060
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1061
|
-
if (scriptCloseIdx === -1) return null;
|
|
1062
|
-
return html.slice(contentStart, scriptCloseIdx);
|
|
1063
|
-
}
|
|
1064
|
-
pos = scriptMatch.end;
|
|
1065
|
-
}
|
|
1066
|
-
return null;
|
|
1067
|
-
}
|
|
1068
|
-
function matchOpenTag(html, pos, tagName) {
|
|
1069
|
-
if (html[pos] !== "<") return null;
|
|
1070
|
-
const nameStart = pos + 1;
|
|
1071
|
-
const nameEnd = nameStart + tagName.length;
|
|
1072
|
-
if (nameEnd > html.length) return null;
|
|
1073
|
-
const slice3 = html.slice(nameStart, nameEnd);
|
|
1074
|
-
if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
|
|
1075
|
-
const charAfter = html[nameEnd];
|
|
1076
|
-
if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
|
|
1077
|
-
return null;
|
|
1078
|
-
}
|
|
1079
|
-
let i = nameEnd;
|
|
1080
|
-
let inSingleQuote = false;
|
|
1081
|
-
let inDoubleQuote = false;
|
|
1082
|
-
while (i < html.length) {
|
|
1083
|
-
const ch = html[i];
|
|
1084
|
-
if (inSingleQuote) {
|
|
1085
|
-
if (ch === "'") inSingleQuote = false;
|
|
1086
|
-
} else if (inDoubleQuote) {
|
|
1087
|
-
if (ch === '"') inDoubleQuote = false;
|
|
1088
|
-
} else if (ch === "'") {
|
|
1089
|
-
inSingleQuote = true;
|
|
1090
|
-
} else if (ch === '"') {
|
|
1091
|
-
inDoubleQuote = true;
|
|
1092
|
-
} else if (ch === ">") {
|
|
1093
|
-
return {
|
|
1094
|
-
start: pos,
|
|
1095
|
-
end: i + 1,
|
|
1096
|
-
tagContent: html.slice(nameEnd, i)
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
i++;
|
|
1100
|
-
}
|
|
1101
|
-
return null;
|
|
1102
|
-
}
|
|
1103
|
-
function findCloseTag(html, startPos, tagName) {
|
|
1104
|
-
const pattern = "</" + tagName;
|
|
1105
|
-
const patternLower = pattern.toLowerCase();
|
|
1106
|
-
const htmlLower = html.toLowerCase();
|
|
1107
|
-
let pos = startPos;
|
|
1108
|
-
while (pos < html.length) {
|
|
1109
|
-
const idx = htmlLower.indexOf(patternLower, pos);
|
|
1110
|
-
if (idx === -1) return -1;
|
|
1111
|
-
const afterIdx = idx + pattern.length;
|
|
1112
|
-
if (afterIdx < html.length) {
|
|
1113
|
-
const ch = html[afterIdx];
|
|
1114
|
-
if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
1115
|
-
return idx;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
pos = idx + 1;
|
|
1119
|
-
}
|
|
1120
|
-
return -1;
|
|
1121
|
-
}
|
|
1122
|
-
function extractAttribute(tagContent, attrName) {
|
|
1123
|
-
const regex = new RegExp(
|
|
1124
|
-
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
1125
|
-
"i"
|
|
1126
|
-
);
|
|
1127
|
-
const match = tagContent.match(regex);
|
|
1128
|
-
if (!match) return null;
|
|
1129
|
-
return match[1] ?? match[2] ?? match[3] ?? null;
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
919
|
// src/service/stateAnalyzer.ts
|
|
1133
920
|
var RESERVED_STREAMS_KEY = "$streams";
|
|
1134
921
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
@@ -1622,32 +1409,361 @@ function inferTypeHint(valueStart) {
|
|
|
1622
1409
|
if (v.startsWith("{")) return "object";
|
|
1623
1410
|
return void 0;
|
|
1624
1411
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1412
|
+
function analyzeSchemaPaths(schema, stateName = "default") {
|
|
1413
|
+
const paths = [];
|
|
1414
|
+
const defs = schema.$defs ?? {};
|
|
1415
|
+
collectSchemaObjectPaths(schema, "", paths, stateName, defs, 0);
|
|
1416
|
+
return paths;
|
|
1417
|
+
}
|
|
1418
|
+
function mergeSchemaCandidates(candidates, applicationStates) {
|
|
1419
|
+
if (applicationStates === void 0 || applicationStates.size === 0) return candidates;
|
|
1420
|
+
const schemaCandidates = [];
|
|
1421
|
+
const schemaKeys = /* @__PURE__ */ new Set();
|
|
1422
|
+
for (const [stateName, schema] of applicationStates) {
|
|
1423
|
+
for (const p of analyzeSchemaPaths(schema, stateName)) {
|
|
1424
|
+
schemaCandidates.push(p);
|
|
1425
|
+
schemaKeys.add(`${stateName} ${p.path}`);
|
|
1426
|
+
}
|
|
1633
1427
|
}
|
|
1634
|
-
|
|
1428
|
+
const kept = candidates.filter((p) => !schemaKeys.has(`${p.stateName} ${p.path}`));
|
|
1429
|
+
return [...kept, ...schemaCandidates];
|
|
1635
1430
|
}
|
|
1636
|
-
function
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1431
|
+
function derefSchemaNodes(node, defs) {
|
|
1432
|
+
const out = [];
|
|
1433
|
+
const stack = [{ node, chain: /* @__PURE__ */ new Set() }];
|
|
1434
|
+
while (stack.length > 0) {
|
|
1435
|
+
const { node: n, chain } = stack.pop();
|
|
1436
|
+
if (n === null || typeof n !== "object") continue;
|
|
1437
|
+
if (typeof n.$ref === "string") {
|
|
1438
|
+
const match = /^#\/\$defs\/(.+)$/.exec(n.$ref);
|
|
1439
|
+
if (match === null || chain.has(n.$ref)) continue;
|
|
1440
|
+
const target = defs[match[1].replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
1441
|
+
if (target === void 0) continue;
|
|
1442
|
+
stack.push({ node: target, chain: /* @__PURE__ */ new Set([...chain, n.$ref]) });
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (Array.isArray(n.anyOf)) {
|
|
1446
|
+
for (let i = n.anyOf.length - 1; i >= 0; i--) stack.push({ node: n.anyOf[i], chain });
|
|
1447
|
+
continue;
|
|
1642
1448
|
}
|
|
1449
|
+
out.push(n);
|
|
1643
1450
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1451
|
+
return out;
|
|
1452
|
+
}
|
|
1453
|
+
function schemaTypeHint(nodes) {
|
|
1454
|
+
const hints = /* @__PURE__ */ new Set();
|
|
1455
|
+
for (const n of nodes) {
|
|
1456
|
+
const types = typeof n.type === "string" ? [n.type] : Array.isArray(n.type) ? n.type : [];
|
|
1457
|
+
if (types.length > 0) {
|
|
1458
|
+
for (const t of types) {
|
|
1459
|
+
if (t === "null") continue;
|
|
1460
|
+
hints.add(t === "integer" ? "number" : t);
|
|
1461
|
+
}
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
if (Array.isArray(n.enum)) {
|
|
1465
|
+
for (const v of n.enum) {
|
|
1466
|
+
const h = inferJsonTypeHint(v);
|
|
1467
|
+
if (h !== void 0 && h !== "null") hints.add(h);
|
|
1468
|
+
}
|
|
1469
|
+
} else if (n.const !== void 0) {
|
|
1470
|
+
const h = inferJsonTypeHint(n.const);
|
|
1471
|
+
if (h !== void 0 && h !== "null") hints.add(h);
|
|
1472
|
+
} else if (n.properties !== void 0) {
|
|
1473
|
+
hints.add("object");
|
|
1474
|
+
} else if (n.items !== void 0) {
|
|
1475
|
+
hints.add("array");
|
|
1476
|
+
}
|
|
1647
1477
|
}
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1478
|
+
return hints.size === 0 ? void 0 : [...hints].join("|");
|
|
1479
|
+
}
|
|
1480
|
+
function collectSchemaObjectPaths(node, prefix, paths, stateName, defs, depth) {
|
|
1481
|
+
if (depth >= MAX_OBJECT_NEST_DEPTH) return;
|
|
1482
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1483
|
+
for (const n of derefSchemaNodes(node, defs)) {
|
|
1484
|
+
for (const [key, child] of Object.entries(n.properties ?? {})) {
|
|
1485
|
+
if (prefix === "" && key.startsWith("$")) continue;
|
|
1486
|
+
if (seen.has(key)) continue;
|
|
1487
|
+
seen.add(key);
|
|
1488
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
1489
|
+
pushSchemaValuePaths(path, child, paths, stateName, defs, depth);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
function pushSchemaValuePaths(path, node, paths, stateName, defs, depth) {
|
|
1494
|
+
const nodes = derefSchemaNodes(node, defs);
|
|
1495
|
+
const typeHint = schemaTypeHint(nodes);
|
|
1496
|
+
paths.push(withHint({ path, kind: "data", stateName, fromSchema: true }, typeHint));
|
|
1497
|
+
const items = nodes.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
|
|
1498
|
+
const isArray = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
|
|
1499
|
+
if (isArray) {
|
|
1500
|
+
const itemNodes = items !== void 0 ? derefSchemaNodes(items, defs) : [];
|
|
1501
|
+
paths.push(withHint({ path: `${path}.*`, kind: "list", stateName, fromSchema: true }, schemaTypeHint(itemNodes)));
|
|
1502
|
+
paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", stateName, fromSchema: true });
|
|
1503
|
+
if (depth >= MAX_OBJECT_NEST_DEPTH) return;
|
|
1504
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1505
|
+
for (const n of itemNodes) {
|
|
1506
|
+
for (const [childKey, childNode] of Object.entries(n.properties ?? {})) {
|
|
1507
|
+
if (seen.has(childKey)) continue;
|
|
1508
|
+
seen.add(childKey);
|
|
1509
|
+
pushSchemaValuePaths(`${path}.*.${childKey}`, childNode, paths, stateName, defs, depth + 1);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
if (nodes.some((n) => n.properties !== void 0)) {
|
|
1515
|
+
collectSchemaObjectPaths(node, path, paths, stateName, defs, depth + 1);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function withHint(candidate, typeHint) {
|
|
1519
|
+
return typeHint === void 0 ? candidate : { ...candidate, typeHint };
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
// src/language/htmlParse.ts
|
|
1523
|
+
function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
|
|
1524
|
+
const blocks = [];
|
|
1525
|
+
let pos = 0;
|
|
1526
|
+
const len = html.length;
|
|
1527
|
+
while (pos < len) {
|
|
1528
|
+
if (html.startsWith("<!--", pos)) {
|
|
1529
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1530
|
+
if (commentEnd === -1) break;
|
|
1531
|
+
pos = commentEnd + 3;
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
1535
|
+
if (wcsMatch === null) {
|
|
1536
|
+
pos++;
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
|
|
1540
|
+
pos = wcsMatch.end;
|
|
1541
|
+
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
1542
|
+
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
1543
|
+
while (pos < wcsEnd) {
|
|
1544
|
+
if (html.startsWith("<!--", pos)) {
|
|
1545
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1546
|
+
if (commentEnd === -1) break;
|
|
1547
|
+
pos = commentEnd + 3;
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1551
|
+
if (scriptMatch === null) {
|
|
1552
|
+
pos++;
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1556
|
+
if (typeAttr?.toLowerCase() !== "module") {
|
|
1557
|
+
pos = scriptMatch.end;
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
const contentStart = scriptMatch.end;
|
|
1561
|
+
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1562
|
+
if (scriptCloseIdx === -1) {
|
|
1563
|
+
pos = contentStart;
|
|
1564
|
+
break;
|
|
1565
|
+
}
|
|
1566
|
+
const contentEnd = scriptCloseIdx;
|
|
1567
|
+
blocks.push({
|
|
1568
|
+
contentStart,
|
|
1569
|
+
contentEnd,
|
|
1570
|
+
content: html.slice(contentStart, contentEnd),
|
|
1571
|
+
stateName
|
|
1572
|
+
});
|
|
1573
|
+
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1574
|
+
if (pos === 0) break;
|
|
1575
|
+
}
|
|
1576
|
+
pos = wcsEnd;
|
|
1577
|
+
if (wcsCloseIdx !== -1) {
|
|
1578
|
+
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
1579
|
+
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
return blocks;
|
|
1583
|
+
}
|
|
1584
|
+
function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
1585
|
+
const elements = [];
|
|
1586
|
+
let pos = 0;
|
|
1587
|
+
const len = html.length;
|
|
1588
|
+
while (pos < len) {
|
|
1589
|
+
if (html.startsWith("<!--", pos)) {
|
|
1590
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1591
|
+
if (commentEnd === -1) break;
|
|
1592
|
+
pos = commentEnd + 3;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
1596
|
+
if (wcsMatch === null) {
|
|
1597
|
+
pos++;
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
|
|
1601
|
+
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
1602
|
+
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
1603
|
+
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
1604
|
+
const tagStart = pos;
|
|
1605
|
+
const tagEnd = wcsMatch.end;
|
|
1606
|
+
pos = wcsMatch.end;
|
|
1607
|
+
const scriptBlocks = [];
|
|
1608
|
+
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
1609
|
+
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
1610
|
+
while (pos < wcsEnd) {
|
|
1611
|
+
if (html.startsWith("<!--", pos)) {
|
|
1612
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1613
|
+
if (commentEnd === -1) break;
|
|
1614
|
+
pos = commentEnd + 3;
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1618
|
+
if (scriptMatch === null) {
|
|
1619
|
+
pos++;
|
|
1620
|
+
continue;
|
|
1621
|
+
}
|
|
1622
|
+
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1623
|
+
if (typeAttr?.toLowerCase() !== "module") {
|
|
1624
|
+
pos = scriptMatch.end;
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
const contentStart = scriptMatch.end;
|
|
1628
|
+
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1629
|
+
if (scriptCloseIdx === -1) {
|
|
1630
|
+
pos = contentStart;
|
|
1631
|
+
break;
|
|
1632
|
+
}
|
|
1633
|
+
scriptBlocks.push({
|
|
1634
|
+
contentStart,
|
|
1635
|
+
contentEnd: scriptCloseIdx,
|
|
1636
|
+
content: html.slice(contentStart, scriptCloseIdx),
|
|
1637
|
+
stateName
|
|
1638
|
+
});
|
|
1639
|
+
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1640
|
+
if (pos === 0) break;
|
|
1641
|
+
}
|
|
1642
|
+
elements.push({ stateName, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
1643
|
+
pos = wcsEnd;
|
|
1644
|
+
if (wcsCloseIdx !== -1) {
|
|
1645
|
+
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
1646
|
+
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
return elements;
|
|
1650
|
+
}
|
|
1651
|
+
function findScriptJsonById(html, id2) {
|
|
1652
|
+
let pos = 0;
|
|
1653
|
+
const len = html.length;
|
|
1654
|
+
while (pos < len) {
|
|
1655
|
+
if (html.startsWith("<!--", pos)) {
|
|
1656
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1657
|
+
if (commentEnd === -1) break;
|
|
1658
|
+
pos = commentEnd + 3;
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1662
|
+
if (scriptMatch === null) {
|
|
1663
|
+
pos++;
|
|
1664
|
+
continue;
|
|
1665
|
+
}
|
|
1666
|
+
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1667
|
+
const idAttr = extractAttribute(scriptMatch.tagContent, "id");
|
|
1668
|
+
if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
|
|
1669
|
+
const contentStart = scriptMatch.end;
|
|
1670
|
+
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1671
|
+
if (scriptCloseIdx === -1) return null;
|
|
1672
|
+
return html.slice(contentStart, scriptCloseIdx);
|
|
1673
|
+
}
|
|
1674
|
+
pos = scriptMatch.end;
|
|
1675
|
+
}
|
|
1676
|
+
return null;
|
|
1677
|
+
}
|
|
1678
|
+
function matchOpenTag(html, pos, tagName) {
|
|
1679
|
+
if (html[pos] !== "<") return null;
|
|
1680
|
+
const nameStart = pos + 1;
|
|
1681
|
+
const nameEnd = nameStart + tagName.length;
|
|
1682
|
+
if (nameEnd > html.length) return null;
|
|
1683
|
+
const slice3 = html.slice(nameStart, nameEnd);
|
|
1684
|
+
if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
|
|
1685
|
+
const charAfter = html[nameEnd];
|
|
1686
|
+
if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
|
|
1687
|
+
return null;
|
|
1688
|
+
}
|
|
1689
|
+
let i = nameEnd;
|
|
1690
|
+
let inSingleQuote = false;
|
|
1691
|
+
let inDoubleQuote = false;
|
|
1692
|
+
while (i < html.length) {
|
|
1693
|
+
const ch = html[i];
|
|
1694
|
+
if (inSingleQuote) {
|
|
1695
|
+
if (ch === "'") inSingleQuote = false;
|
|
1696
|
+
} else if (inDoubleQuote) {
|
|
1697
|
+
if (ch === '"') inDoubleQuote = false;
|
|
1698
|
+
} else if (ch === "'") {
|
|
1699
|
+
inSingleQuote = true;
|
|
1700
|
+
} else if (ch === '"') {
|
|
1701
|
+
inDoubleQuote = true;
|
|
1702
|
+
} else if (ch === ">") {
|
|
1703
|
+
return {
|
|
1704
|
+
start: pos,
|
|
1705
|
+
end: i + 1,
|
|
1706
|
+
tagContent: html.slice(nameEnd, i)
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
i++;
|
|
1710
|
+
}
|
|
1711
|
+
return null;
|
|
1712
|
+
}
|
|
1713
|
+
function findCloseTag(html, startPos, tagName) {
|
|
1714
|
+
const pattern = "</" + tagName;
|
|
1715
|
+
const patternLower = pattern.toLowerCase();
|
|
1716
|
+
const htmlLower = html.toLowerCase();
|
|
1717
|
+
let pos = startPos;
|
|
1718
|
+
while (pos < html.length) {
|
|
1719
|
+
const idx = htmlLower.indexOf(patternLower, pos);
|
|
1720
|
+
if (idx === -1) return -1;
|
|
1721
|
+
const afterIdx = idx + pattern.length;
|
|
1722
|
+
if (afterIdx < html.length) {
|
|
1723
|
+
const ch = html[afterIdx];
|
|
1724
|
+
if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
1725
|
+
return idx;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
pos = idx + 1;
|
|
1729
|
+
}
|
|
1730
|
+
return -1;
|
|
1731
|
+
}
|
|
1732
|
+
function extractAttribute(tagContent, attrName) {
|
|
1733
|
+
const regex = new RegExp(
|
|
1734
|
+
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
1735
|
+
"i"
|
|
1736
|
+
);
|
|
1737
|
+
const match = tagContent.match(regex);
|
|
1738
|
+
if (!match) return null;
|
|
1739
|
+
return match[1] ?? match[2] ?? match[3] ?? null;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
// src/service/statePathResolver.ts
|
|
1743
|
+
function getStatePathsFromHtml(html, stateTagName = "wcs-state", fileReader) {
|
|
1744
|
+
const elements = parseWcsStateElements(html, stateTagName);
|
|
1745
|
+
const allPaths = [];
|
|
1746
|
+
for (const element of elements) {
|
|
1747
|
+
const paths = resolveElementPaths(element, html, fileReader);
|
|
1748
|
+
allPaths.push(...paths);
|
|
1749
|
+
}
|
|
1750
|
+
return allPaths;
|
|
1751
|
+
}
|
|
1752
|
+
function resolveElementPaths(element, html, fileReader) {
|
|
1753
|
+
if (element.stateAttr) {
|
|
1754
|
+
const jsonContent = findScriptJsonById(html, element.stateAttr);
|
|
1755
|
+
if (jsonContent) {
|
|
1756
|
+
const paths = analyzeJsonPaths(jsonContent, element.stateName);
|
|
1757
|
+
if (paths.length > 0) return paths;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
if (element.srcAttr && fileReader) {
|
|
1761
|
+
const paths = resolveSrcAttribute(element.srcAttr, element.stateName, fileReader);
|
|
1762
|
+
if (paths.length > 0) return paths;
|
|
1763
|
+
}
|
|
1764
|
+
if (element.jsonAttr) {
|
|
1765
|
+
const paths = analyzeJsonPaths(element.jsonAttr, element.stateName);
|
|
1766
|
+
if (paths.length > 0) return paths;
|
|
1651
1767
|
}
|
|
1652
1768
|
if (element.scriptBlocks.length > 0) {
|
|
1653
1769
|
return element.scriptBlocks.flatMap(
|
|
@@ -1812,6 +1928,8 @@ var ja = {
|
|
|
1812
1928
|
commandTokenUndeclared: (t) => `\u30B3\u30DE\u30F3\u30C9\u30C8\u30FC\u30AF\u30F3 "${t}" \u306F $commandTokens \u306B\u5BA3\u8A00\u3055\u308C\u3066\u3044\u307E\u305B\u3093`,
|
|
1813
1929
|
streamPathMissing: (p) => `\u30D1\u30B9 "${p}" \u306F $streams \u5BA3\u8A00\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
|
|
1814
1930
|
pathMissing: (p) => `\u30D1\u30B9 "${p}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
|
|
1931
|
+
pathNonexistent: (p) => `\u30D1\u30B9 "${p}" \u306F\u5BA3\u8A00\u3055\u308C\u305F stateSchema \u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
|
|
1932
|
+
pathTypeMismatch: (p, label, expected, actual) => `\u30D1\u30B9 "${p}" \u306F stateSchema \u4E0A\u3067 ${actual} \u578B\u3067\u3059\u304C\u3001${label} \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059`,
|
|
1815
1933
|
expansionSuffix: (x) => `\uFF08\u5C55\u958B: ${x}\uFF09`,
|
|
1816
1934
|
patternPathOutsideFor: (p) => `\u30D1\u30BF\u30FC\u30F3\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
|
|
1817
1935
|
omittedPathOutsideFor: (p) => `\u7701\u7565\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
|
|
@@ -1851,7 +1969,9 @@ var ja = {
|
|
|
1851
1969
|
storageSeedClobber: (path, raw) => `<wcs-storage> \u306E value \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C ${raw} \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u521D\u671F\u66F8\u304D\u623B\u3057\u304C\u4FDD\u5B58\u5024\u3092\u4E0A\u66F8\u304D\u3057\u307E\u3059 \u2014 undefined \u3067\u30B7\u30FC\u30C9\uFF08\`${path}: undefined\`\uFF09\u3059\u308B\u304B manual \u3092\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044`,
|
|
1852
1970
|
devtoolsAfterState: () => `@wcstack/devtools/auto \u306F @wcstack/state/auto \u3088\u308A\u5148\u306B\u8AAD\u307F\u8FBC\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u5F8C\u3060\u3068\u914D\u7DDA\u53F0\u5E33\u304C\u30E9\u30A4\u30D6\u3067 captured \u3055\u308C\u307E\u305B\u3093\uFF09`,
|
|
1853
1971
|
baseHrefMissing: () => `@wcstack/router \u3092\u4F7F\u3046 SPA \u306B\u306F <head> \u5185\u306E <base href="/"> \u304C\u5FC5\u8981\u3067\u3059\uFF08\u7121\u3044\u3068\u30C7\u30A3\u30FC\u30D7\u30EA\u30F3\u30AF\u3067 basename \u304C\u8AA4\u5C0E\u51FA\u3055\u308C\u307E\u3059\uFF09`,
|
|
1854
|
-
signalsDualEntry: () => `@wcstack/signals \u3068 @wcstack/signals/dom \u304C\u540C\u4E00\u30DA\u30FC\u30B8\u304B\u3089 import \u3055\u308C\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u81EA\u5DF1\u5B8C\u7D50\u30D0\u30F3\u30C9\u30EB\u306E\u305F\u3081\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u304C\u4E8C\u91CD\u5316\u3057\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059 \u2014 \u3059\u3079\u3066 /dom \u30A8\u30F3\u30C8\u30EA\u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044
|
|
1972
|
+
signalsDualEntry: () => `@wcstack/signals \u3068 @wcstack/signals/dom \u304C\u540C\u4E00\u30DA\u30FC\u30B8\u304B\u3089 import \u3055\u308C\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u81EA\u5DF1\u5B8C\u7D50\u30D0\u30F3\u30C9\u30EB\u306E\u305F\u3081\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u304C\u4E8C\u91CD\u5316\u3057\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059 \u2014 \u3059\u3079\u3066 /dom \u30A8\u30F3\u30C8\u30EA\u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
1973
|
+
namedStateAttrDeprecated: (name) => `<wcs-state name="${name}"> \u306F v2 \u3067\u5EC3\u6B62\u3055\u308C\u307E\u3059\u3002\u30EB\u30FC\u30C8\u30C4\u30EA\u30FC\u3078\u306E\u30DE\u30A6\u30F3\u30C8 <wcs-state mount="${name}"> \u306B\u7F6E\u304D\u63DB\u3048\u3001\u30D1\u30B9\u306F "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`,
|
|
1974
|
+
namedStatePathDeprecated: (name) => name === "default" ? `"@default" \u306F\u4E0D\u8981\u3067\u3001v2 \u3067\u5EC3\u6B62\u3055\u308C\u307E\u3059\u3002"@default" \u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09` : `"@${name}" \u306B\u3088\u308B state \u6307\u5B9A\u306F v2 \u3067\u5EC3\u6B62\u3055\u308C\u307E\u3059\u3002\u30DE\u30A6\u30F3\u30C8\u3057\u305F\u30C4\u30EA\u30FC\u3092 "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`
|
|
1855
1975
|
};
|
|
1856
1976
|
var EN_EXPECTED_LABEL = {
|
|
1857
1977
|
array: "an array-typed path",
|
|
@@ -1867,6 +1987,8 @@ var en = {
|
|
|
1867
1987
|
commandTokenUndeclared: (t) => `Command token "${t}" is not declared in $commandTokens`,
|
|
1868
1988
|
streamPathMissing: (p) => `Path "${p}" does not exist in the $streams declaration`,
|
|
1869
1989
|
pathMissing: (p) => `Path "${p}" does not exist in the state definition`,
|
|
1990
|
+
pathNonexistent: (p) => `Path "${p}" does not exist in the declared stateSchema`,
|
|
1991
|
+
pathTypeMismatch: (p, label, expected, actual) => `Path "${p}" is ${actual} in the stateSchema, but ${label} requires ${expected === "array" ? "an array" : expected === "boolean" ? "a boolean" : "a string"}`,
|
|
1870
1992
|
expansionSuffix: (x) => ` (expanded: ${x})`,
|
|
1871
1993
|
patternPathOutsideFor: (p) => `Pattern path "${p}" cannot be used outside a <template for>`,
|
|
1872
1994
|
omittedPathOutsideFor: (p) => `Shorthand path "${p}" cannot be used outside a <template for>`,
|
|
@@ -1906,19 +2028,213 @@ var en = {
|
|
|
1906
2028
|
storageSeedClobber: (path, raw) => `The <wcs-storage> value-bound slot "${path}" is seeded with ${raw}. The initial write-back overwrites the persisted value \u2014 seed it with undefined (\`${path}: undefined\`) or add manual`,
|
|
1907
2029
|
devtoolsAfterState: () => `Load @wcstack/devtools/auto BEFORE @wcstack/state/auto (otherwise the wiring ledger is not captured live)`,
|
|
1908
2030
|
baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
|
|
1909
|
-
signalsDualEntry: () => `Both @wcstack/signals and @wcstack/signals/dom are imported on this page. On a CDN each entry is a self-contained bundle, so the reactive core is duplicated and reactivity breaks at the seam \u2014 import everything from the single /dom entry
|
|
2031
|
+
signalsDualEntry: () => `Both @wcstack/signals and @wcstack/signals/dom are imported on this page. On a CDN each entry is a self-contained bundle, so the reactive core is duplicated and reactivity breaks at the seam \u2014 import everything from the single /dom entry`,
|
|
2032
|
+
namedStateAttrDeprecated: (name) => `<wcs-state name="${name}"> is deprecated and will be removed in v2. Mount the state onto the root tree with <wcs-state mount="${name}"> and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
|
|
2033
|
+
namedStatePathDeprecated: (name) => name === "default" ? `The "@default" selector is redundant and will be removed in v2; drop it (docs/state-mount-design.md \xA79)` : `The "@${name}" state selector is deprecated and will be removed in v2. Read the mounted tree as "${name}.<path>" instead (docs/state-mount-design.md \xA79)`
|
|
1910
2034
|
};
|
|
1911
2035
|
var CATALOGS = { ja, en };
|
|
1912
2036
|
function getMessages(locale3) {
|
|
1913
2037
|
return CATALOGS[resolveLocale(locale3)];
|
|
1914
2038
|
}
|
|
1915
2039
|
|
|
2040
|
+
// src/core/sidecar/schemaSubset.ts
|
|
2041
|
+
var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
2042
|
+
"type",
|
|
2043
|
+
"properties",
|
|
2044
|
+
"required",
|
|
2045
|
+
"items",
|
|
2046
|
+
"enum",
|
|
2047
|
+
"const",
|
|
2048
|
+
"anyOf",
|
|
2049
|
+
"$defs",
|
|
2050
|
+
"$ref"
|
|
2051
|
+
]);
|
|
2052
|
+
var DiagnosticContext = class {
|
|
2053
|
+
constructor(spans) {
|
|
2054
|
+
this.spans = spans;
|
|
2055
|
+
}
|
|
2056
|
+
diagnostics = [];
|
|
2057
|
+
add(code, pointer2, message, severity, extra = {}, useKeySpan = false) {
|
|
2058
|
+
const span = this.spans.get(pointer2);
|
|
2059
|
+
const start = span === void 0 ? 0 : useKeySpan ? span.keyStart ?? span.start : span.start;
|
|
2060
|
+
const end = span === void 0 ? 0 : useKeySpan ? span.keyEnd ?? span.end : span.end;
|
|
2061
|
+
this.diagnostics.push({ code, start, end, message, severity, ...extra });
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
function isSchemaObject(value) {
|
|
2065
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2066
|
+
}
|
|
2067
|
+
function isSchemaMap(value) {
|
|
2068
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2069
|
+
}
|
|
2070
|
+
function validateSchemaSubset(schema, pointerBase, ctx, rootDefs) {
|
|
2071
|
+
walkKeywords(schema, pointerBase, ctx, rootDefs);
|
|
2072
|
+
const safe = /* @__PURE__ */ new Set();
|
|
2073
|
+
detectCycles(schema, pointerBase, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
|
|
2074
|
+
for (const [name, def] of Object.entries(rootDefs)) {
|
|
2075
|
+
detectCycles(def, `${pointerBase}/$defs/${escape(name)}`, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
function walkKeywords(node, ptr, ctx, rootDefs) {
|
|
2079
|
+
if (!isSchemaObject(node)) return;
|
|
2080
|
+
for (const keyword of Object.keys(node)) {
|
|
2081
|
+
if (!ALLOWED_SCHEMA_KEYWORDS.has(keyword)) {
|
|
2082
|
+
ctx.add(
|
|
2083
|
+
WcsDiagnosticCode.ManifestUnknownKeyword,
|
|
2084
|
+
`${ptr}/${escape(keyword)}`,
|
|
2085
|
+
`Unsupported schema keyword "${keyword}". Allowed: ${[...ALLOWED_SCHEMA_KEYWORDS].join(", ")}.`,
|
|
2086
|
+
"warning",
|
|
2087
|
+
{},
|
|
2088
|
+
true
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (typeof node.$ref === "string") {
|
|
2093
|
+
if (!node.$ref.startsWith("#/")) {
|
|
2094
|
+
ctx.add(
|
|
2095
|
+
WcsDiagnosticCode.ManifestExternalRef,
|
|
2096
|
+
`${ptr}/$ref`,
|
|
2097
|
+
`External $ref "${node.$ref}" is forbidden; only local "#/$defs/..." references are allowed.`,
|
|
2098
|
+
"error"
|
|
2099
|
+
);
|
|
2100
|
+
} else if (resolveLocalRef(node.$ref, rootDefs) === void 0) {
|
|
2101
|
+
ctx.add(
|
|
2102
|
+
WcsDiagnosticCode.ManifestRefUnresolved,
|
|
2103
|
+
`${ptr}/$ref`,
|
|
2104
|
+
`Unresolved local $ref "${node.$ref}".`,
|
|
2105
|
+
"error"
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
if (isSchemaMap(node.properties)) {
|
|
2110
|
+
for (const [name, child] of Object.entries(node.properties)) {
|
|
2111
|
+
walkKeywords(child, `${ptr}/properties/${escape(name)}`, ctx, rootDefs);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
if (node.items !== void 0 && isSchemaObject(node.items)) {
|
|
2115
|
+
walkKeywords(node.items, `${ptr}/items`, ctx, rootDefs);
|
|
2116
|
+
}
|
|
2117
|
+
if (Array.isArray(node.anyOf)) {
|
|
2118
|
+
node.anyOf.forEach((child, i) => walkKeywords(child, `${ptr}/anyOf/${i}`, ctx, rootDefs));
|
|
2119
|
+
}
|
|
2120
|
+
if (isSchemaMap(node.$defs)) {
|
|
2121
|
+
for (const [name, child] of Object.entries(node.$defs)) {
|
|
2122
|
+
walkKeywords(child, `${ptr}/$defs/${escape(name)}`, ctx, rootDefs);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
function detectCycles(node, ptr, ctx, rootDefs, refStack, safe) {
|
|
2127
|
+
if (!isSchemaObject(node)) return;
|
|
2128
|
+
if (typeof node.$ref === "string") {
|
|
2129
|
+
const ref = node.$ref;
|
|
2130
|
+
if (!ref.startsWith("#/")) return;
|
|
2131
|
+
if (refStack.has(ref)) {
|
|
2132
|
+
ctx.add(WcsDiagnosticCode.ManifestRefCycle, `${ptr}/$ref`, `Cyclic $ref detected at "${ref}".`, "error");
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
if (safe.has(ref)) return;
|
|
2136
|
+
const target = resolveLocalRef(ref, rootDefs);
|
|
2137
|
+
if (target === void 0) return;
|
|
2138
|
+
refStack.add(ref);
|
|
2139
|
+
detectCycles(target, ptr, ctx, rootDefs, refStack, safe);
|
|
2140
|
+
refStack.delete(ref);
|
|
2141
|
+
safe.add(ref);
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
if (isSchemaMap(node.properties)) {
|
|
2145
|
+
for (const child of Object.values(node.properties)) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
|
|
2146
|
+
}
|
|
2147
|
+
if (node.items !== void 0 && isSchemaObject(node.items)) {
|
|
2148
|
+
detectCycles(node.items, ptr, ctx, rootDefs, refStack, safe);
|
|
2149
|
+
}
|
|
2150
|
+
if (Array.isArray(node.anyOf)) {
|
|
2151
|
+
for (const child of node.anyOf) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
function resolveLocalRef(ref, rootDefs) {
|
|
2155
|
+
const match = /^#\/\$defs\/(.+)$/.exec(ref);
|
|
2156
|
+
if (match === null) return void 0;
|
|
2157
|
+
const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
|
|
2158
|
+
return rootDefs[name];
|
|
2159
|
+
}
|
|
2160
|
+
function resolveSchemaPath(root, rootDefs, segments) {
|
|
2161
|
+
let current = root;
|
|
2162
|
+
for (let depth = 0; depth < segments.length; depth++) {
|
|
2163
|
+
const segment = segments[depth];
|
|
2164
|
+
const resolved = derefUnion(current, rootDefs);
|
|
2165
|
+
if (resolved.kind === "ref-error") return resolved;
|
|
2166
|
+
const candidates = resolved.nodes;
|
|
2167
|
+
if (segment === "*") {
|
|
2168
|
+
const items = firstDefined(candidates, (n) => isSchemaObject(n.items) ? n.items : void 0);
|
|
2169
|
+
if (items === void 0) {
|
|
2170
|
+
return { kind: "unknown" };
|
|
2171
|
+
}
|
|
2172
|
+
current = items;
|
|
2173
|
+
continue;
|
|
2174
|
+
}
|
|
2175
|
+
if (segment === "length" && candidates.some((n) => hasType(n, "array"))) {
|
|
2176
|
+
current = { type: "number" };
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
const child = firstDefined(candidates, (n) => isSchemaMap(n.properties) ? n.properties[segment] : void 0);
|
|
2180
|
+
if (child !== void 0) {
|
|
2181
|
+
current = child;
|
|
2182
|
+
continue;
|
|
2183
|
+
}
|
|
2184
|
+
const anyObject = candidates.some((n) => hasType(n, "object") || isSchemaMap(n.properties));
|
|
2185
|
+
if (anyObject) {
|
|
2186
|
+
return { kind: "nonexistent", segment, depth };
|
|
2187
|
+
}
|
|
2188
|
+
return { kind: "unknown" };
|
|
2189
|
+
}
|
|
2190
|
+
const final = derefUnion(current, rootDefs);
|
|
2191
|
+
if (final.kind === "ref-error") return final;
|
|
2192
|
+
return { kind: "resolved", schema: final.nodes.length === 1 ? final.nodes[0] : current };
|
|
2193
|
+
}
|
|
2194
|
+
function derefUnion(node, rootDefs) {
|
|
2195
|
+
const out = [];
|
|
2196
|
+
const stack = [{ node, chain: /* @__PURE__ */ new Set() }];
|
|
2197
|
+
while (stack.length > 0) {
|
|
2198
|
+
const { node: n, chain } = stack.pop();
|
|
2199
|
+
if (typeof n.$ref === "string") {
|
|
2200
|
+
if (!n.$ref.startsWith("#/") || chain.has(n.$ref)) {
|
|
2201
|
+
return { kind: "ref-error", ref: n.$ref };
|
|
2202
|
+
}
|
|
2203
|
+
const target = resolveLocalRef(n.$ref, rootDefs);
|
|
2204
|
+
if (target === void 0) return { kind: "ref-error", ref: n.$ref };
|
|
2205
|
+
stack.push({ node: target, chain: /* @__PURE__ */ new Set([...chain, n.$ref]) });
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
if (Array.isArray(n.anyOf)) {
|
|
2209
|
+
for (const branch of n.anyOf) stack.push({ node: branch, chain });
|
|
2210
|
+
continue;
|
|
2211
|
+
}
|
|
2212
|
+
out.push(n);
|
|
2213
|
+
}
|
|
2214
|
+
return { kind: "ok", nodes: out };
|
|
2215
|
+
}
|
|
2216
|
+
function firstDefined(nodes, pick) {
|
|
2217
|
+
for (const n of nodes) {
|
|
2218
|
+
const v = pick(n);
|
|
2219
|
+
if (v !== void 0) return v;
|
|
2220
|
+
}
|
|
2221
|
+
return void 0;
|
|
2222
|
+
}
|
|
2223
|
+
function hasType(node, t) {
|
|
2224
|
+
const type = node.type;
|
|
2225
|
+
if (type === void 0) return false;
|
|
2226
|
+
return Array.isArray(type) ? type.includes(t) : type === t;
|
|
2227
|
+
}
|
|
2228
|
+
function escape(key) {
|
|
2229
|
+
return key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2230
|
+
}
|
|
2231
|
+
|
|
1916
2232
|
// src/service/bindingValidator.ts
|
|
1917
2233
|
var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
|
|
1918
|
-
function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader) {
|
|
2234
|
+
function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader, applicationStates) {
|
|
1919
2235
|
const diagnostics = [];
|
|
1920
2236
|
const msgs = getMessages(locale3);
|
|
1921
|
-
const statePaths = getStatePathsFromHtml(html, stateTagName, fileReader);
|
|
2237
|
+
const statePaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
|
|
1922
2238
|
const pathsByState = /* @__PURE__ */ new Map();
|
|
1923
2239
|
for (const p of statePaths) {
|
|
1924
2240
|
const list = pathsByState.get(p.stateName) ?? [];
|
|
@@ -2041,16 +2357,17 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2041
2357
|
}
|
|
2042
2358
|
}
|
|
2043
2359
|
if (checkPath) {
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2360
|
+
const schema = applicationStates?.get(parsed.targetState);
|
|
2361
|
+
const verdict = schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2362
|
+
if (verdict) {
|
|
2046
2363
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2047
2364
|
const pathStart = bindingStart + pathOffset;
|
|
2048
2365
|
diagnostics.push({
|
|
2049
|
-
code:
|
|
2366
|
+
code: verdict.code,
|
|
2050
2367
|
start: pathStart,
|
|
2051
2368
|
end: pathStart + pathTrimmed.length,
|
|
2052
|
-
message: `${message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
|
|
2053
|
-
severity:
|
|
2369
|
+
message: `${verdict.message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
|
|
2370
|
+
severity: verdict.severity
|
|
2054
2371
|
});
|
|
2055
2372
|
}
|
|
2056
2373
|
}
|
|
@@ -2169,12 +2486,13 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2169
2486
|
if (typeReq && resultType !== typeReq.expected) {
|
|
2170
2487
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2171
2488
|
const pathStart = bindingStart + pathOffset;
|
|
2489
|
+
const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationStates?.has(parsed.targetState) === true && scopedPaths.some((p) => p.path === pathTrimmed && p.fromSchema === true);
|
|
2172
2490
|
diagnostics.push({
|
|
2173
|
-
code: WcsDiagnosticCode.BindingTypeExpectation,
|
|
2491
|
+
code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
|
|
2174
2492
|
start: pathStart,
|
|
2175
2493
|
end: pathStart + pathTrimmed.length,
|
|
2176
|
-
message: msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
|
|
2177
|
-
severity: typeReq.severity
|
|
2494
|
+
message: schemaDefinite ? msgs.pathTypeMismatch(pathTrimmed, typeReq.label, typeReq.expected, resultType) : msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
|
|
2495
|
+
severity: schemaDefinite ? "error" : typeReq.severity
|
|
2178
2496
|
});
|
|
2179
2497
|
}
|
|
2180
2498
|
}
|
|
@@ -2345,6 +2663,20 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
|
|
|
2345
2663
|
}
|
|
2346
2664
|
return null;
|
|
2347
2665
|
}
|
|
2666
|
+
function toMissingVerdict(message) {
|
|
2667
|
+
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
2668
|
+
}
|
|
2669
|
+
function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, schema, msgs) {
|
|
2670
|
+
if (checkPath.startsWith("$")) {
|
|
2671
|
+
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2672
|
+
}
|
|
2673
|
+
if (scopedPathSet.has(checkPath)) return null;
|
|
2674
|
+
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
2675
|
+
if (resolution.kind === "nonexistent") {
|
|
2676
|
+
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
2677
|
+
}
|
|
2678
|
+
return null;
|
|
2679
|
+
}
|
|
2348
2680
|
function collectStructuralTemplates(html, attrName) {
|
|
2349
2681
|
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2350
2682
|
const attrRegex = new RegExp(`${escaped}\\s*=\\s*(["'])`, "i");
|
|
@@ -2746,10 +3078,19 @@ function isInsideTag(html, offset, tagName) {
|
|
|
2746
3078
|
}
|
|
2747
3079
|
|
|
2748
3080
|
// src/service/templateSyntaxValidator.ts
|
|
2749
|
-
function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader) {
|
|
3081
|
+
function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader, applicationStates) {
|
|
2750
3082
|
const diagnostics = [];
|
|
2751
3083
|
const msgs = getMessages(locale3);
|
|
2752
|
-
const allPaths = getStatePathsFromHtml(html, stateTagName, fileReader);
|
|
3084
|
+
const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
|
|
3085
|
+
const defaultSchema = applicationStates?.get("default");
|
|
3086
|
+
const missingVerdict = (path, displayPath, pathSet2, scoped) => {
|
|
3087
|
+
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
3088
|
+
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
3089
|
+
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
3090
|
+
return resolution.kind === "nonexistent" ? { code: WcsDiagnosticCode.PathNonexistent, severity: "error", message: msgs.pathNonexistent(displayPath) } : null;
|
|
3091
|
+
}
|
|
3092
|
+
return { code: WcsDiagnosticCode.BindingPathMissing, severity: "warning", message: msgs.pathMissing(displayPath) };
|
|
3093
|
+
};
|
|
2753
3094
|
if (allPaths.length === 0) return diagnostics;
|
|
2754
3095
|
const defaultPaths = allPaths.filter((p) => p.stateName === "default");
|
|
2755
3096
|
const pathSet = new Set(defaultPaths.map((p) => p.path));
|
|
@@ -2829,24 +3170,28 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
2829
3170
|
const forPath = insideFor ? getInnermostForPath(html, item.matchStart, bindAttrName) : null;
|
|
2830
3171
|
if (forPath && !forPath.startsWith(".")) {
|
|
2831
3172
|
const expandedPath = pathPart === "." ? `${forPath}.*` : `${forPath}.*.${pathPart.slice(1)}`;
|
|
2832
|
-
|
|
3173
|
+
const verdict = missingVerdict(expandedPath, pathPart, pathSet, defaultPaths);
|
|
3174
|
+
if (verdict) {
|
|
2833
3175
|
diagnostics.push({
|
|
2834
|
-
code:
|
|
3176
|
+
code: verdict.code,
|
|
2835
3177
|
start: item.exprStart,
|
|
2836
3178
|
end: item.exprStart + pathPart.length,
|
|
2837
|
-
message:
|
|
2838
|
-
severity:
|
|
3179
|
+
message: verdict.message + msgs.expansionSuffix(expandedPath),
|
|
3180
|
+
severity: verdict.severity
|
|
2839
3181
|
});
|
|
2840
3182
|
}
|
|
2841
3183
|
}
|
|
2842
|
-
} else
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
3184
|
+
} else {
|
|
3185
|
+
const verdict = missingVerdict(pathPart, pathPart, pathSet, defaultPaths);
|
|
3186
|
+
if (verdict) {
|
|
3187
|
+
diagnostics.push({
|
|
3188
|
+
code: verdict.code,
|
|
3189
|
+
start: item.exprStart,
|
|
3190
|
+
end: item.exprStart + pathPart.length,
|
|
3191
|
+
message: verdict.message,
|
|
3192
|
+
severity: verdict.severity
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
2850
3195
|
}
|
|
2851
3196
|
}
|
|
2852
3197
|
for (let i = 1; i < parts.length; i++) {
|
|
@@ -3732,11 +4077,14 @@ var BUILTIN_TAGS = {
|
|
|
3732
4077
|
"wcs-raf": {
|
|
3733
4078
|
"package": "raf",
|
|
3734
4079
|
"hasWcBindable": true,
|
|
3735
|
-
"observedAttributes": [
|
|
4080
|
+
"observedAttributes": [
|
|
4081
|
+
"reduced-motion"
|
|
4082
|
+
],
|
|
3736
4083
|
"inputs": {
|
|
3737
4084
|
"once": "once",
|
|
3738
4085
|
"repeat": "repeat",
|
|
3739
4086
|
"manual": "manual",
|
|
4087
|
+
"reducedMotion": "reduced-motion",
|
|
3740
4088
|
"trigger": null
|
|
3741
4089
|
},
|
|
3742
4090
|
"properties": [
|
|
@@ -4617,6 +4965,78 @@ function validateEntry(entry, pathSet, msgs) {
|
|
|
4617
4965
|
return null;
|
|
4618
4966
|
}
|
|
4619
4967
|
|
|
4968
|
+
// src/service/namedStateValidator.ts
|
|
4969
|
+
function findStateSelector(expr, embedded = false) {
|
|
4970
|
+
const colon = embedded ? -1 : expr.indexOf(":");
|
|
4971
|
+
const from = colon + 1;
|
|
4972
|
+
let depth = 0;
|
|
4973
|
+
let end = expr.length;
|
|
4974
|
+
for (let i = from; i < expr.length; i++) {
|
|
4975
|
+
const ch = expr[i];
|
|
4976
|
+
if (ch === "(") depth++;
|
|
4977
|
+
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
4978
|
+
else if (ch === "|" && depth === 0) {
|
|
4979
|
+
end = i;
|
|
4980
|
+
break;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
const at = expr.indexOf("@", from);
|
|
4984
|
+
if (at === -1 || at >= end) return null;
|
|
4985
|
+
const raw = expr.slice(at + 1, end);
|
|
4986
|
+
const name = raw.trim();
|
|
4987
|
+
const nameStart = at + 1 + (raw.length - raw.trimStart().length);
|
|
4988
|
+
return { start: at, end: name.length === 0 ? at + 1 : nameStart + name.length, name: name.length === 0 ? "default" : name };
|
|
4989
|
+
}
|
|
4990
|
+
function validateNamedState(html, attrName, stateTagName = "wcs-state", locale3) {
|
|
4991
|
+
const msgs = getMessages(locale3);
|
|
4992
|
+
const diagnostics = [];
|
|
4993
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
4994
|
+
const tagText = html.slice(element.tagStart, element.tagEnd);
|
|
4995
|
+
if (/\sbind-component(?=[\s=>/])/i.test(tagText)) continue;
|
|
4996
|
+
const match = /(?:^|\s)name\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tagText);
|
|
4997
|
+
if (match === null) continue;
|
|
4998
|
+
const value = match[1] ?? match[2] ?? match[3] ?? "";
|
|
4999
|
+
const quoted = match[1] !== void 0 || match[2] !== void 0;
|
|
5000
|
+
const valueEnd = element.tagStart + match.index + match[0].length - (quoted ? 1 : 0);
|
|
5001
|
+
diagnostics.push({
|
|
5002
|
+
code: WcsDiagnosticCode.NamedStateDeprecated,
|
|
5003
|
+
start: valueEnd - value.length,
|
|
5004
|
+
end: valueEnd,
|
|
5005
|
+
message: msgs.namedStateAttrDeprecated(value),
|
|
5006
|
+
severity: "warning"
|
|
5007
|
+
});
|
|
5008
|
+
}
|
|
5009
|
+
for (const attr of findAllBindAttributes(html, attrName)) {
|
|
5010
|
+
let pos = 0;
|
|
5011
|
+
for (const expr of splitBindingExpressions(attr.value)) {
|
|
5012
|
+
const selector = findStateSelector(expr);
|
|
5013
|
+
if (selector !== null) {
|
|
5014
|
+
diagnostics.push({
|
|
5015
|
+
code: WcsDiagnosticCode.NamedStateDeprecated,
|
|
5016
|
+
start: attr.valueStart + pos + selector.start,
|
|
5017
|
+
end: attr.valueStart + pos + selector.end,
|
|
5018
|
+
message: msgs.namedStatePathDeprecated(selector.name),
|
|
5019
|
+
severity: "warning"
|
|
5020
|
+
});
|
|
5021
|
+
}
|
|
5022
|
+
pos += expr.length + 1;
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
5025
|
+
for (const mustache of findAllMustacheSyntax(html)) {
|
|
5026
|
+
const selector = findStateSelector(mustache.expression, true);
|
|
5027
|
+
if (selector !== null) {
|
|
5028
|
+
diagnostics.push({
|
|
5029
|
+
code: WcsDiagnosticCode.NamedStateDeprecated,
|
|
5030
|
+
start: mustache.exprStart + selector.start,
|
|
5031
|
+
end: mustache.exprStart + selector.end,
|
|
5032
|
+
message: msgs.namedStatePathDeprecated(selector.name),
|
|
5033
|
+
severity: "warning"
|
|
5034
|
+
});
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
return diagnostics;
|
|
5038
|
+
}
|
|
5039
|
+
|
|
4620
5040
|
// ../state/dist/parser.esm.js
|
|
4621
5041
|
var DELIMITER2 = ".";
|
|
4622
5042
|
var WILDCARD2 = "*";
|
|
@@ -5480,6 +5900,7 @@ function parseStatePart(statePart) {
|
|
|
5480
5900
|
} else {
|
|
5481
5901
|
stateAndPath = statePart.trim();
|
|
5482
5902
|
}
|
|
5903
|
+
if (stateAndPath.indexOf(STATE_NAME_SEPARATOR3) !== -1) ;
|
|
5483
5904
|
const [statePathName, stateName = "default"] = stateAndPath.split(STATE_NAME_SEPARATOR3).map(trimFn);
|
|
5484
5905
|
const pathInfo = getPathInfo(statePathName);
|
|
5485
5906
|
return {
|
|
@@ -6077,154 +6498,6 @@ function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrNa
|
|
|
6077
6498
|
return out;
|
|
6078
6499
|
}
|
|
6079
6500
|
|
|
6080
|
-
// src/core/validateDocument.ts
|
|
6081
|
-
function validateDocument(text, options = {}) {
|
|
6082
|
-
const bindAttribute = options.bindAttribute ?? "data-wcs";
|
|
6083
|
-
const stateTagName = options.stateTagName ?? "wcs-state";
|
|
6084
|
-
const locale3 = options.locale;
|
|
6085
|
-
const fileReader = options.fileReader;
|
|
6086
|
-
const out = [];
|
|
6087
|
-
out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader));
|
|
6088
|
-
out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader));
|
|
6089
|
-
out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
|
|
6090
|
-
out.push(...validateAriaAttributes(text, bindAttribute, locale3));
|
|
6091
|
-
out.push(...validateDocumentEnv(text, locale3));
|
|
6092
|
-
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
6093
|
-
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
6094
|
-
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
6095
|
-
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|
|
6096
|
-
out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
|
|
6097
|
-
}
|
|
6098
|
-
for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
|
|
6099
|
-
out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
|
|
6100
|
-
}
|
|
6101
|
-
return sortDiagnostics(out);
|
|
6102
|
-
}
|
|
6103
|
-
|
|
6104
|
-
// src/core/sidecar/schemaSubset.ts
|
|
6105
|
-
var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
6106
|
-
"type",
|
|
6107
|
-
"properties",
|
|
6108
|
-
"required",
|
|
6109
|
-
"items",
|
|
6110
|
-
"enum",
|
|
6111
|
-
"const",
|
|
6112
|
-
"anyOf",
|
|
6113
|
-
"$defs",
|
|
6114
|
-
"$ref"
|
|
6115
|
-
]);
|
|
6116
|
-
var DiagnosticContext = class {
|
|
6117
|
-
constructor(spans) {
|
|
6118
|
-
this.spans = spans;
|
|
6119
|
-
}
|
|
6120
|
-
diagnostics = [];
|
|
6121
|
-
add(code, pointer2, message, severity, extra = {}, useKeySpan = false) {
|
|
6122
|
-
const span = this.spans.get(pointer2);
|
|
6123
|
-
const start = span === void 0 ? 0 : useKeySpan ? span.keyStart ?? span.start : span.start;
|
|
6124
|
-
const end = span === void 0 ? 0 : useKeySpan ? span.keyEnd ?? span.end : span.end;
|
|
6125
|
-
this.diagnostics.push({ code, start, end, message, severity, ...extra });
|
|
6126
|
-
}
|
|
6127
|
-
};
|
|
6128
|
-
function isSchemaObject(value) {
|
|
6129
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6130
|
-
}
|
|
6131
|
-
function isSchemaMap(value) {
|
|
6132
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6133
|
-
}
|
|
6134
|
-
function validateSchemaSubset(schema, pointerBase, ctx, rootDefs) {
|
|
6135
|
-
walkKeywords(schema, pointerBase, ctx, rootDefs);
|
|
6136
|
-
const safe = /* @__PURE__ */ new Set();
|
|
6137
|
-
detectCycles(schema, pointerBase, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
|
|
6138
|
-
for (const [name, def] of Object.entries(rootDefs)) {
|
|
6139
|
-
detectCycles(def, `${pointerBase}/$defs/${escape(name)}`, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
|
|
6140
|
-
}
|
|
6141
|
-
}
|
|
6142
|
-
function walkKeywords(node, ptr, ctx, rootDefs) {
|
|
6143
|
-
if (!isSchemaObject(node)) return;
|
|
6144
|
-
for (const keyword of Object.keys(node)) {
|
|
6145
|
-
if (!ALLOWED_SCHEMA_KEYWORDS.has(keyword)) {
|
|
6146
|
-
ctx.add(
|
|
6147
|
-
WcsDiagnosticCode.ManifestUnknownKeyword,
|
|
6148
|
-
`${ptr}/${escape(keyword)}`,
|
|
6149
|
-
`Unsupported schema keyword "${keyword}". Allowed: ${[...ALLOWED_SCHEMA_KEYWORDS].join(", ")}.`,
|
|
6150
|
-
"warning",
|
|
6151
|
-
{},
|
|
6152
|
-
true
|
|
6153
|
-
);
|
|
6154
|
-
}
|
|
6155
|
-
}
|
|
6156
|
-
if (typeof node.$ref === "string") {
|
|
6157
|
-
if (!node.$ref.startsWith("#/")) {
|
|
6158
|
-
ctx.add(
|
|
6159
|
-
WcsDiagnosticCode.ManifestExternalRef,
|
|
6160
|
-
`${ptr}/$ref`,
|
|
6161
|
-
`External $ref "${node.$ref}" is forbidden; only local "#/$defs/..." references are allowed.`,
|
|
6162
|
-
"error"
|
|
6163
|
-
);
|
|
6164
|
-
} else if (resolveLocalRef(node.$ref, rootDefs) === void 0) {
|
|
6165
|
-
ctx.add(
|
|
6166
|
-
WcsDiagnosticCode.ManifestRefUnresolved,
|
|
6167
|
-
`${ptr}/$ref`,
|
|
6168
|
-
`Unresolved local $ref "${node.$ref}".`,
|
|
6169
|
-
"error"
|
|
6170
|
-
);
|
|
6171
|
-
}
|
|
6172
|
-
}
|
|
6173
|
-
if (isSchemaMap(node.properties)) {
|
|
6174
|
-
for (const [name, child] of Object.entries(node.properties)) {
|
|
6175
|
-
walkKeywords(child, `${ptr}/properties/${escape(name)}`, ctx, rootDefs);
|
|
6176
|
-
}
|
|
6177
|
-
}
|
|
6178
|
-
if (node.items !== void 0 && isSchemaObject(node.items)) {
|
|
6179
|
-
walkKeywords(node.items, `${ptr}/items`, ctx, rootDefs);
|
|
6180
|
-
}
|
|
6181
|
-
if (Array.isArray(node.anyOf)) {
|
|
6182
|
-
node.anyOf.forEach((child, i) => walkKeywords(child, `${ptr}/anyOf/${i}`, ctx, rootDefs));
|
|
6183
|
-
}
|
|
6184
|
-
if (isSchemaMap(node.$defs)) {
|
|
6185
|
-
for (const [name, child] of Object.entries(node.$defs)) {
|
|
6186
|
-
walkKeywords(child, `${ptr}/$defs/${escape(name)}`, ctx, rootDefs);
|
|
6187
|
-
}
|
|
6188
|
-
}
|
|
6189
|
-
}
|
|
6190
|
-
function detectCycles(node, ptr, ctx, rootDefs, refStack, safe) {
|
|
6191
|
-
if (!isSchemaObject(node)) return;
|
|
6192
|
-
if (typeof node.$ref === "string") {
|
|
6193
|
-
const ref = node.$ref;
|
|
6194
|
-
if (!ref.startsWith("#/")) return;
|
|
6195
|
-
if (refStack.has(ref)) {
|
|
6196
|
-
ctx.add(WcsDiagnosticCode.ManifestRefCycle, `${ptr}/$ref`, `Cyclic $ref detected at "${ref}".`, "error");
|
|
6197
|
-
return;
|
|
6198
|
-
}
|
|
6199
|
-
if (safe.has(ref)) return;
|
|
6200
|
-
const target = resolveLocalRef(ref, rootDefs);
|
|
6201
|
-
if (target === void 0) return;
|
|
6202
|
-
refStack.add(ref);
|
|
6203
|
-
detectCycles(target, ptr, ctx, rootDefs, refStack, safe);
|
|
6204
|
-
refStack.delete(ref);
|
|
6205
|
-
safe.add(ref);
|
|
6206
|
-
return;
|
|
6207
|
-
}
|
|
6208
|
-
if (isSchemaMap(node.properties)) {
|
|
6209
|
-
for (const child of Object.values(node.properties)) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
|
|
6210
|
-
}
|
|
6211
|
-
if (node.items !== void 0 && isSchemaObject(node.items)) {
|
|
6212
|
-
detectCycles(node.items, ptr, ctx, rootDefs, refStack, safe);
|
|
6213
|
-
}
|
|
6214
|
-
if (Array.isArray(node.anyOf)) {
|
|
6215
|
-
for (const child of node.anyOf) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
|
|
6216
|
-
}
|
|
6217
|
-
}
|
|
6218
|
-
function resolveLocalRef(ref, rootDefs) {
|
|
6219
|
-
const match = /^#\/\$defs\/(.+)$/.exec(ref);
|
|
6220
|
-
if (match === null) return void 0;
|
|
6221
|
-
const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
|
|
6222
|
-
return rootDefs[name];
|
|
6223
|
-
}
|
|
6224
|
-
function escape(key) {
|
|
6225
|
-
return key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
6226
|
-
}
|
|
6227
|
-
|
|
6228
6501
|
// src/core/sidecar/jsonSource.ts
|
|
6229
6502
|
var JsonReader = class {
|
|
6230
6503
|
constructor(text) {
|
|
@@ -6488,8 +6761,13 @@ function resolvePackageContracts(loaded) {
|
|
|
6488
6761
|
const collided = /* @__PURE__ */ new Set();
|
|
6489
6762
|
const firstSource = /* @__PURE__ */ new Map();
|
|
6490
6763
|
const filterOwner = /* @__PURE__ */ new Map();
|
|
6764
|
+
const stateOwner = /* @__PURE__ */ new Map();
|
|
6765
|
+
const stateWinners = /* @__PURE__ */ new Map();
|
|
6766
|
+
const collidedStates = /* @__PURE__ */ new Set();
|
|
6767
|
+
let hasApplicationArtifact = false;
|
|
6491
6768
|
for (const lm of loaded) {
|
|
6492
6769
|
if (lm.manifest === null) continue;
|
|
6770
|
+
if (lm.manifest.kind === "application") hasApplicationArtifact = true;
|
|
6493
6771
|
const types = lm.manifest.manifestExtensions?.["wcstack.types"];
|
|
6494
6772
|
if (lm.manifest.kind === "package" && types !== void 0) {
|
|
6495
6773
|
for (const [tag, component] of Object.entries(types.components ?? {})) {
|
|
@@ -6541,13 +6819,102 @@ function resolvePackageContracts(loaded) {
|
|
|
6541
6819
|
);
|
|
6542
6820
|
}
|
|
6543
6821
|
}
|
|
6822
|
+
if (lm.manifest.kind === "application" && application?.states !== void 0) {
|
|
6823
|
+
for (const [name, entry] of Object.entries(application.states)) {
|
|
6824
|
+
const schema = entry?.stateSchema;
|
|
6825
|
+
if (schema === null || typeof schema !== "object" || Array.isArray(schema)) continue;
|
|
6826
|
+
const priorSource = stateOwner.get(name);
|
|
6827
|
+
if (priorSource === void 0) {
|
|
6828
|
+
stateOwner.set(name, lm.artifact.source);
|
|
6829
|
+
stateWinners.set(name, schema);
|
|
6830
|
+
continue;
|
|
6831
|
+
}
|
|
6832
|
+
collidedStates.add(name);
|
|
6833
|
+
stateWinners.delete(name);
|
|
6834
|
+
ctxFor(lm).add(
|
|
6835
|
+
WcsDiagnosticCode.ManifestStateCollision,
|
|
6836
|
+
pointer("manifestExtensions", "wcstack.application", "states", name),
|
|
6837
|
+
`State "${name}" declares a stateSchema in multiple application artifacts (also in "${priorSource}"); neither is used.`,
|
|
6838
|
+
"error",
|
|
6839
|
+
{ statePath: name },
|
|
6840
|
+
true
|
|
6841
|
+
);
|
|
6842
|
+
}
|
|
6843
|
+
}
|
|
6544
6844
|
}
|
|
6545
6845
|
const diagnosticsBySource = /* @__PURE__ */ new Map();
|
|
6546
6846
|
for (const [source, diags] of perSource) {
|
|
6547
6847
|
const kept = diags.filter((d) => !(d.code === WcsDiagnosticCode.ManifestOverride && d.tag !== void 0 && collided.has(d.tag)));
|
|
6548
6848
|
if (kept.length > 0) diagnosticsBySource.set(source, kept);
|
|
6549
6849
|
}
|
|
6550
|
-
return { tags: winners, diagnosticsBySource };
|
|
6850
|
+
return { tags: winners, applicationStates: stateWinners, hasApplicationArtifact, diagnosticsBySource };
|
|
6851
|
+
}
|
|
6852
|
+
|
|
6853
|
+
// src/core/sidecar/discover.ts
|
|
6854
|
+
var APPLICATION_MANIFEST_FILENAME = "wcstack.manifest.json";
|
|
6855
|
+
var MAX_ASCEND = 16;
|
|
6856
|
+
function discoverApplicationManifest(fileReader) {
|
|
6857
|
+
for (let up = 0; up <= MAX_ASCEND; up++) {
|
|
6858
|
+
const relativePath = `${"../".repeat(up)}${APPLICATION_MANIFEST_FILENAME}`;
|
|
6859
|
+
const text = fileReader(relativePath);
|
|
6860
|
+
if (text === void 0) continue;
|
|
6861
|
+
const loaded = loadManifest({ text, source: relativePath });
|
|
6862
|
+
return { relativePath, text, loaded, states: applicationStatesOf(loaded) };
|
|
6863
|
+
}
|
|
6864
|
+
return void 0;
|
|
6865
|
+
}
|
|
6866
|
+
function applicationStatesOf(loaded) {
|
|
6867
|
+
const states = /* @__PURE__ */ new Map();
|
|
6868
|
+
const manifest = loaded.manifest;
|
|
6869
|
+
if (manifest === null || manifest.kind !== "application") return states;
|
|
6870
|
+
const application = manifest.manifestExtensions?.["wcstack.application"];
|
|
6871
|
+
for (const [name, entry] of Object.entries(application?.states ?? {})) {
|
|
6872
|
+
const schema = entry?.stateSchema;
|
|
6873
|
+
if (schema !== null && typeof schema === "object" && !Array.isArray(schema)) {
|
|
6874
|
+
states.set(name, schema);
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6877
|
+
return states;
|
|
6878
|
+
}
|
|
6879
|
+
function joinRelativeSource(htmlSource, relativePath) {
|
|
6880
|
+
const sepIndex = Math.max(htmlSource.lastIndexOf("/"), htmlSource.lastIndexOf("\\"));
|
|
6881
|
+
const dirSegments = sepIndex === -1 ? [] : htmlSource.slice(0, sepIndex).split(/[\\/]/);
|
|
6882
|
+
for (const segment of relativePath.split("/")) {
|
|
6883
|
+
if (segment === "" || segment === ".") continue;
|
|
6884
|
+
if (segment === "..") {
|
|
6885
|
+
if (dirSegments.length > 0 && dirSegments[dirSegments.length - 1] !== "..") dirSegments.pop();
|
|
6886
|
+
else dirSegments.push("..");
|
|
6887
|
+
continue;
|
|
6888
|
+
}
|
|
6889
|
+
dirSegments.push(segment);
|
|
6890
|
+
}
|
|
6891
|
+
return dirSegments.join("/");
|
|
6892
|
+
}
|
|
6893
|
+
|
|
6894
|
+
// src/core/validateDocument.ts
|
|
6895
|
+
function validateDocument(text, options = {}) {
|
|
6896
|
+
const bindAttribute = options.bindAttribute ?? "data-wcs";
|
|
6897
|
+
const stateTagName = options.stateTagName ?? "wcs-state";
|
|
6898
|
+
const locale3 = options.locale;
|
|
6899
|
+
const fileReader = options.fileReader;
|
|
6900
|
+
const applicationStates = options.applicationStates ?? (fileReader !== void 0 ? discoverApplicationManifest(fileReader)?.states : void 0);
|
|
6901
|
+
const out = [];
|
|
6902
|
+
out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader, applicationStates));
|
|
6903
|
+
out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader, applicationStates));
|
|
6904
|
+
out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
|
|
6905
|
+
out.push(...validateAriaAttributes(text, bindAttribute, locale3));
|
|
6906
|
+
out.push(...validateDocumentEnv(text, locale3));
|
|
6907
|
+
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
6908
|
+
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
6909
|
+
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
6910
|
+
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
6911
|
+
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|
|
6912
|
+
out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
|
|
6913
|
+
}
|
|
6914
|
+
for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
|
|
6915
|
+
out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
|
|
6916
|
+
}
|
|
6917
|
+
return sortDiagnostics(out);
|
|
6551
6918
|
}
|
|
6552
6919
|
|
|
6553
6920
|
// src/core/sidecar/drift.ts
|
|
@@ -6606,13 +6973,24 @@ function checkDrift(tag, component, live, ctx) {
|
|
|
6606
6973
|
}
|
|
6607
6974
|
|
|
6608
6975
|
// src/core/sidecar/validate.ts
|
|
6976
|
+
function validateManifestArtifact(artifact) {
|
|
6977
|
+
const loaded = loadManifest(artifact);
|
|
6978
|
+
validateLoadedSchemas(loaded);
|
|
6979
|
+
return sortDiagnostics(loaded.ctx.diagnostics);
|
|
6980
|
+
}
|
|
6609
6981
|
function validateLoadedSchemas(loaded) {
|
|
6610
6982
|
if (loaded.manifest === null) return;
|
|
6611
6983
|
const types = loaded.manifest.manifestExtensions?.["wcstack.types"];
|
|
6612
|
-
|
|
6613
|
-
for (const [tag, component] of Object.entries(types.components ?? {})) {
|
|
6984
|
+
for (const [tag, component] of Object.entries(types?.components ?? {})) {
|
|
6614
6985
|
validateComponentSchemas(tag, component, loaded.ctx);
|
|
6615
6986
|
}
|
|
6987
|
+
const application = loaded.manifest.manifestExtensions?.["wcstack.application"];
|
|
6988
|
+
for (const [name, entry] of Object.entries(application?.states ?? {})) {
|
|
6989
|
+
const schema = entry?.stateSchema;
|
|
6990
|
+
if (schema === null || typeof schema !== "object" || Array.isArray(schema)) continue;
|
|
6991
|
+
const ptr = `${pointer("manifestExtensions", "wcstack.application", "states", name)}/stateSchema`;
|
|
6992
|
+
validateSchemaSubset(schema, ptr, loaded.ctx, schema.$defs ?? {});
|
|
6993
|
+
}
|
|
6616
6994
|
}
|
|
6617
6995
|
function validateComponentSchemas(tag, component, ctx) {
|
|
6618
6996
|
const base = pointer("manifestExtensions", "wcstack.types", "components", tag);
|
|
@@ -6662,7 +7040,9 @@ function validateManifestSet(input) {
|
|
|
6662
7040
|
return {
|
|
6663
7041
|
diagnostics: sortDiagnostics(all),
|
|
6664
7042
|
byArtifact: sortedByArtifact,
|
|
6665
|
-
resolvedTags
|
|
7043
|
+
resolvedTags,
|
|
7044
|
+
resolvedStates: resolved.applicationStates,
|
|
7045
|
+
hasApplicationArtifact: resolved.hasApplicationArtifact
|
|
6666
7046
|
};
|
|
6667
7047
|
}
|
|
6668
7048
|
function escapePtr(key) {
|
|
@@ -6673,13 +7053,9 @@ function escapePtr(key) {
|
|
|
6673
7053
|
var severityLabel = { error: "error", warning: "warning", info: "info" };
|
|
6674
7054
|
function runValidation(inputs, options = {}) {
|
|
6675
7055
|
const diagnosticsBySource = /* @__PURE__ */ new Map();
|
|
6676
|
-
|
|
6677
|
-
if (input.kind === "html") {
|
|
6678
|
-
const docOptions = input.fileReader !== void 0 ? { ...options, fileReader: input.fileReader } : options;
|
|
6679
|
-
diagnosticsBySource.set(input.source, validateDocument(input.text, docOptions));
|
|
6680
|
-
}
|
|
6681
|
-
}
|
|
7056
|
+
const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
|
|
6682
7057
|
const manifestInputs = inputs.filter((i) => i.kind === "manifest");
|
|
7058
|
+
let explicitStates;
|
|
6683
7059
|
if (manifestInputs.length > 0) {
|
|
6684
7060
|
const result = validateManifestSet({
|
|
6685
7061
|
artifacts: manifestInputs.map((m) => ({ text: m.text, source: m.source })),
|
|
@@ -6688,8 +7064,29 @@ function runValidation(inputs, options = {}) {
|
|
|
6688
7064
|
for (const input of manifestInputs) {
|
|
6689
7065
|
diagnosticsBySource.set(input.source, result.byArtifact.get(input.source) ?? []);
|
|
6690
7066
|
}
|
|
7067
|
+
if (result.hasApplicationArtifact) explicitStates = result.resolvedStates;
|
|
7068
|
+
}
|
|
7069
|
+
for (const input of inputs) {
|
|
7070
|
+
if (input.kind !== "html") continue;
|
|
7071
|
+
let applicationStates = explicitStates;
|
|
7072
|
+
if (applicationStates === void 0 && input.fileReader !== void 0) {
|
|
7073
|
+
const discovered = discoverApplicationManifest(input.fileReader);
|
|
7074
|
+
applicationStates = discovered?.states ?? /* @__PURE__ */ new Map();
|
|
7075
|
+
if (discovered !== void 0) {
|
|
7076
|
+
const source = joinRelativeSource(input.source, discovered.relativePath);
|
|
7077
|
+
if (!diagnosticsBySource.has(source)) {
|
|
7078
|
+
textBySource.set(source, discovered.text);
|
|
7079
|
+
diagnosticsBySource.set(source, validateManifestArtifact({ text: discovered.text, source }));
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
const docOptions = {
|
|
7084
|
+
...options,
|
|
7085
|
+
...input.fileReader !== void 0 ? { fileReader: input.fileReader } : {},
|
|
7086
|
+
...applicationStates !== void 0 ? { applicationStates } : {}
|
|
7087
|
+
};
|
|
7088
|
+
diagnosticsBySource.set(input.source, validateDocument(input.text, docOptions));
|
|
6691
7089
|
}
|
|
6692
|
-
const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
|
|
6693
7090
|
const lines = [];
|
|
6694
7091
|
let errorCount = 0;
|
|
6695
7092
|
let warningCount = 0;
|
|
@@ -6711,7 +7108,7 @@ function runValidation(inputs, options = {}) {
|
|
|
6711
7108
|
errorCount,
|
|
6712
7109
|
warningCount,
|
|
6713
7110
|
infoCount,
|
|
6714
|
-
exitCode: errorCount > 0 ? 1 : 0,
|
|
7111
|
+
exitCode: errorCount > 0 || options.strict === true && warningCount > 0 ? 1 : 0,
|
|
6715
7112
|
diagnosticsBySource
|
|
6716
7113
|
};
|
|
6717
7114
|
}
|
|
@@ -6728,6 +7125,7 @@ function parseArgs(argv) {
|
|
|
6728
7125
|
else if (arg.startsWith("--state-tag=")) options.stateTagName = arg.slice("--state-tag=".length);
|
|
6729
7126
|
else if (arg.startsWith("--lang=")) options.locale = arg.slice("--lang=".length);
|
|
6730
7127
|
else if (arg === "--errors-only" || arg === "--quiet") options.errorsOnly = true;
|
|
7128
|
+
else if (arg === "--strict") options.strict = true;
|
|
6731
7129
|
else if (!arg.startsWith("-")) files.push(arg);
|
|
6732
7130
|
}
|
|
6733
7131
|
return { options, files };
|
|
@@ -6746,7 +7144,7 @@ function main(argv) {
|
|
|
6746
7144
|
const { options, files } = parseArgs(argv);
|
|
6747
7145
|
const locale3 = resolveCliLocale(options.locale);
|
|
6748
7146
|
if (files.length === 0) {
|
|
6749
|
-
process.stderr.write("usage: wcs-validate [--attr=data-wcs] [--state-tag=wcs-state] [--lang=ja|en] <file> [<file> ...]\n");
|
|
7147
|
+
process.stderr.write("usage: wcs-validate [--attr=data-wcs] [--state-tag=wcs-state] [--lang=ja|en] [--errors-only] [--strict] <file> [<file> ...]\n");
|
|
6750
7148
|
return 2;
|
|
6751
7149
|
}
|
|
6752
7150
|
const inputs = [];
|
|
@@ -6768,7 +7166,7 @@ function main(argv) {
|
|
|
6768
7166
|
}
|
|
6769
7167
|
process.stdout.write(
|
|
6770
7168
|
`
|
|
6771
|
-
${result.errorCount} error(s), ${result.warningCount} warning(s), ${result.infoCount} info
|
|
7169
|
+
${result.errorCount} error(s), ${result.warningCount} warning(s), ${result.infoCount} info${options.strict ? " (strict)" : ""}
|
|
6772
7170
|
`
|
|
6773
7171
|
);
|
|
6774
7172
|
return result.exitCode;
|