@wcstack/lint 1.31.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 +986 -459
- 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 ---
|
|
@@ -118,7 +121,7 @@ var WcsDiagnosticCode = {
|
|
|
118
121
|
TokenMisconfigured: "wcs/token-misconfigured",
|
|
119
122
|
NestedAssign: "wcs/nested-assign",
|
|
120
123
|
// --- 意味論(構文・存在検査では捕まらない取り違え。service/semanticValidator.ts) ---
|
|
121
|
-
// `$getAll` / `$resolve` の添字の本数がパスの `*` の本数と噛み合わない。
|
|
124
|
+
// `$getAll` / `$setAll` / `$resolve` の添字の本数がパスの `*` の本数と噛み合わない。
|
|
122
125
|
// ランタイムは同じ code で raiseError する(超過は以前は黙って無視されていた)。
|
|
123
126
|
IndexArity: "wcs/index-arity",
|
|
124
127
|
// ワイルドカードの階数がスコープの段数を超える(`matrix.*.*` を 1 段の for で読む、
|
|
@@ -157,13 +160,22 @@ var WcsDiagnosticCode = {
|
|
|
157
160
|
TriggerSeededTruthy: "wcs/trigger-seeded-truthy",
|
|
158
161
|
// 非 manual <wcs-storage> value バインド先の空値シード(初期書き戻しが保存値を上書き)。
|
|
159
162
|
StorageSeedClobber: "wcs/storage-seed-clobber",
|
|
163
|
+
// --- accessibility (docs/a11y-design.md §8 / D9) ---
|
|
164
|
+
// `attr.aria-*` バインドの属性名が WAI-ARIA に存在しない(タイポ)。
|
|
165
|
+
// setAttribute はそのまま書き、支援技術は黙って無視する。severity は warning
|
|
166
|
+
// (error 昇格時は packages/lint/scripts/smoke-test.mjs の対ケース更新が必須)。
|
|
167
|
+
AriaAttrUnknown: "wcs/aria-attr-unknown",
|
|
160
168
|
// --- document-level load configuration ---
|
|
161
169
|
// @wcstack/state/auto より後に他 wcstack /auto が読まれている。
|
|
162
170
|
ScriptOrder: "wcs/script-order",
|
|
163
171
|
// router/auto があるのに <base href> がない(SPA の basename 誤導出)。
|
|
164
172
|
BaseHrefMissing: "wcs/base-href-missing",
|
|
165
173
|
// @wcstack/signals と /dom エントリの同一ページ混在(リアクティブコア二重化)。
|
|
166
|
-
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"
|
|
167
179
|
};
|
|
168
180
|
function sortDiagnostics(diagnostics) {
|
|
169
181
|
const severityRank = { error: 0, warning: 1, info: 2 };
|
|
@@ -391,12 +403,12 @@ var fix = (options) => {
|
|
|
391
403
|
};
|
|
392
404
|
};
|
|
393
405
|
var locale = (options) => {
|
|
394
|
-
const
|
|
406
|
+
const explicit = options?.[0];
|
|
395
407
|
return (value) => {
|
|
396
408
|
if (typeof value !== "number") {
|
|
397
409
|
valueMustBeNumber("locale");
|
|
398
410
|
}
|
|
399
|
-
return value.toLocaleString(
|
|
411
|
+
return value.toLocaleString(explicit ?? config.locale);
|
|
400
412
|
};
|
|
401
413
|
};
|
|
402
414
|
var uc = (_options) => {
|
|
@@ -576,30 +588,30 @@ var truncate = (options) => {
|
|
|
576
588
|
};
|
|
577
589
|
};
|
|
578
590
|
var date = (options) => {
|
|
579
|
-
const
|
|
591
|
+
const explicit = options?.[0];
|
|
580
592
|
return (value) => {
|
|
581
593
|
if (!(value instanceof Date)) {
|
|
582
594
|
valueMustBeDate("date");
|
|
583
595
|
}
|
|
584
|
-
return value.toLocaleDateString(
|
|
596
|
+
return value.toLocaleDateString(explicit ?? config.locale);
|
|
585
597
|
};
|
|
586
598
|
};
|
|
587
599
|
var time = (options) => {
|
|
588
|
-
const
|
|
600
|
+
const explicit = options?.[0];
|
|
589
601
|
return (value) => {
|
|
590
602
|
if (!(value instanceof Date)) {
|
|
591
603
|
valueMustBeDate("time");
|
|
592
604
|
}
|
|
593
|
-
return value.toLocaleTimeString(
|
|
605
|
+
return value.toLocaleTimeString(explicit ?? config.locale);
|
|
594
606
|
};
|
|
595
607
|
};
|
|
596
608
|
var datetime = (options) => {
|
|
597
|
-
const
|
|
609
|
+
const explicit = options?.[0];
|
|
598
610
|
return (value) => {
|
|
599
611
|
if (!(value instanceof Date)) {
|
|
600
612
|
valueMustBeDate("datetime");
|
|
601
613
|
}
|
|
602
|
-
return value.toLocaleString(
|
|
614
|
+
return value.toLocaleString(explicit ?? config.locale);
|
|
603
615
|
};
|
|
604
616
|
};
|
|
605
617
|
var ymd = (options) => {
|
|
@@ -904,226 +916,6 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
|
|
|
904
916
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
905
917
|
}));
|
|
906
918
|
|
|
907
|
-
// src/language/htmlParse.ts
|
|
908
|
-
function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
|
|
909
|
-
const blocks = [];
|
|
910
|
-
let pos = 0;
|
|
911
|
-
const len = html.length;
|
|
912
|
-
while (pos < len) {
|
|
913
|
-
if (html.startsWith("<!--", pos)) {
|
|
914
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
915
|
-
if (commentEnd === -1) break;
|
|
916
|
-
pos = commentEnd + 3;
|
|
917
|
-
continue;
|
|
918
|
-
}
|
|
919
|
-
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
920
|
-
if (wcsMatch === null) {
|
|
921
|
-
pos++;
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
|
|
925
|
-
pos = wcsMatch.end;
|
|
926
|
-
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
927
|
-
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
928
|
-
while (pos < wcsEnd) {
|
|
929
|
-
if (html.startsWith("<!--", pos)) {
|
|
930
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
931
|
-
if (commentEnd === -1) break;
|
|
932
|
-
pos = commentEnd + 3;
|
|
933
|
-
continue;
|
|
934
|
-
}
|
|
935
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
936
|
-
if (scriptMatch === null) {
|
|
937
|
-
pos++;
|
|
938
|
-
continue;
|
|
939
|
-
}
|
|
940
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
941
|
-
if (typeAttr?.toLowerCase() !== "module") {
|
|
942
|
-
pos = scriptMatch.end;
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
const contentStart = scriptMatch.end;
|
|
946
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
947
|
-
if (scriptCloseIdx === -1) {
|
|
948
|
-
pos = contentStart;
|
|
949
|
-
break;
|
|
950
|
-
}
|
|
951
|
-
const contentEnd = scriptCloseIdx;
|
|
952
|
-
blocks.push({
|
|
953
|
-
contentStart,
|
|
954
|
-
contentEnd,
|
|
955
|
-
content: html.slice(contentStart, contentEnd),
|
|
956
|
-
stateName
|
|
957
|
-
});
|
|
958
|
-
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
959
|
-
if (pos === 0) break;
|
|
960
|
-
}
|
|
961
|
-
pos = wcsEnd;
|
|
962
|
-
if (wcsCloseIdx !== -1) {
|
|
963
|
-
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
964
|
-
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
return blocks;
|
|
968
|
-
}
|
|
969
|
-
function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
970
|
-
const elements = [];
|
|
971
|
-
let pos = 0;
|
|
972
|
-
const len = html.length;
|
|
973
|
-
while (pos < len) {
|
|
974
|
-
if (html.startsWith("<!--", pos)) {
|
|
975
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
976
|
-
if (commentEnd === -1) break;
|
|
977
|
-
pos = commentEnd + 3;
|
|
978
|
-
continue;
|
|
979
|
-
}
|
|
980
|
-
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
981
|
-
if (wcsMatch === null) {
|
|
982
|
-
pos++;
|
|
983
|
-
continue;
|
|
984
|
-
}
|
|
985
|
-
const stateName = extractAttribute(wcsMatch.tagContent, "name") ?? "default";
|
|
986
|
-
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
987
|
-
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
988
|
-
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
989
|
-
const tagStart = pos;
|
|
990
|
-
const tagEnd = wcsMatch.end;
|
|
991
|
-
pos = wcsMatch.end;
|
|
992
|
-
const scriptBlocks = [];
|
|
993
|
-
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
994
|
-
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
995
|
-
while (pos < wcsEnd) {
|
|
996
|
-
if (html.startsWith("<!--", pos)) {
|
|
997
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
998
|
-
if (commentEnd === -1) break;
|
|
999
|
-
pos = commentEnd + 3;
|
|
1000
|
-
continue;
|
|
1001
|
-
}
|
|
1002
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1003
|
-
if (scriptMatch === null) {
|
|
1004
|
-
pos++;
|
|
1005
|
-
continue;
|
|
1006
|
-
}
|
|
1007
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1008
|
-
if (typeAttr?.toLowerCase() !== "module") {
|
|
1009
|
-
pos = scriptMatch.end;
|
|
1010
|
-
continue;
|
|
1011
|
-
}
|
|
1012
|
-
const contentStart = scriptMatch.end;
|
|
1013
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1014
|
-
if (scriptCloseIdx === -1) {
|
|
1015
|
-
pos = contentStart;
|
|
1016
|
-
break;
|
|
1017
|
-
}
|
|
1018
|
-
scriptBlocks.push({
|
|
1019
|
-
contentStart,
|
|
1020
|
-
contentEnd: scriptCloseIdx,
|
|
1021
|
-
content: html.slice(contentStart, scriptCloseIdx),
|
|
1022
|
-
stateName
|
|
1023
|
-
});
|
|
1024
|
-
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1025
|
-
if (pos === 0) break;
|
|
1026
|
-
}
|
|
1027
|
-
elements.push({ stateName, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
1028
|
-
pos = wcsEnd;
|
|
1029
|
-
if (wcsCloseIdx !== -1) {
|
|
1030
|
-
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
1031
|
-
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
return elements;
|
|
1035
|
-
}
|
|
1036
|
-
function findScriptJsonById(html, id2) {
|
|
1037
|
-
let pos = 0;
|
|
1038
|
-
const len = html.length;
|
|
1039
|
-
while (pos < len) {
|
|
1040
|
-
if (html.startsWith("<!--", pos)) {
|
|
1041
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1042
|
-
if (commentEnd === -1) break;
|
|
1043
|
-
pos = commentEnd + 3;
|
|
1044
|
-
continue;
|
|
1045
|
-
}
|
|
1046
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1047
|
-
if (scriptMatch === null) {
|
|
1048
|
-
pos++;
|
|
1049
|
-
continue;
|
|
1050
|
-
}
|
|
1051
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1052
|
-
const idAttr = extractAttribute(scriptMatch.tagContent, "id");
|
|
1053
|
-
if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
|
|
1054
|
-
const contentStart = scriptMatch.end;
|
|
1055
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1056
|
-
if (scriptCloseIdx === -1) return null;
|
|
1057
|
-
return html.slice(contentStart, scriptCloseIdx);
|
|
1058
|
-
}
|
|
1059
|
-
pos = scriptMatch.end;
|
|
1060
|
-
}
|
|
1061
|
-
return null;
|
|
1062
|
-
}
|
|
1063
|
-
function matchOpenTag(html, pos, tagName) {
|
|
1064
|
-
if (html[pos] !== "<") return null;
|
|
1065
|
-
const nameStart = pos + 1;
|
|
1066
|
-
const nameEnd = nameStart + tagName.length;
|
|
1067
|
-
if (nameEnd > html.length) return null;
|
|
1068
|
-
const slice3 = html.slice(nameStart, nameEnd);
|
|
1069
|
-
if (slice3.toLowerCase() !== tagName.toLowerCase()) return null;
|
|
1070
|
-
const charAfter = html[nameEnd];
|
|
1071
|
-
if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
|
|
1072
|
-
return null;
|
|
1073
|
-
}
|
|
1074
|
-
let i = nameEnd;
|
|
1075
|
-
let inSingleQuote = false;
|
|
1076
|
-
let inDoubleQuote = false;
|
|
1077
|
-
while (i < html.length) {
|
|
1078
|
-
const ch = html[i];
|
|
1079
|
-
if (inSingleQuote) {
|
|
1080
|
-
if (ch === "'") inSingleQuote = false;
|
|
1081
|
-
} else if (inDoubleQuote) {
|
|
1082
|
-
if (ch === '"') inDoubleQuote = false;
|
|
1083
|
-
} else if (ch === "'") {
|
|
1084
|
-
inSingleQuote = true;
|
|
1085
|
-
} else if (ch === '"') {
|
|
1086
|
-
inDoubleQuote = true;
|
|
1087
|
-
} else if (ch === ">") {
|
|
1088
|
-
return {
|
|
1089
|
-
start: pos,
|
|
1090
|
-
end: i + 1,
|
|
1091
|
-
tagContent: html.slice(nameEnd, i)
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
i++;
|
|
1095
|
-
}
|
|
1096
|
-
return null;
|
|
1097
|
-
}
|
|
1098
|
-
function findCloseTag(html, startPos, tagName) {
|
|
1099
|
-
const pattern = "</" + tagName;
|
|
1100
|
-
const patternLower = pattern.toLowerCase();
|
|
1101
|
-
const htmlLower = html.toLowerCase();
|
|
1102
|
-
let pos = startPos;
|
|
1103
|
-
while (pos < html.length) {
|
|
1104
|
-
const idx = htmlLower.indexOf(patternLower, pos);
|
|
1105
|
-
if (idx === -1) return -1;
|
|
1106
|
-
const afterIdx = idx + pattern.length;
|
|
1107
|
-
if (afterIdx < html.length) {
|
|
1108
|
-
const ch = html[afterIdx];
|
|
1109
|
-
if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
1110
|
-
return idx;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
pos = idx + 1;
|
|
1114
|
-
}
|
|
1115
|
-
return -1;
|
|
1116
|
-
}
|
|
1117
|
-
function extractAttribute(tagContent, attrName) {
|
|
1118
|
-
const regex = new RegExp(
|
|
1119
|
-
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
1120
|
-
"i"
|
|
1121
|
-
);
|
|
1122
|
-
const match = tagContent.match(regex);
|
|
1123
|
-
if (!match) return null;
|
|
1124
|
-
return match[1] ?? match[2] ?? match[3] ?? null;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
919
|
// src/service/stateAnalyzer.ts
|
|
1128
920
|
var RESERVED_STREAMS_KEY = "$streams";
|
|
1129
921
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
@@ -1580,42 +1372,371 @@ function extractJsDocType(content, propIndex) {
|
|
|
1580
1372
|
const typeExpr = jsdocMatch[1].trim();
|
|
1581
1373
|
return normalizeJsDocType(typeExpr);
|
|
1582
1374
|
}
|
|
1583
|
-
function normalizeJsDocType(typeExpr) {
|
|
1584
|
-
const parts = typeExpr.split("|").map((p) => p.trim());
|
|
1585
|
-
const normalized = parts.map((p) => {
|
|
1586
|
-
const lower = p.toLowerCase();
|
|
1587
|
-
if (lower === "string") return "string";
|
|
1588
|
-
if (lower === "number") return "number";
|
|
1589
|
-
if (lower === "boolean") return "boolean";
|
|
1590
|
-
if (lower === "null") return "null";
|
|
1591
|
-
if (lower === "undefined") return "null";
|
|
1592
|
-
if (lower.endsWith("[]") || lower.startsWith("array")) return "array";
|
|
1593
|
-
if (lower === "object") return "object";
|
|
1375
|
+
function normalizeJsDocType(typeExpr) {
|
|
1376
|
+
const parts = typeExpr.split("|").map((p) => p.trim());
|
|
1377
|
+
const normalized = parts.map((p) => {
|
|
1378
|
+
const lower = p.toLowerCase();
|
|
1379
|
+
if (lower === "string") return "string";
|
|
1380
|
+
if (lower === "number") return "number";
|
|
1381
|
+
if (lower === "boolean") return "boolean";
|
|
1382
|
+
if (lower === "null") return "null";
|
|
1383
|
+
if (lower === "undefined") return "null";
|
|
1384
|
+
if (lower.endsWith("[]") || lower.startsWith("array")) return "array";
|
|
1385
|
+
if (lower === "object") return "object";
|
|
1386
|
+
return null;
|
|
1387
|
+
}).filter((p) => p !== null);
|
|
1388
|
+
if (normalized.length === 0) return void 0;
|
|
1389
|
+
const unique = [...new Set(normalized)].sort();
|
|
1390
|
+
return unique.join("|");
|
|
1391
|
+
}
|
|
1392
|
+
function isEscaped(text, i) {
|
|
1393
|
+
let backslashCount = 0;
|
|
1394
|
+
let j = i - 1;
|
|
1395
|
+
while (j >= 0 && text[j] === "\\") {
|
|
1396
|
+
backslashCount++;
|
|
1397
|
+
j--;
|
|
1398
|
+
}
|
|
1399
|
+
return backslashCount % 2 === 1;
|
|
1400
|
+
}
|
|
1401
|
+
function inferTypeHint(valueStart) {
|
|
1402
|
+
const v = valueStart.trim().replace(/,\s*$/, "");
|
|
1403
|
+
if (/^-?\d+\.\d/.test(v)) return "number";
|
|
1404
|
+
if (/^-?\d/.test(v)) return "number";
|
|
1405
|
+
if (/^["'`]/.test(v)) return "string";
|
|
1406
|
+
if (v === "true" || v === "false") return "boolean";
|
|
1407
|
+
if (v === "null") return "null";
|
|
1408
|
+
if (v.startsWith("[")) return "array";
|
|
1409
|
+
if (v.startsWith("{")) return "object";
|
|
1410
|
+
return void 0;
|
|
1411
|
+
}
|
|
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
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
const kept = candidates.filter((p) => !schemaKeys.has(`${p.stateName} ${p.path}`));
|
|
1429
|
+
return [...kept, ...schemaCandidates];
|
|
1430
|
+
}
|
|
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;
|
|
1448
|
+
}
|
|
1449
|
+
out.push(n);
|
|
1450
|
+
}
|
|
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
|
+
}
|
|
1477
|
+
}
|
|
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 !== "/") {
|
|
1594
1687
|
return null;
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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;
|
|
1599
1712
|
}
|
|
1600
|
-
function
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
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;
|
|
1606
1729
|
}
|
|
1607
|
-
return
|
|
1730
|
+
return -1;
|
|
1608
1731
|
}
|
|
1609
|
-
function
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
if (
|
|
1616
|
-
|
|
1617
|
-
if (v.startsWith("{")) return "object";
|
|
1618
|
-
return void 0;
|
|
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;
|
|
1619
1740
|
}
|
|
1620
1741
|
|
|
1621
1742
|
// src/service/statePathResolver.ts
|
|
@@ -1807,6 +1928,8 @@ var ja = {
|
|
|
1807
1928
|
commandTokenUndeclared: (t) => `\u30B3\u30DE\u30F3\u30C9\u30C8\u30FC\u30AF\u30F3 "${t}" \u306F $commandTokens \u306B\u5BA3\u8A00\u3055\u308C\u3066\u3044\u307E\u305B\u3093`,
|
|
1808
1929
|
streamPathMissing: (p) => `\u30D1\u30B9 "${p}" \u306F $streams \u5BA3\u8A00\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
|
|
1809
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`,
|
|
1810
1933
|
expansionSuffix: (x) => `\uFF08\u5C55\u958B: ${x}\uFF09`,
|
|
1811
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`,
|
|
1812
1935
|
omittedPathOutsideFor: (p) => `\u7701\u7565\u30D1\u30B9 "${p}" \u306F <template for> \u306E\u5916\u5074\u3067\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
|
|
@@ -1839,13 +1962,16 @@ var ja = {
|
|
|
1839
1962
|
tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
|
|
1840
1963
|
spreadNoBindable: (tag) => `'...'\uFF08spread\uFF09\u306F <${tag}> \u306B\u6709\u52B9\u306A wcBindable \u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059 \u2014 \u3053\u306E\u30BF\u30B0\u306F\u5BA3\u8A00\u3092\u6301\u305F\u306A\u3044\u305F\u3081\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u30A8\u30E9\u30FC\u3092\u9001\u51FA\u3057\u307E\u3059`,
|
|
1841
1964
|
tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken \u306E\u30AD\u30FC "${name}" \u306F <${tag}> \u306E wcBindable \u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u751F DOM \u30A4\u30D9\u30F3\u30C8\u540D\u306F\u767A\u706B\u3057\u307E\u305B\u3093 \u2014 \u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
|
|
1965
|
+
ariaAttrUnknown: (name) => `"${name}" \u306F WAI-ARIA \u306E\u5C5E\u6027\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002setAttribute \u306F\u305D\u306E\u307E\u307E\u66F8\u304D\u8FBC\u307F\u307E\u3059\u304C\u3001\u652F\u63F4\u6280\u8853\u306B\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059`,
|
|
1842
1966
|
didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
|
|
1843
1967
|
none: () => `\u306A\u3057`,
|
|
1844
1968
|
triggerSeededTruthy: (path) => `trigger \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C true \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002trigger \u306F\u30A8\u30C3\u30B8\u691C\u51FA\u306A\u3057\uFF08truthy \u66F8\u304D\u8FBC\u307F\u3067\u5373\u767A\u706B\u30FBmanual \u3082\u30D0\u30A4\u30D1\u30B9\uFF09\u306E\u305F\u3081\u3001\u30D0\u30A4\u30F3\u30C9\u6642\u306B\u5373\u767A\u706B\u3057\u307E\u3059\u3002false \u3067\u30B7\u30FC\u30C9\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
1845
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`,
|
|
1846
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`,
|
|
1847
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`,
|
|
1848
|
-
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`
|
|
1849
1975
|
};
|
|
1850
1976
|
var EN_EXPECTED_LABEL = {
|
|
1851
1977
|
array: "an array-typed path",
|
|
@@ -1861,6 +1987,8 @@ var en = {
|
|
|
1861
1987
|
commandTokenUndeclared: (t) => `Command token "${t}" is not declared in $commandTokens`,
|
|
1862
1988
|
streamPathMissing: (p) => `Path "${p}" does not exist in the $streams declaration`,
|
|
1863
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"}`,
|
|
1864
1992
|
expansionSuffix: (x) => ` (expanded: ${x})`,
|
|
1865
1993
|
patternPathOutsideFor: (p) => `Pattern path "${p}" cannot be used outside a <template for>`,
|
|
1866
1994
|
omittedPathOutsideFor: (p) => `Shorthand path "${p}" cannot be used outside a <template for>`,
|
|
@@ -1893,25 +2021,220 @@ var en = {
|
|
|
1893
2021
|
tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
|
|
1894
2022
|
spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
|
|
1895
2023
|
tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken key "${name}" is not a wcBindable property of <${tag}>. Raw DOM event names never fire \u2014 use the property name (declared: ${declared})`,
|
|
2024
|
+
ariaAttrUnknown: (name) => `"${name}" is not a WAI-ARIA attribute. setAttribute writes it anyway, and assistive technology silently ignores it`,
|
|
1896
2025
|
didYouMean: (c) => `. Did you mean "${c}"?`,
|
|
1897
2026
|
none: () => `none`,
|
|
1898
2027
|
triggerSeededTruthy: (path) => `The trigger-bound slot "${path}" is seeded with true. trigger has no edge detection (any truthy write fires, and it bypasses manual), so it fires immediately at bind. Seed it with false`,
|
|
1899
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`,
|
|
1900
2029
|
devtoolsAfterState: () => `Load @wcstack/devtools/auto BEFORE @wcstack/state/auto (otherwise the wiring ledger is not captured live)`,
|
|
1901
2030
|
baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
|
|
1902
|
-
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)`
|
|
1903
2034
|
};
|
|
1904
2035
|
var CATALOGS = { ja, en };
|
|
1905
2036
|
function getMessages(locale3) {
|
|
1906
2037
|
return CATALOGS[resolveLocale(locale3)];
|
|
1907
2038
|
}
|
|
1908
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
|
+
|
|
1909
2232
|
// src/service/bindingValidator.ts
|
|
1910
2233
|
var filterMap = new Map(BUILTIN_FILTERS.map((f) => [f.name, f]));
|
|
1911
|
-
function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader) {
|
|
2234
|
+
function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, fileReader, applicationStates) {
|
|
1912
2235
|
const diagnostics = [];
|
|
1913
2236
|
const msgs = getMessages(locale3);
|
|
1914
|
-
const statePaths = getStatePathsFromHtml(html, stateTagName, fileReader);
|
|
2237
|
+
const statePaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationStates);
|
|
1915
2238
|
const pathsByState = /* @__PURE__ */ new Map();
|
|
1916
2239
|
for (const p of statePaths) {
|
|
1917
2240
|
const list = pathsByState.get(p.stateName) ?? [];
|
|
@@ -2034,16 +2357,17 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2034
2357
|
}
|
|
2035
2358
|
}
|
|
2036
2359
|
if (checkPath) {
|
|
2037
|
-
const
|
|
2038
|
-
|
|
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) {
|
|
2039
2363
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2040
2364
|
const pathStart = bindingStart + pathOffset;
|
|
2041
2365
|
diagnostics.push({
|
|
2042
|
-
code:
|
|
2366
|
+
code: verdict.code,
|
|
2043
2367
|
start: pathStart,
|
|
2044
2368
|
end: pathStart + pathTrimmed.length,
|
|
2045
|
-
message: `${message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
|
|
2046
|
-
severity:
|
|
2369
|
+
message: `${verdict.message}${pathTrimmed.startsWith(".") ? msgs.expansionSuffix(checkPath) : ""}`,
|
|
2370
|
+
severity: verdict.severity
|
|
2047
2371
|
});
|
|
2048
2372
|
}
|
|
2049
2373
|
}
|
|
@@ -2162,12 +2486,13 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2162
2486
|
if (typeReq && resultType !== typeReq.expected) {
|
|
2163
2487
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2164
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);
|
|
2165
2490
|
diagnostics.push({
|
|
2166
|
-
code: WcsDiagnosticCode.BindingTypeExpectation,
|
|
2491
|
+
code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
|
|
2167
2492
|
start: pathStart,
|
|
2168
2493
|
end: pathStart + pathTrimmed.length,
|
|
2169
|
-
message: msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
|
|
2170
|
-
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
|
|
2171
2496
|
});
|
|
2172
2497
|
}
|
|
2173
2498
|
}
|
|
@@ -2338,6 +2663,20 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
|
|
|
2338
2663
|
}
|
|
2339
2664
|
return null;
|
|
2340
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
|
+
}
|
|
2341
2680
|
function collectStructuralTemplates(html, attrName) {
|
|
2342
2681
|
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2343
2682
|
const attrRegex = new RegExp(`${escaped}\\s*=\\s*(["'])`, "i");
|
|
@@ -2739,10 +3078,19 @@ function isInsideTag(html, offset, tagName) {
|
|
|
2739
3078
|
}
|
|
2740
3079
|
|
|
2741
3080
|
// src/service/templateSyntaxValidator.ts
|
|
2742
|
-
function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader) {
|
|
3081
|
+
function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale3, fileReader, applicationStates) {
|
|
2743
3082
|
const diagnostics = [];
|
|
2744
3083
|
const msgs = getMessages(locale3);
|
|
2745
|
-
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
|
+
};
|
|
2746
3094
|
if (allPaths.length === 0) return diagnostics;
|
|
2747
3095
|
const defaultPaths = allPaths.filter((p) => p.stateName === "default");
|
|
2748
3096
|
const pathSet = new Set(defaultPaths.map((p) => p.path));
|
|
@@ -2822,24 +3170,28 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
2822
3170
|
const forPath = insideFor ? getInnermostForPath(html, item.matchStart, bindAttrName) : null;
|
|
2823
3171
|
if (forPath && !forPath.startsWith(".")) {
|
|
2824
3172
|
const expandedPath = pathPart === "." ? `${forPath}.*` : `${forPath}.*.${pathPart.slice(1)}`;
|
|
2825
|
-
|
|
3173
|
+
const verdict = missingVerdict(expandedPath, pathPart, pathSet, defaultPaths);
|
|
3174
|
+
if (verdict) {
|
|
2826
3175
|
diagnostics.push({
|
|
2827
|
-
code:
|
|
3176
|
+
code: verdict.code,
|
|
2828
3177
|
start: item.exprStart,
|
|
2829
3178
|
end: item.exprStart + pathPart.length,
|
|
2830
|
-
message:
|
|
2831
|
-
severity:
|
|
3179
|
+
message: verdict.message + msgs.expansionSuffix(expandedPath),
|
|
3180
|
+
severity: verdict.severity
|
|
2832
3181
|
});
|
|
2833
3182
|
}
|
|
2834
3183
|
}
|
|
2835
|
-
} else
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
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
|
+
}
|
|
2843
3195
|
}
|
|
2844
3196
|
}
|
|
2845
3197
|
for (let i = 1; i < parts.length; i++) {
|
|
@@ -3725,11 +4077,14 @@ var BUILTIN_TAGS = {
|
|
|
3725
4077
|
"wcs-raf": {
|
|
3726
4078
|
"package": "raf",
|
|
3727
4079
|
"hasWcBindable": true,
|
|
3728
|
-
"observedAttributes": [
|
|
4080
|
+
"observedAttributes": [
|
|
4081
|
+
"reduced-motion"
|
|
4082
|
+
],
|
|
3729
4083
|
"inputs": {
|
|
3730
4084
|
"once": "once",
|
|
3731
4085
|
"repeat": "repeat",
|
|
3732
4086
|
"manual": "manual",
|
|
4087
|
+
"reducedMotion": "reduced-motion",
|
|
3733
4088
|
"trigger": null
|
|
3734
4089
|
},
|
|
3735
4090
|
"properties": [
|
|
@@ -4006,6 +4361,35 @@ var BUILTIN_TAGS = {
|
|
|
4006
4361
|
"abort"
|
|
4007
4362
|
]
|
|
4008
4363
|
},
|
|
4364
|
+
"wcs-view-transition": {
|
|
4365
|
+
"package": "view-transition",
|
|
4366
|
+
"hasWcBindable": true,
|
|
4367
|
+
"observedAttributes": [
|
|
4368
|
+
"mode",
|
|
4369
|
+
"naming",
|
|
4370
|
+
"naming-limit",
|
|
4371
|
+
"reduced-motion",
|
|
4372
|
+
"types",
|
|
4373
|
+
"disabled",
|
|
4374
|
+
"for"
|
|
4375
|
+
],
|
|
4376
|
+
"inputs": {
|
|
4377
|
+
"disabled": "disabled",
|
|
4378
|
+
"mode": "mode",
|
|
4379
|
+
"naming": "naming",
|
|
4380
|
+
"namingLimit": "naming-limit",
|
|
4381
|
+
"reducedMotion": "reduced-motion",
|
|
4382
|
+
"types": "types",
|
|
4383
|
+
"participants": "for"
|
|
4384
|
+
},
|
|
4385
|
+
"properties": [
|
|
4386
|
+
"active",
|
|
4387
|
+
"error"
|
|
4388
|
+
],
|
|
4389
|
+
"commands": [
|
|
4390
|
+
"skip"
|
|
4391
|
+
]
|
|
4392
|
+
},
|
|
4009
4393
|
"wcs-wakelock": {
|
|
4010
4394
|
"package": "wakelock",
|
|
4011
4395
|
"hasWcBindable": true,
|
|
@@ -4318,6 +4702,98 @@ function hasBooleanAttribute(attrsText, attrName) {
|
|
|
4318
4702
|
return new RegExp(`(?:^|\\s)${attrName}(?:\\s|=|$)`, "i").test(attrsText);
|
|
4319
4703
|
}
|
|
4320
4704
|
|
|
4705
|
+
// src/service/ariaValidator.ts
|
|
4706
|
+
var ARIA_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
4707
|
+
// widget attributes
|
|
4708
|
+
"aria-autocomplete",
|
|
4709
|
+
"aria-checked",
|
|
4710
|
+
"aria-disabled",
|
|
4711
|
+
"aria-errormessage",
|
|
4712
|
+
"aria-expanded",
|
|
4713
|
+
"aria-haspopup",
|
|
4714
|
+
"aria-hidden",
|
|
4715
|
+
"aria-invalid",
|
|
4716
|
+
"aria-label",
|
|
4717
|
+
"aria-level",
|
|
4718
|
+
"aria-modal",
|
|
4719
|
+
"aria-multiline",
|
|
4720
|
+
"aria-multiselectable",
|
|
4721
|
+
"aria-orientation",
|
|
4722
|
+
"aria-placeholder",
|
|
4723
|
+
"aria-pressed",
|
|
4724
|
+
"aria-readonly",
|
|
4725
|
+
"aria-required",
|
|
4726
|
+
"aria-selected",
|
|
4727
|
+
"aria-sort",
|
|
4728
|
+
"aria-valuemax",
|
|
4729
|
+
"aria-valuemin",
|
|
4730
|
+
"aria-valuenow",
|
|
4731
|
+
"aria-valuetext",
|
|
4732
|
+
// live region attributes
|
|
4733
|
+
"aria-busy",
|
|
4734
|
+
"aria-live",
|
|
4735
|
+
"aria-relevant",
|
|
4736
|
+
"aria-atomic",
|
|
4737
|
+
// drag-and-drop (deprecated in 1.1, still valid names)
|
|
4738
|
+
"aria-dropeffect",
|
|
4739
|
+
"aria-grabbed",
|
|
4740
|
+
// relationship attributes
|
|
4741
|
+
"aria-activedescendant",
|
|
4742
|
+
"aria-colcount",
|
|
4743
|
+
"aria-colindex",
|
|
4744
|
+
"aria-colindextext",
|
|
4745
|
+
"aria-colspan",
|
|
4746
|
+
"aria-controls",
|
|
4747
|
+
"aria-describedby",
|
|
4748
|
+
"aria-details",
|
|
4749
|
+
"aria-flowto",
|
|
4750
|
+
"aria-labelledby",
|
|
4751
|
+
"aria-owns",
|
|
4752
|
+
"aria-posinset",
|
|
4753
|
+
"aria-rowcount",
|
|
4754
|
+
"aria-rowindex",
|
|
4755
|
+
"aria-rowindextext",
|
|
4756
|
+
"aria-rowspan",
|
|
4757
|
+
"aria-setsize",
|
|
4758
|
+
// global additions
|
|
4759
|
+
"aria-current",
|
|
4760
|
+
"aria-keyshortcuts",
|
|
4761
|
+
"aria-roledescription",
|
|
4762
|
+
// 1.3 additions with broad implementation
|
|
4763
|
+
"aria-braillelabel",
|
|
4764
|
+
"aria-brailleroledescription",
|
|
4765
|
+
"aria-description"
|
|
4766
|
+
]);
|
|
4767
|
+
function validateAriaAttributes(html, bindAttribute = "data-wcs", locale3) {
|
|
4768
|
+
const diagnostics = [];
|
|
4769
|
+
const msgs = getMessages(locale3);
|
|
4770
|
+
for (const attr of findAllBindAttributes(html, bindAttribute)) {
|
|
4771
|
+
let exprOffset = 0;
|
|
4772
|
+
for (const expr of splitBindingExpressions(attr.value)) {
|
|
4773
|
+
const exprStart = attr.valueStart + exprOffset;
|
|
4774
|
+
exprOffset += expr.length + 1;
|
|
4775
|
+
const property = parseBindingExpression(expr).property;
|
|
4776
|
+
if (!property) continue;
|
|
4777
|
+
const bare = property.split("#")[0];
|
|
4778
|
+
if (!bare.toLowerCase().startsWith("attr.aria-")) continue;
|
|
4779
|
+
const ariaName = bare.slice("attr.".length).toLowerCase();
|
|
4780
|
+
if (ARIA_ATTRIBUTES.has(ariaName)) continue;
|
|
4781
|
+
const propIndex = expr.indexOf(property);
|
|
4782
|
+
const start = propIndex === -1 ? exprStart : exprStart + propIndex;
|
|
4783
|
+
const end = propIndex === -1 ? exprStart + expr.length : start + property.length;
|
|
4784
|
+
diagnostics.push({
|
|
4785
|
+
code: WcsDiagnosticCode.AriaAttrUnknown,
|
|
4786
|
+
start,
|
|
4787
|
+
end,
|
|
4788
|
+
severity: "warning",
|
|
4789
|
+
member: ariaName,
|
|
4790
|
+
message: msgs.ariaAttrUnknown(ariaName) + suggestion(ariaName, [...ARIA_ATTRIBUTES], msgs)
|
|
4791
|
+
});
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
return diagnostics;
|
|
4795
|
+
}
|
|
4796
|
+
|
|
4321
4797
|
// src/service/documentEnvValidator.ts
|
|
4322
4798
|
function validateDocumentEnv(html, locale3) {
|
|
4323
4799
|
const diagnostics = [];
|
|
@@ -4489,6 +4965,78 @@ function validateEntry(entry, pathSet, msgs) {
|
|
|
4489
4965
|
return null;
|
|
4490
4966
|
}
|
|
4491
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
|
+
|
|
4492
5040
|
// ../state/dist/parser.esm.js
|
|
4493
5041
|
var DELIMITER2 = ".";
|
|
4494
5042
|
var WILDCARD2 = "*";
|
|
@@ -4863,12 +5411,12 @@ var fix2 = (options) => {
|
|
|
4863
5411
|
};
|
|
4864
5412
|
};
|
|
4865
5413
|
var locale2 = (options) => {
|
|
4866
|
-
const
|
|
5414
|
+
const explicit = options?.[0];
|
|
4867
5415
|
return (value) => {
|
|
4868
5416
|
if (typeof value !== "number") {
|
|
4869
5417
|
valueMustBeNumber2("locale");
|
|
4870
5418
|
}
|
|
4871
|
-
return value.toLocaleString(
|
|
5419
|
+
return value.toLocaleString(explicit ?? config2.locale);
|
|
4872
5420
|
};
|
|
4873
5421
|
};
|
|
4874
5422
|
var uc2 = (_options) => {
|
|
@@ -5048,30 +5596,30 @@ var truncate2 = (options) => {
|
|
|
5048
5596
|
};
|
|
5049
5597
|
};
|
|
5050
5598
|
var date2 = (options) => {
|
|
5051
|
-
const
|
|
5599
|
+
const explicit = options?.[0];
|
|
5052
5600
|
return (value) => {
|
|
5053
5601
|
if (!(value instanceof Date)) {
|
|
5054
5602
|
valueMustBeDate2("date");
|
|
5055
5603
|
}
|
|
5056
|
-
return value.toLocaleDateString(
|
|
5604
|
+
return value.toLocaleDateString(explicit ?? config2.locale);
|
|
5057
5605
|
};
|
|
5058
5606
|
};
|
|
5059
5607
|
var time2 = (options) => {
|
|
5060
|
-
const
|
|
5608
|
+
const explicit = options?.[0];
|
|
5061
5609
|
return (value) => {
|
|
5062
5610
|
if (!(value instanceof Date)) {
|
|
5063
5611
|
valueMustBeDate2("time");
|
|
5064
5612
|
}
|
|
5065
|
-
return value.toLocaleTimeString(
|
|
5613
|
+
return value.toLocaleTimeString(explicit ?? config2.locale);
|
|
5066
5614
|
};
|
|
5067
5615
|
};
|
|
5068
5616
|
var datetime2 = (options) => {
|
|
5069
|
-
const
|
|
5617
|
+
const explicit = options?.[0];
|
|
5070
5618
|
return (value) => {
|
|
5071
5619
|
if (!(value instanceof Date)) {
|
|
5072
5620
|
valueMustBeDate2("datetime");
|
|
5073
5621
|
}
|
|
5074
|
-
return value.toLocaleString(
|
|
5622
|
+
return value.toLocaleString(explicit ?? config2.locale);
|
|
5075
5623
|
};
|
|
5076
5624
|
};
|
|
5077
5625
|
var ymd2 = (options) => {
|
|
@@ -5352,6 +5900,7 @@ function parseStatePart(statePart) {
|
|
|
5352
5900
|
} else {
|
|
5353
5901
|
stateAndPath = statePart.trim();
|
|
5354
5902
|
}
|
|
5903
|
+
if (stateAndPath.indexOf(STATE_NAME_SEPARATOR3) !== -1) ;
|
|
5355
5904
|
const [statePathName, stateName = "default"] = stateAndPath.split(STATE_NAME_SEPARATOR3).map(trimFn);
|
|
5356
5905
|
const pathInfo = getPathInfo(statePathName);
|
|
5357
5906
|
return {
|
|
@@ -5663,7 +6212,7 @@ function buildReferenceIndex(html, options = {}) {
|
|
|
5663
6212
|
|
|
5664
6213
|
// src/service/semanticValidator.ts
|
|
5665
6214
|
var STATE_UPDATED_CALLBACK = "$updatedCallback";
|
|
5666
|
-
var API_CALL = /\.\s*\$(getAll|resolve)\s*\(/g;
|
|
6215
|
+
var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
|
|
5667
6216
|
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
5668
6217
|
function splitCallArgs(source, open) {
|
|
5669
6218
|
const args = [];
|
|
@@ -5949,153 +6498,6 @@ function validateSemantics(html, stateTagName = "wcs-state", locale3, bindAttrNa
|
|
|
5949
6498
|
return out;
|
|
5950
6499
|
}
|
|
5951
6500
|
|
|
5952
|
-
// src/core/validateDocument.ts
|
|
5953
|
-
function validateDocument(text, options = {}) {
|
|
5954
|
-
const bindAttribute = options.bindAttribute ?? "data-wcs";
|
|
5955
|
-
const stateTagName = options.stateTagName ?? "wcs-state";
|
|
5956
|
-
const locale3 = options.locale;
|
|
5957
|
-
const fileReader = options.fileReader;
|
|
5958
|
-
const out = [];
|
|
5959
|
-
out.push(...validateBindings(text, bindAttribute, stateTagName, locale3, fileReader));
|
|
5960
|
-
out.push(...validateTemplateSyntax(text, stateTagName, bindAttribute, locale3, fileReader));
|
|
5961
|
-
out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale3, fileReader));
|
|
5962
|
-
out.push(...validateDocumentEnv(text, locale3));
|
|
5963
|
-
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
5964
|
-
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
5965
|
-
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
5966
|
-
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|
|
5967
|
-
out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
|
|
5968
|
-
}
|
|
5969
|
-
for (const d of validateNestedAssigns(text, stateTagName, locale3)) {
|
|
5970
|
-
out.push({ code: WcsDiagnosticCode.NestedAssign, start: d.start, end: d.end, message: d.message, severity: d.severity });
|
|
5971
|
-
}
|
|
5972
|
-
return sortDiagnostics(out);
|
|
5973
|
-
}
|
|
5974
|
-
|
|
5975
|
-
// src/core/sidecar/schemaSubset.ts
|
|
5976
|
-
var ALLOWED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
5977
|
-
"type",
|
|
5978
|
-
"properties",
|
|
5979
|
-
"required",
|
|
5980
|
-
"items",
|
|
5981
|
-
"enum",
|
|
5982
|
-
"const",
|
|
5983
|
-
"anyOf",
|
|
5984
|
-
"$defs",
|
|
5985
|
-
"$ref"
|
|
5986
|
-
]);
|
|
5987
|
-
var DiagnosticContext = class {
|
|
5988
|
-
constructor(spans) {
|
|
5989
|
-
this.spans = spans;
|
|
5990
|
-
}
|
|
5991
|
-
diagnostics = [];
|
|
5992
|
-
add(code, pointer2, message, severity, extra = {}, useKeySpan = false) {
|
|
5993
|
-
const span = this.spans.get(pointer2);
|
|
5994
|
-
const start = span === void 0 ? 0 : useKeySpan ? span.keyStart ?? span.start : span.start;
|
|
5995
|
-
const end = span === void 0 ? 0 : useKeySpan ? span.keyEnd ?? span.end : span.end;
|
|
5996
|
-
this.diagnostics.push({ code, start, end, message, severity, ...extra });
|
|
5997
|
-
}
|
|
5998
|
-
};
|
|
5999
|
-
function isSchemaObject(value) {
|
|
6000
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6001
|
-
}
|
|
6002
|
-
function isSchemaMap(value) {
|
|
6003
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6004
|
-
}
|
|
6005
|
-
function validateSchemaSubset(schema, pointerBase, ctx, rootDefs) {
|
|
6006
|
-
walkKeywords(schema, pointerBase, ctx, rootDefs);
|
|
6007
|
-
const safe = /* @__PURE__ */ new Set();
|
|
6008
|
-
detectCycles(schema, pointerBase, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
|
|
6009
|
-
for (const [name, def] of Object.entries(rootDefs)) {
|
|
6010
|
-
detectCycles(def, `${pointerBase}/$defs/${escape(name)}`, ctx, rootDefs, /* @__PURE__ */ new Set(), safe);
|
|
6011
|
-
}
|
|
6012
|
-
}
|
|
6013
|
-
function walkKeywords(node, ptr, ctx, rootDefs) {
|
|
6014
|
-
if (!isSchemaObject(node)) return;
|
|
6015
|
-
for (const keyword of Object.keys(node)) {
|
|
6016
|
-
if (!ALLOWED_SCHEMA_KEYWORDS.has(keyword)) {
|
|
6017
|
-
ctx.add(
|
|
6018
|
-
WcsDiagnosticCode.ManifestUnknownKeyword,
|
|
6019
|
-
`${ptr}/${escape(keyword)}`,
|
|
6020
|
-
`Unsupported schema keyword "${keyword}". Allowed: ${[...ALLOWED_SCHEMA_KEYWORDS].join(", ")}.`,
|
|
6021
|
-
"warning",
|
|
6022
|
-
{},
|
|
6023
|
-
true
|
|
6024
|
-
);
|
|
6025
|
-
}
|
|
6026
|
-
}
|
|
6027
|
-
if (typeof node.$ref === "string") {
|
|
6028
|
-
if (!node.$ref.startsWith("#/")) {
|
|
6029
|
-
ctx.add(
|
|
6030
|
-
WcsDiagnosticCode.ManifestExternalRef,
|
|
6031
|
-
`${ptr}/$ref`,
|
|
6032
|
-
`External $ref "${node.$ref}" is forbidden; only local "#/$defs/..." references are allowed.`,
|
|
6033
|
-
"error"
|
|
6034
|
-
);
|
|
6035
|
-
} else if (resolveLocalRef(node.$ref, rootDefs) === void 0) {
|
|
6036
|
-
ctx.add(
|
|
6037
|
-
WcsDiagnosticCode.ManifestRefUnresolved,
|
|
6038
|
-
`${ptr}/$ref`,
|
|
6039
|
-
`Unresolved local $ref "${node.$ref}".`,
|
|
6040
|
-
"error"
|
|
6041
|
-
);
|
|
6042
|
-
}
|
|
6043
|
-
}
|
|
6044
|
-
if (isSchemaMap(node.properties)) {
|
|
6045
|
-
for (const [name, child] of Object.entries(node.properties)) {
|
|
6046
|
-
walkKeywords(child, `${ptr}/properties/${escape(name)}`, ctx, rootDefs);
|
|
6047
|
-
}
|
|
6048
|
-
}
|
|
6049
|
-
if (node.items !== void 0 && isSchemaObject(node.items)) {
|
|
6050
|
-
walkKeywords(node.items, `${ptr}/items`, ctx, rootDefs);
|
|
6051
|
-
}
|
|
6052
|
-
if (Array.isArray(node.anyOf)) {
|
|
6053
|
-
node.anyOf.forEach((child, i) => walkKeywords(child, `${ptr}/anyOf/${i}`, ctx, rootDefs));
|
|
6054
|
-
}
|
|
6055
|
-
if (isSchemaMap(node.$defs)) {
|
|
6056
|
-
for (const [name, child] of Object.entries(node.$defs)) {
|
|
6057
|
-
walkKeywords(child, `${ptr}/$defs/${escape(name)}`, ctx, rootDefs);
|
|
6058
|
-
}
|
|
6059
|
-
}
|
|
6060
|
-
}
|
|
6061
|
-
function detectCycles(node, ptr, ctx, rootDefs, refStack, safe) {
|
|
6062
|
-
if (!isSchemaObject(node)) return;
|
|
6063
|
-
if (typeof node.$ref === "string") {
|
|
6064
|
-
const ref = node.$ref;
|
|
6065
|
-
if (!ref.startsWith("#/")) return;
|
|
6066
|
-
if (refStack.has(ref)) {
|
|
6067
|
-
ctx.add(WcsDiagnosticCode.ManifestRefCycle, `${ptr}/$ref`, `Cyclic $ref detected at "${ref}".`, "error");
|
|
6068
|
-
return;
|
|
6069
|
-
}
|
|
6070
|
-
if (safe.has(ref)) return;
|
|
6071
|
-
const target = resolveLocalRef(ref, rootDefs);
|
|
6072
|
-
if (target === void 0) return;
|
|
6073
|
-
refStack.add(ref);
|
|
6074
|
-
detectCycles(target, ptr, ctx, rootDefs, refStack, safe);
|
|
6075
|
-
refStack.delete(ref);
|
|
6076
|
-
safe.add(ref);
|
|
6077
|
-
return;
|
|
6078
|
-
}
|
|
6079
|
-
if (isSchemaMap(node.properties)) {
|
|
6080
|
-
for (const child of Object.values(node.properties)) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
|
|
6081
|
-
}
|
|
6082
|
-
if (node.items !== void 0 && isSchemaObject(node.items)) {
|
|
6083
|
-
detectCycles(node.items, ptr, ctx, rootDefs, refStack, safe);
|
|
6084
|
-
}
|
|
6085
|
-
if (Array.isArray(node.anyOf)) {
|
|
6086
|
-
for (const child of node.anyOf) detectCycles(child, ptr, ctx, rootDefs, refStack, safe);
|
|
6087
|
-
}
|
|
6088
|
-
}
|
|
6089
|
-
function resolveLocalRef(ref, rootDefs) {
|
|
6090
|
-
const match = /^#\/\$defs\/(.+)$/.exec(ref);
|
|
6091
|
-
if (match === null) return void 0;
|
|
6092
|
-
const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
|
|
6093
|
-
return rootDefs[name];
|
|
6094
|
-
}
|
|
6095
|
-
function escape(key) {
|
|
6096
|
-
return key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
6097
|
-
}
|
|
6098
|
-
|
|
6099
6501
|
// src/core/sidecar/jsonSource.ts
|
|
6100
6502
|
var JsonReader = class {
|
|
6101
6503
|
constructor(text) {
|
|
@@ -6359,8 +6761,13 @@ function resolvePackageContracts(loaded) {
|
|
|
6359
6761
|
const collided = /* @__PURE__ */ new Set();
|
|
6360
6762
|
const firstSource = /* @__PURE__ */ new Map();
|
|
6361
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;
|
|
6362
6768
|
for (const lm of loaded) {
|
|
6363
6769
|
if (lm.manifest === null) continue;
|
|
6770
|
+
if (lm.manifest.kind === "application") hasApplicationArtifact = true;
|
|
6364
6771
|
const types = lm.manifest.manifestExtensions?.["wcstack.types"];
|
|
6365
6772
|
if (lm.manifest.kind === "package" && types !== void 0) {
|
|
6366
6773
|
for (const [tag, component] of Object.entries(types.components ?? {})) {
|
|
@@ -6412,13 +6819,102 @@ function resolvePackageContracts(loaded) {
|
|
|
6412
6819
|
);
|
|
6413
6820
|
}
|
|
6414
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
|
+
}
|
|
6415
6844
|
}
|
|
6416
6845
|
const diagnosticsBySource = /* @__PURE__ */ new Map();
|
|
6417
6846
|
for (const [source, diags] of perSource) {
|
|
6418
6847
|
const kept = diags.filter((d) => !(d.code === WcsDiagnosticCode.ManifestOverride && d.tag !== void 0 && collided.has(d.tag)));
|
|
6419
6848
|
if (kept.length > 0) diagnosticsBySource.set(source, kept);
|
|
6420
6849
|
}
|
|
6421
|
-
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);
|
|
6422
6918
|
}
|
|
6423
6919
|
|
|
6424
6920
|
// src/core/sidecar/drift.ts
|
|
@@ -6477,13 +6973,24 @@ function checkDrift(tag, component, live, ctx) {
|
|
|
6477
6973
|
}
|
|
6478
6974
|
|
|
6479
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
|
+
}
|
|
6480
6981
|
function validateLoadedSchemas(loaded) {
|
|
6481
6982
|
if (loaded.manifest === null) return;
|
|
6482
6983
|
const types = loaded.manifest.manifestExtensions?.["wcstack.types"];
|
|
6483
|
-
|
|
6484
|
-
for (const [tag, component] of Object.entries(types.components ?? {})) {
|
|
6984
|
+
for (const [tag, component] of Object.entries(types?.components ?? {})) {
|
|
6485
6985
|
validateComponentSchemas(tag, component, loaded.ctx);
|
|
6486
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
|
+
}
|
|
6487
6994
|
}
|
|
6488
6995
|
function validateComponentSchemas(tag, component, ctx) {
|
|
6489
6996
|
const base = pointer("manifestExtensions", "wcstack.types", "components", tag);
|
|
@@ -6533,7 +7040,9 @@ function validateManifestSet(input) {
|
|
|
6533
7040
|
return {
|
|
6534
7041
|
diagnostics: sortDiagnostics(all),
|
|
6535
7042
|
byArtifact: sortedByArtifact,
|
|
6536
|
-
resolvedTags
|
|
7043
|
+
resolvedTags,
|
|
7044
|
+
resolvedStates: resolved.applicationStates,
|
|
7045
|
+
hasApplicationArtifact: resolved.hasApplicationArtifact
|
|
6537
7046
|
};
|
|
6538
7047
|
}
|
|
6539
7048
|
function escapePtr(key) {
|
|
@@ -6544,13 +7053,9 @@ function escapePtr(key) {
|
|
|
6544
7053
|
var severityLabel = { error: "error", warning: "warning", info: "info" };
|
|
6545
7054
|
function runValidation(inputs, options = {}) {
|
|
6546
7055
|
const diagnosticsBySource = /* @__PURE__ */ new Map();
|
|
6547
|
-
|
|
6548
|
-
if (input.kind === "html") {
|
|
6549
|
-
const docOptions = input.fileReader !== void 0 ? { ...options, fileReader: input.fileReader } : options;
|
|
6550
|
-
diagnosticsBySource.set(input.source, validateDocument(input.text, docOptions));
|
|
6551
|
-
}
|
|
6552
|
-
}
|
|
7056
|
+
const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
|
|
6553
7057
|
const manifestInputs = inputs.filter((i) => i.kind === "manifest");
|
|
7058
|
+
let explicitStates;
|
|
6554
7059
|
if (manifestInputs.length > 0) {
|
|
6555
7060
|
const result = validateManifestSet({
|
|
6556
7061
|
artifacts: manifestInputs.map((m) => ({ text: m.text, source: m.source })),
|
|
@@ -6559,8 +7064,29 @@ function runValidation(inputs, options = {}) {
|
|
|
6559
7064
|
for (const input of manifestInputs) {
|
|
6560
7065
|
diagnosticsBySource.set(input.source, result.byArtifact.get(input.source) ?? []);
|
|
6561
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));
|
|
6562
7089
|
}
|
|
6563
|
-
const textBySource = new Map(inputs.map((i) => [i.source, i.text]));
|
|
6564
7090
|
const lines = [];
|
|
6565
7091
|
let errorCount = 0;
|
|
6566
7092
|
let warningCount = 0;
|
|
@@ -6582,7 +7108,7 @@ function runValidation(inputs, options = {}) {
|
|
|
6582
7108
|
errorCount,
|
|
6583
7109
|
warningCount,
|
|
6584
7110
|
infoCount,
|
|
6585
|
-
exitCode: errorCount > 0 ? 1 : 0,
|
|
7111
|
+
exitCode: errorCount > 0 || options.strict === true && warningCount > 0 ? 1 : 0,
|
|
6586
7112
|
diagnosticsBySource
|
|
6587
7113
|
};
|
|
6588
7114
|
}
|
|
@@ -6599,6 +7125,7 @@ function parseArgs(argv) {
|
|
|
6599
7125
|
else if (arg.startsWith("--state-tag=")) options.stateTagName = arg.slice("--state-tag=".length);
|
|
6600
7126
|
else if (arg.startsWith("--lang=")) options.locale = arg.slice("--lang=".length);
|
|
6601
7127
|
else if (arg === "--errors-only" || arg === "--quiet") options.errorsOnly = true;
|
|
7128
|
+
else if (arg === "--strict") options.strict = true;
|
|
6602
7129
|
else if (!arg.startsWith("-")) files.push(arg);
|
|
6603
7130
|
}
|
|
6604
7131
|
return { options, files };
|
|
@@ -6617,7 +7144,7 @@ function main(argv) {
|
|
|
6617
7144
|
const { options, files } = parseArgs(argv);
|
|
6618
7145
|
const locale3 = resolveCliLocale(options.locale);
|
|
6619
7146
|
if (files.length === 0) {
|
|
6620
|
-
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");
|
|
6621
7148
|
return 2;
|
|
6622
7149
|
}
|
|
6623
7150
|
const inputs = [];
|
|
@@ -6639,7 +7166,7 @@ function main(argv) {
|
|
|
6639
7166
|
}
|
|
6640
7167
|
process.stdout.write(
|
|
6641
7168
|
`
|
|
6642
|
-
${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)" : ""}
|
|
6643
7170
|
`
|
|
6644
7171
|
);
|
|
6645
7172
|
return result.exitCode;
|