@wcstack/lint 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1087 -139
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -147,6 +147,36 @@ var WcsDiagnosticCode = {
|
|
|
147
147
|
// `$watch` のキーが状態定義に存在しない。バインディング側と違い黙って発火しない
|
|
148
148
|
// だけなので気づけない。severity は binding-path-missing に揃える(warning)。
|
|
149
149
|
WatchPathMissing: "wcs/watch-path-missing",
|
|
150
|
+
// --- <wcs-state> script: $recursion declaration / `**` paths ---
|
|
151
|
+
// ランタイムと同じ code 語彙(@wcstack/state src/recursion/ が正本。
|
|
152
|
+
// docs/state-recursive-path-impl-plan.md §7)。静的に出すのは**パス文字列と宣言だけで
|
|
153
|
+
// 決まる**ものに限る。データを見ないと決まらない wcs/recursion-shared-list /
|
|
154
|
+
// wcs/recursion-cycle / wcs/recursion-depth-exceeded、および評価時の呼び出し文脈に
|
|
155
|
+
// 依存する wcs/recursion-context は runtime 専用(静的側は出さない)。
|
|
156
|
+
//
|
|
157
|
+
// `**` を解釈しない場所へ `**` が渡った(data-wcs / mustache / $watch キー / $listKeys
|
|
158
|
+
// キー / $resolve / $postUpdate / $trackDependency / 代入)、または `$recursion` 宣言が
|
|
159
|
+
// 無いのに `**` を使った。runtime は PathInfo の不変条件として raiseError するか
|
|
160
|
+
//(API 経由)、getter を黙って無視する(宣言なしの `**` getter)。
|
|
161
|
+
RecursionUnsupported: "wcs/recursion-unsupported",
|
|
162
|
+
// 宣言済みアンカーと合致しない `**`(綴り違い・2 つ目の `**`)、または `**` の後ろが
|
|
163
|
+
// 整形されていない(空セグメント・`**` 直後の素の `*`)。
|
|
164
|
+
RecursionAnchor: "wcs/recursion-anchor",
|
|
165
|
+
// `$getAll` の添字の形が `**` に対して定義できない(非空の接頭辞 / 配列でない値)。
|
|
166
|
+
RecursionGetAllForm: "wcs/recursion-getall-form",
|
|
167
|
+
// `$setAll` の添字・値の形が `**` に対して定義できない
|
|
168
|
+
//(非空の接頭辞 / 添字省略 / mapper / spread)。
|
|
169
|
+
RecursionSetAllForm: "wcs/recursion-setall-form",
|
|
170
|
+
// ノード自身・子リスト・子ノード・子リストの length・多段の反復サブパスなら子リストへ
|
|
171
|
+
// 至る途中のオブジェクトへの一括書き込み(確定済みの子アドレスを壊す)。
|
|
172
|
+
RecursionStructuralWrite: "wcs/recursion-structural-write",
|
|
173
|
+
// 再帰 getter(およびその派生値の中)への書き込み。setter は初版では持てない。
|
|
174
|
+
RecursionReadonly: "wcs/recursion-readonly",
|
|
175
|
+
// `$recursion` 宣言そのもの、または `**` getter の宣言の形が不正(アンカー / 反復
|
|
176
|
+
// サブパスの形・複数宣言・setter・getter でない・ノード自身・構造を名指す接尾辞・
|
|
177
|
+
// 展開形と同名の具体 getter・ボリューム / マウント下での宣言)。
|
|
178
|
+
//(ランタイムは初期化時に raiseError)。wcs/watch-declaration-invalid の再帰版。
|
|
179
|
+
RecursionDeclarationInvalid: "wcs/recursion-declaration-invalid",
|
|
150
180
|
TypeAnnotation: "wcs/type-annotation",
|
|
151
181
|
TemplateSyntax: "wcs/template-syntax",
|
|
152
182
|
// --- <wcs-state> script: array reactivity hazards ---
|
|
@@ -843,6 +873,7 @@ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
|
843
873
|
var STATE_ON_NAME = "$on";
|
|
844
874
|
var STATE_STREAMS_NAME = "$streams";
|
|
845
875
|
var STATE_WATCH_NAME = "$watch";
|
|
876
|
+
var STATE_RECURSION_NAME = "$recursion";
|
|
846
877
|
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
847
878
|
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
848
879
|
var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
@@ -905,6 +936,7 @@ function getWcsManifest() {
|
|
|
905
936
|
STATE_STREAMS_NAME,
|
|
906
937
|
STATE_WATCH_NAME,
|
|
907
938
|
STATE_LIST_KEYS_NAME,
|
|
939
|
+
STATE_RECURSION_NAME,
|
|
908
940
|
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
909
941
|
STATE_STREAM_ERROR_NAMESPACE_NAME
|
|
910
942
|
]
|
|
@@ -926,12 +958,173 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
|
|
|
926
958
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
927
959
|
}));
|
|
928
960
|
|
|
961
|
+
// src/service/recursionPaths.ts
|
|
962
|
+
var RECURSION_WILDCARD = "**";
|
|
963
|
+
var RECURSION_KEY = "$recursion";
|
|
964
|
+
function hasRecursionWildcard(path) {
|
|
965
|
+
return path.indexOf(RECURSION_WILDCARD) !== -1;
|
|
966
|
+
}
|
|
967
|
+
function checkNodePath(path) {
|
|
968
|
+
if (typeof path !== "string" || path.length === 0) return "empty";
|
|
969
|
+
const segments = path.split(".");
|
|
970
|
+
if (segments.some((segment) => segment.length === 0)) return "emptySegment";
|
|
971
|
+
if (segments.length < 2 || segments[segments.length - 1] !== "*") return "notElement";
|
|
972
|
+
if (segments[0].startsWith("$")) return "reservedRoot";
|
|
973
|
+
if (path.indexOf("#") !== -1) return "reservedMount";
|
|
974
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
975
|
+
if (segments[i] === "*") return "midWildcard";
|
|
976
|
+
if (segments[i] === RECURSION_WILDCARD) return "nestedRecursion";
|
|
977
|
+
if (!isNaN(Number(segments[i]))) return "indexSegment";
|
|
978
|
+
}
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
function makeRecursionSpec(anchor, repeat) {
|
|
982
|
+
return Object.freeze({
|
|
983
|
+
anchor,
|
|
984
|
+
repeat,
|
|
985
|
+
recursiveAnchor: anchor.slice(0, anchor.lastIndexOf(".")) + "." + RECURSION_WILDCARD,
|
|
986
|
+
anchorList: anchor.slice(0, anchor.lastIndexOf(".")),
|
|
987
|
+
repeatList: repeat.slice(0, repeat.lastIndexOf("."))
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
function splitRecursivePath(spec, path) {
|
|
991
|
+
if (path === spec.recursiveAnchor) return "";
|
|
992
|
+
if (!path.startsWith(spec.recursiveAnchor + ".")) return null;
|
|
993
|
+
const suffix = path.slice(spec.recursiveAnchor.length);
|
|
994
|
+
if (hasRecursionWildcard(suffix)) return null;
|
|
995
|
+
const segments = suffix.slice(1).split(".");
|
|
996
|
+
if (segments[0] === "*" || segments.some((segment) => segment.length === 0)) return null;
|
|
997
|
+
return suffix;
|
|
998
|
+
}
|
|
999
|
+
function foldSuffixIndexes(suffix) {
|
|
1000
|
+
return suffix.length === 0 ? suffix : "." + indexSegmentsToWildcard(suffix.slice(1));
|
|
1001
|
+
}
|
|
1002
|
+
function foldRecursion(spec, path) {
|
|
1003
|
+
if (!path.startsWith(spec.anchor)) return null;
|
|
1004
|
+
const unit3 = "." + spec.repeat;
|
|
1005
|
+
let cursor = spec.anchor.length;
|
|
1006
|
+
let depth = 0;
|
|
1007
|
+
while (path.startsWith(unit3, cursor)) {
|
|
1008
|
+
cursor += unit3.length;
|
|
1009
|
+
depth++;
|
|
1010
|
+
}
|
|
1011
|
+
if (cursor !== path.length && path.charCodeAt(cursor) !== 46) return null;
|
|
1012
|
+
return { depth, rest: path.slice(cursor) };
|
|
1013
|
+
}
|
|
1014
|
+
function matchesRecursion(specs, path, has) {
|
|
1015
|
+
for (const spec of specs) {
|
|
1016
|
+
const folded = foldRecursion(spec, path);
|
|
1017
|
+
if (folded === null) continue;
|
|
1018
|
+
const unit3 = "." + spec.repeat;
|
|
1019
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1020
|
+
const rest = unit3.repeat(folded.depth - depth) + folded.rest;
|
|
1021
|
+
if (has(spec.anchor + rest)) return true;
|
|
1022
|
+
if (has(spec.recursiveAnchor + rest)) return true;
|
|
1023
|
+
for (let dot = rest.lastIndexOf("."); dot > 0; dot = rest.lastIndexOf(".", dot - 1)) {
|
|
1024
|
+
if (has(spec.recursiveAnchor + rest.slice(0, dot))) return true;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
function owningGetterSuffix(spec, getterSuffixes, path) {
|
|
1031
|
+
const pattern = indexSegmentsToWildcard(path);
|
|
1032
|
+
const folded = foldRecursion(spec, pattern);
|
|
1033
|
+
if (folded === null) return null;
|
|
1034
|
+
const unit3 = "." + spec.repeat;
|
|
1035
|
+
for (const suffix of getterSuffixes) {
|
|
1036
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1037
|
+
const expansion = spec.anchor + unit3.repeat(depth) + suffix;
|
|
1038
|
+
if (pattern === expansion || pattern.startsWith(expansion + ".")) return suffix;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
function indexSegmentsToWildcard(path) {
|
|
1044
|
+
return path.split(".").map((segment) => segment !== "*" && !Number.isNaN(Number(segment)) ? "*" : segment).join(".");
|
|
1045
|
+
}
|
|
1046
|
+
function concreteExpansionSuffix(spec, getterSuffixes, key) {
|
|
1047
|
+
const folded = foldRecursion(spec, key);
|
|
1048
|
+
if (folded === null) return null;
|
|
1049
|
+
const unit3 = "." + spec.repeat;
|
|
1050
|
+
for (const suffix of getterSuffixes) {
|
|
1051
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1052
|
+
if (key === spec.anchor + unit3.repeat(depth) + suffix) return suffix;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
function collectRecursionSpecs(candidates) {
|
|
1058
|
+
const out = [];
|
|
1059
|
+
for (const candidate of candidates) {
|
|
1060
|
+
if (candidate.kind !== "recursionAnchor" || typeof candidate.repeat !== "string") continue;
|
|
1061
|
+
if (!candidate.path.endsWith("." + RECURSION_WILDCARD)) continue;
|
|
1062
|
+
const anchor = candidate.path.slice(0, candidate.path.length - RECURSION_WILDCARD.length) + "*";
|
|
1063
|
+
if (out.some((spec) => spec.anchor === anchor && spec.repeat === candidate.repeat)) continue;
|
|
1064
|
+
out.push(makeRecursionSpec(anchor, candidate.repeat));
|
|
1065
|
+
}
|
|
1066
|
+
return out;
|
|
1067
|
+
}
|
|
1068
|
+
function impliedStructurePaths(spec) {
|
|
1069
|
+
const out = [
|
|
1070
|
+
{ path: spec.anchorList, kind: "data", typeHint: "array" },
|
|
1071
|
+
{ path: spec.anchor, kind: "list" },
|
|
1072
|
+
{ path: `${spec.anchorList}.length`, kind: "data", typeHint: "number" }
|
|
1073
|
+
];
|
|
1074
|
+
const repeatSegments = spec.repeatList.split(".");
|
|
1075
|
+
for (let i = 1; i < repeatSegments.length; i++) {
|
|
1076
|
+
out.push({ path: `${spec.anchor}.${repeatSegments.slice(0, i).join(".")}`, kind: "data" });
|
|
1077
|
+
}
|
|
1078
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}`, kind: "data", typeHint: "array" });
|
|
1079
|
+
out.push({ path: `${spec.anchor}.${spec.repeat}`, kind: "list" });
|
|
1080
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}.length`, kind: "data", typeHint: "number" });
|
|
1081
|
+
return out;
|
|
1082
|
+
}
|
|
1083
|
+
function structuralWriteTarget(spec, suffix) {
|
|
1084
|
+
const unit3 = "." + spec.repeat;
|
|
1085
|
+
let rest = suffix;
|
|
1086
|
+
while (rest.startsWith(unit3)) rest = rest.slice(unit3.length);
|
|
1087
|
+
if (rest.length === 0) return "node";
|
|
1088
|
+
if (rest === "." + spec.repeatList + ".length") return "length";
|
|
1089
|
+
const segments = spec.repeatList.split(".");
|
|
1090
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
1091
|
+
if (rest === "." + segments.slice(0, i).join(".")) return i === segments.length ? "list" : "branch";
|
|
1092
|
+
}
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
function sameFamily(spec, a, b) {
|
|
1096
|
+
const unit3 = "." + spec.repeat;
|
|
1097
|
+
const shorter = a.length <= b.length ? a : b;
|
|
1098
|
+
const longer = a.length <= b.length ? b : a;
|
|
1099
|
+
if (!longer.endsWith(shorter)) return false;
|
|
1100
|
+
const gap = longer.slice(0, longer.length - shorter.length);
|
|
1101
|
+
if (gap.length === 0) return true;
|
|
1102
|
+
if (gap.length % unit3.length !== 0) return false;
|
|
1103
|
+
for (let cursor = 0; cursor < gap.length; cursor += unit3.length) {
|
|
1104
|
+
if (!gap.startsWith(unit3, cursor)) return false;
|
|
1105
|
+
}
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
function conflictingGetterSuffix(spec, getterSuffixes, suffix) {
|
|
1109
|
+
for (const declared of getterSuffixes) {
|
|
1110
|
+
if (coversSuffix(spec, declared, suffix)) return declared;
|
|
1111
|
+
}
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
function coversSuffix(spec, familySuffix, suffix) {
|
|
1115
|
+
for (let end = suffix.length; end > 0; end = suffix.lastIndexOf(".", end - 1)) {
|
|
1116
|
+
if (sameFamily(spec, familySuffix, suffix.slice(0, end))) return true;
|
|
1117
|
+
}
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
929
1121
|
// src/service/stateAnalyzer.ts
|
|
930
1122
|
var RESERVED_STREAMS_KEY = "$streams";
|
|
931
1123
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
932
1124
|
var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
|
|
933
1125
|
var RESERVED_LIST_KEYS_KEY = "$listKeys";
|
|
934
1126
|
var RESERVED_WATCH_KEY = "$watch";
|
|
1127
|
+
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
935
1128
|
function analyzeStatePaths(scriptContent) {
|
|
936
1129
|
const objectContent = extractDefaultExportObject(scriptContent);
|
|
937
1130
|
if (!objectContent) return [];
|
|
@@ -939,6 +1132,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
939
1132
|
const topLevelProps = parseTopLevelProperties(objectContent);
|
|
940
1133
|
const pendingStreamValues = [];
|
|
941
1134
|
const pendingListKeys = [];
|
|
1135
|
+
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
942
1136
|
for (const prop of topLevelProps) {
|
|
943
1137
|
if (prop.name.startsWith("$")) {
|
|
944
1138
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
@@ -950,7 +1144,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
950
1144
|
}
|
|
951
1145
|
if (prop.kind === "getter") {
|
|
952
1146
|
if (!paths.some((p) => p.path === prop.name)) {
|
|
953
|
-
paths.push({ path: prop.name, kind: "computed" });
|
|
1147
|
+
paths.push({ path: prop.name, kind: hasRecursionWildcard(prop.name) ? "recursive" : "computed" });
|
|
954
1148
|
}
|
|
955
1149
|
continue;
|
|
956
1150
|
}
|
|
@@ -963,27 +1157,113 @@ function analyzeStatePaths(scriptContent) {
|
|
|
963
1157
|
for (const listKeyEntry of pendingListKeys) {
|
|
964
1158
|
pushListKeyPaths(listKeyEntry, paths);
|
|
965
1159
|
}
|
|
1160
|
+
if (recursionSpec !== null) {
|
|
1161
|
+
if (!paths.some((p) => p.path === recursionSpec.recursiveAnchor && p.kind === "recursionAnchor")) {
|
|
1162
|
+
paths.push({ path: recursionSpec.recursiveAnchor, kind: "recursionAnchor", repeat: recursionSpec.repeat });
|
|
1163
|
+
}
|
|
1164
|
+
for (const implied of impliedStructurePaths(recursionSpec)) {
|
|
1165
|
+
if (paths.some((p) => p.path === implied.path)) continue;
|
|
1166
|
+
paths.push({ path: implied.path, kind: implied.kind, typeHint: implied.typeHint });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
966
1169
|
collectRowShapesFromAssignments(scriptContent, paths);
|
|
967
1170
|
return paths;
|
|
968
1171
|
}
|
|
1172
|
+
function specFromRecursionValue(prop) {
|
|
1173
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value)) return null;
|
|
1174
|
+
const entries = parseTopLevelProperties(extractObjectContent(prop.value)).filter((e) => e.kind === "data");
|
|
1175
|
+
if (entries.length !== 1) return null;
|
|
1176
|
+
const anchor = entries[0].name;
|
|
1177
|
+
const repeat = extractStringLiteralValue(entries[0].value);
|
|
1178
|
+
if (repeat === null) return null;
|
|
1179
|
+
if (checkNodePath(anchor) !== null || checkNodePath(repeat) !== null) return null;
|
|
1180
|
+
return makeRecursionSpec(anchor, repeat);
|
|
1181
|
+
}
|
|
1182
|
+
function analyzeRecursionDeclaration(scriptContent) {
|
|
1183
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1184
|
+
if (!root) return null;
|
|
1185
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_RECURSION_KEY);
|
|
1186
|
+
if (!prop || prop.nameStart === void 0 || prop.nameEnd === void 0) return null;
|
|
1187
|
+
const span = { start: root.start + prop.nameStart, end: root.start + prop.nameEnd };
|
|
1188
|
+
if (prop.kind === "method") {
|
|
1189
|
+
return { ...span, notObject: true, objectLiteral: false, entries: [], spec: null };
|
|
1190
|
+
}
|
|
1191
|
+
if (prop.kind !== "data" || !prop.value || prop.valueStart === void 0) {
|
|
1192
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1193
|
+
}
|
|
1194
|
+
if (!isObjectLiteral(prop.value)) {
|
|
1195
|
+
const scan = maskCommentsAndStrings(prop.value).trim();
|
|
1196
|
+
const definite = /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^\[/.test(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1197
|
+
return { ...span, notObject: definite, objectLiteral: false, entries: [], spec: null };
|
|
1198
|
+
}
|
|
1199
|
+
const objectContent = extractObjectContent(prop.value);
|
|
1200
|
+
if (hasUndecidableEntries(objectContent)) {
|
|
1201
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1202
|
+
}
|
|
1203
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1204
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1205
|
+
const entries = [];
|
|
1206
|
+
for (const entry of parseTopLevelProperties(objectContent)) {
|
|
1207
|
+
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1208
|
+
const valueStart = entry.valueStart === void 0 ? innerStart + entry.nameEnd : innerStart + entry.valueStart + (entry.value ? entry.value.length - entry.value.trimStart().length : 0);
|
|
1209
|
+
entries.push({
|
|
1210
|
+
anchor: entry.name,
|
|
1211
|
+
repeat: entry.kind === "data" ? extractStringLiteralValue(entry.value) : null,
|
|
1212
|
+
repeatDefinitelyNotString: entry.kind !== "data" || isDefiniteNonStringLiteral(entry.value),
|
|
1213
|
+
start: innerStart + entry.nameStart,
|
|
1214
|
+
end: innerStart + entry.nameEnd,
|
|
1215
|
+
valueStart,
|
|
1216
|
+
valueEnd: valueStart + (entry.value?.trim().length ?? 0)
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
return { ...span, notObject: false, objectLiteral: true, entries, spec: specFromRecursionValue(prop) };
|
|
1220
|
+
}
|
|
969
1221
|
function analyzeWatchEntries(scriptContent) {
|
|
1222
|
+
return analyzeObjectEntries(scriptContent, RESERVED_WATCH_KEY).map((entry) => ({
|
|
1223
|
+
key: entry.key,
|
|
1224
|
+
start: entry.start,
|
|
1225
|
+
end: entry.end,
|
|
1226
|
+
// メソッド短縮記法は関数。data は値リテラルの形で判定し、識別子参照は疑わない。
|
|
1227
|
+
definitelyNotFunction: entry.kind === "data" && isNonFunctionLiteral(entry.value)
|
|
1228
|
+
}));
|
|
1229
|
+
}
|
|
1230
|
+
function analyzeListKeyEntries(scriptContent) {
|
|
1231
|
+
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1232
|
+
}
|
|
1233
|
+
function hasDefaultExportObject(scriptContent) {
|
|
1234
|
+
return locateDefaultExportObject(scriptContent) !== null;
|
|
1235
|
+
}
|
|
1236
|
+
function hasTopLevelSpread(scriptContent) {
|
|
1237
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1238
|
+
if (!root) return false;
|
|
1239
|
+
const scan = maskCommentsAndStrings(root.content);
|
|
1240
|
+
let depth = 0;
|
|
1241
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1242
|
+
const ch = scan[i];
|
|
1243
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
1244
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
1245
|
+
else if (depth === 0 && ch === "." && scan.startsWith("...", i)) return true;
|
|
1246
|
+
}
|
|
1247
|
+
return false;
|
|
1248
|
+
}
|
|
1249
|
+
function analyzeObjectEntries(scriptContent, key) {
|
|
970
1250
|
const root = locateDefaultExportObject(scriptContent);
|
|
971
1251
|
if (!root) return [];
|
|
972
|
-
const
|
|
973
|
-
if (!
|
|
1252
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1253
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
|
|
974
1254
|
return [];
|
|
975
1255
|
}
|
|
976
|
-
const leading =
|
|
977
|
-
const innerStart = root.start +
|
|
1256
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1257
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
978
1258
|
const entries = [];
|
|
979
|
-
for (const entry of parseTopLevelProperties(extractObjectContent(
|
|
1259
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
|
|
980
1260
|
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
981
1261
|
entries.push({
|
|
982
1262
|
key: entry.name,
|
|
983
1263
|
start: innerStart + entry.nameStart,
|
|
984
1264
|
end: innerStart + entry.nameEnd,
|
|
985
|
-
|
|
986
|
-
|
|
1265
|
+
kind: entry.kind,
|
|
1266
|
+
value: entry.value
|
|
987
1267
|
});
|
|
988
1268
|
}
|
|
989
1269
|
return entries;
|
|
@@ -1093,6 +1373,9 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1093
1373
|
if (listPath.length === 0 || segments.some((s) => s.length === 0) || segments[segments.length - 1] === "*") {
|
|
1094
1374
|
return;
|
|
1095
1375
|
}
|
|
1376
|
+
if (hasRecursionWildcard(listPath)) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1096
1379
|
const has = (path) => paths.some((p) => p.path === path);
|
|
1097
1380
|
if (!has(listPath)) paths.push({ path: listPath, kind: "data", typeHint: "array" });
|
|
1098
1381
|
if (!has(`${listPath}.*`)) paths.push({ path: `${listPath}.*`, kind: "list" });
|
|
@@ -1107,8 +1390,42 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1107
1390
|
}
|
|
1108
1391
|
function extractStringLiteralValue(value) {
|
|
1109
1392
|
if (!value) return null;
|
|
1110
|
-
const match = value.trim().match(/^["']([^"'\\]*)["']$/);
|
|
1111
|
-
|
|
1393
|
+
const match = value.trim().match(/^(?:["']([^"'\\]*)["']|`([^`\\$]*)`)$/);
|
|
1394
|
+
const literal2 = match ? match[1] ?? match[2] : null;
|
|
1395
|
+
return literal2 !== null && literal2 !== void 0 && literal2.length > 0 ? literal2 : null;
|
|
1396
|
+
}
|
|
1397
|
+
function hasUndecidableEntries(objectContent) {
|
|
1398
|
+
const scan = maskCommentsAndStrings(objectContent);
|
|
1399
|
+
let depth = 0;
|
|
1400
|
+
let atKey = true;
|
|
1401
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1402
|
+
const ch = scan[i];
|
|
1403
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
1404
|
+
if (depth === 0 && atKey && (ch === "[" || scan.startsWith("...", i))) return true;
|
|
1405
|
+
depth++;
|
|
1406
|
+
atKey = false;
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
1410
|
+
depth--;
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
if (depth !== 0) continue;
|
|
1414
|
+
if (ch === ",") {
|
|
1415
|
+
atKey = true;
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
if (/\s/.test(ch)) continue;
|
|
1419
|
+
if (atKey && scan.startsWith("...", i)) return true;
|
|
1420
|
+
atKey = false;
|
|
1421
|
+
}
|
|
1422
|
+
return false;
|
|
1423
|
+
}
|
|
1424
|
+
function isDefiniteNonStringLiteral(value) {
|
|
1425
|
+
if (!value) return false;
|
|
1426
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1427
|
+
if (scan.length === 0) return false;
|
|
1428
|
+
return /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^[[{]/.test(scan) || /^(?:async\s+)?function\b/.test(scan) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1112
1429
|
}
|
|
1113
1430
|
var ROW_ASSIGN = new RegExp(
|
|
1114
1431
|
String.raw`\bthis\s*(?:\.\s*([$\w]+)|\[\s*["']([^"']+)["']\s*\])\s*=(?![=>])\s*(?:(\[)|(?:[^;={}]|=>)*?\.\s*(?:concat|toSpliced|with)\s*(\())`,
|
|
@@ -2100,7 +2417,94 @@ var ja = {
|
|
|
2100
2417
|
default:
|
|
2101
2418
|
return `"mount" \u30D1\u30B9 "${mountPath}" \u306B\u4E88\u7D04\u6587\u5B57\uFF08$, #, @\uFF09\u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08runtime: must not use reserved characters.\uFF09`;
|
|
2102
2419
|
}
|
|
2103
|
-
}
|
|
2420
|
+
},
|
|
2421
|
+
recursionUnsupported: (p, where) => {
|
|
2422
|
+
switch (where) {
|
|
2423
|
+
case "binding":
|
|
2424
|
+
return `"${p}" \u306E "**" \u306F data-wcs \u3067\u306F\u4F7F\u3048\u307E\u305B\u3093\u3002"**" \u306F $recursion \u5BA3\u8A00\u30FB\u518D\u5E30 getter \u306E\u30AD\u30FC\u30FB$getAll / $setAll \u306E\u30D1\u30B9\u5F15\u6570\u3060\u3051\u306E\u8A18\u53F7\u3067\u3059\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u30D0\u30A4\u30F3\u30C9\u78BA\u7ACB\u6642\u306B throw \u3057\u307E\u3059\uFF09\u3002HTML \u3067\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\u3092\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`;
|
|
2425
|
+
case "watch":
|
|
2426
|
+
return `$watch \u306E\u30AD\u30FC "${p}" \u306B "**" \u306F\u4F7F\u3048\u307E\u305B\u3093\u3002\u76E3\u8996\u306F\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u306B\u5BFE\u3057\u3066\u306E\u307F\u6210\u7ACB\u3057\u307E\u3059`;
|
|
2427
|
+
case "resolve":
|
|
2428
|
+
return `$resolve("${p}") \u306B "**" \u306F\u6E21\u305B\u307E\u305B\u3093\u3002$resolve \u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\u3068\u6DFB\u5B57\u30BF\u30D7\u30EB\u306E\u53B3\u5BC6\u4E00\u81F4\u3060\u3051\u3092\u53D7\u3051\u4ED8\u3051\u307E\u3059`;
|
|
2429
|
+
case "assignment":
|
|
2430
|
+
return `this["${p}"] \u3078\u306E\u4EE3\u5165\u306B "**" \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08\u518D\u5E30 setter \u306F\u521D\u7248\u3067\u306F\u6301\u3066\u305A\u3001\u4EE3\u5165\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\u306B\u3057\u304B\u6210\u7ACB\u3057\u307E\u305B\u3093\uFF09\u3002$setAll("${p}", [], value) \u3067\u5168\u6DF1\u3055\u3078\u30D6\u30ED\u30FC\u30C9\u30AD\u30E3\u30B9\u30C8\u3059\u308B\u304B\u3001\u5177\u4F53\u30D1\u30B9\u3078\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`;
|
|
2431
|
+
case "postUpdate":
|
|
2432
|
+
return `$postUpdate("${p}") \u306B "**" \u306F\u6E21\u305B\u307E\u305B\u3093\u3002\u901A\u77E5\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u306B\u5BFE\u3057\u3066\u306E\u307F\u6210\u7ACB\u3057\u307E\u3059`;
|
|
2433
|
+
case "trackDependency":
|
|
2434
|
+
return `$trackDependency("${p}") \u306B "**" \u306F\u6E21\u305B\u307E\u305B\u3093\u3002\u4F9D\u5B58\u306E\u767B\u9332\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u306B\u5BFE\u3057\u3066\u306E\u307F\u6210\u7ACB\u3057\u307E\u3059`;
|
|
2435
|
+
case "listKeys":
|
|
2436
|
+
return `$listKeys \u306E\u30AD\u30FC "${p}" \u306B "**" \u306F\u4F7F\u3048\u307E\u305B\u3093\u3002\u30AD\u30FC\u4ED8\u304D\u30EA\u30B9\u30C8\u306F 1 \u672C\u306E\u5177\u4F53\u30EA\u30B9\u30C8\u30D1\u30B9\u3067\u3059 \u2014 \u6DF1\u3055\u3054\u3068\u306B\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: "nodes.*.children"\uFF09`;
|
|
2437
|
+
default:
|
|
2438
|
+
return `"${p}" \u306F "**" \u3092\u542B\u307F\u307E\u3059\u304C\u3001\u3053\u306E state \u306B\u306F $recursion \u5BA3\u8A00\u304C\u3042\u308A\u307E\u305B\u3093\u3002$recursion = { "<anchor>": "<repeat>" }\uFF08\u4F8B: { "nodes.*": "children.*" }\uFF09\u3092\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BA3\u8A00\u304C\u7121\u3044\u3068 "**" \u306E\u30AD\u30FC\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059\uFF09`;
|
|
2439
|
+
}
|
|
2440
|
+
},
|
|
2441
|
+
recursionAnchorMismatch: (p, anchor) => `"${p}" \u306F\u5BA3\u8A00\u6E08\u307F\u306E\u518D\u5E30\u30A2\u30F3\u30AB\u30FC "${anchor}" \u3068\u5408\u81F4\u3057\u307E\u305B\u3093\uFF08\u521D\u7248\u306F state \u3054\u3068\u306B 1 \u3064\u306E\u81EA\u5DF1\u518D\u5E30\u306E\u307F\u3002"**" \u306E\u5F8C\u308D\u306F\u6574\u5F62\u3055\u308C\u305F\u63A5\u5C3E\u8F9E \u2014 2 \u3064\u76EE\u306E "**"\u30FB\u7A7A\u30BB\u30B0\u30E1\u30F3\u30C8\u30FB"**" \u76F4\u5F8C\u306E\u7D20\u306E "*" \u306F\u7F6E\u3051\u307E\u305B\u3093\uFF09`,
|
|
2442
|
+
recursionGetAllForm: (p, problem) => problem === "notArray" ? `$getAll("${p}", indexes) \u306E "**" \u306E\u6DFB\u5B57\u306F\u3001\u7701\u7565\uFF08\u8A55\u4FA1\u4E2D\u306E\u518D\u5E30 getter \u306E\u6DF1\u3055\uFF09\u304B [] \uFF08\u5168\u6DF1\u3055\uFF09\u306E\u3069\u3061\u3089\u304B\u3067\u3059\u3002null \u3084\u914D\u5217\u3067\u306A\u3044\u5024\u306F\u6E21\u305B\u307E\u305B\u3093` : `$getAll("${p}", indexes) \u306E "**" \u306B\u975E\u7A7A\u306E\u63A5\u982D\u8F9E\u306F\u6E21\u305B\u307E\u305B\u3093\uFF08\u63A5\u982D\u8F9E\u306F\u3069\u306E\u6DF1\u3055\u306B\u9069\u7528\u3055\u308C\u308B\u304B\u3092\u8A00\u3048\u307E\u305B\u3093\uFF09\u3002\u6DFB\u5B57\u3092\u7701\u7565\u3059\u308B\u3068\u8A55\u4FA1\u4E2D\u306E\u518D\u5E30 getter \u306E\u6DF1\u3055\u3001[] \u3092\u6E21\u3059\u3068\u5168\u6DF1\u3055\u306B\u306A\u308A\u307E\u3059`,
|
|
2443
|
+
recursionSetAllForm: (p, problem) => {
|
|
2444
|
+
switch (problem) {
|
|
2445
|
+
case "prefix":
|
|
2446
|
+
return `$setAll("${p}", indexes, \u2026) \u306E "**" \u306B\u975E\u7A7A\u306E\u63A5\u982D\u8F9E\u306F\u6E21\u305B\u307E\u305B\u3093\uFF08\u63A5\u982D\u8F9E\u306F\u3069\u306E\u6DF1\u3055\u306B\u9069\u7528\u3055\u308C\u308B\u304B\u3092\u8A00\u3048\u307E\u305B\u3093\uFF09\u3002[] \u3092\u6E21\u3057\u3066\u5168\u6DF1\u3055\u3078\u30D6\u30ED\u30FC\u30C9\u30AD\u30E3\u30B9\u30C8\u3057\u3066\u304F\u3060\u3055\u3044`;
|
|
2447
|
+
case "noIndexes":
|
|
2448
|
+
return `$setAll("${p}", \u2026) \u306E "**" \u306B\u306F\u660E\u793A\u7684\u306A\u7A7A\u306E\u6DFB\u5B57\u914D\u5217 [] \u304C\u5FC5\u8981\u3067\u3059\uFF08\u66F8\u304D\u8FBC\u307F API \u306F\u6587\u8108\u3092\u53D6\u308A\u307E\u305B\u3093\uFF09`;
|
|
2449
|
+
case "mapper":
|
|
2450
|
+
return `$setAll("${p}", \u2026) \u306E "**" \u306F mapper \u3092\u53D6\u308C\u307E\u305B\u3093\uFF08\u6DFB\u5B57\u30BF\u30D7\u30EB\u306E\u672C\u6570\u304C\u6DF1\u3055\u3054\u3068\u306B\u5909\u308F\u308B\u305F\u3081\uFF09\u3002\u5B9A\u6570\u5024\u3092\u6E21\u3057\u3066\u304F\u3060\u3055\u3044`;
|
|
2451
|
+
default:
|
|
2452
|
+
return `$setAll("${p}", \u2026) \u306E "**" \u306F { spread: true } \u3092\u53D6\u308C\u307E\u305B\u3093\uFF08\u5E73\u5766\u306A\u914D\u5217\u3092\u6728\u306B\u914D\u308B\u306B\u306F\u4F5C\u8005\u304C\u8D70\u67FB\u9806\u3092\u77E5\u308B\u5FC5\u8981\u304C\u3042\u308A\u3001\u5951\u7D04\u306B\u306A\u308A\u307E\u305B\u3093\uFF09`;
|
|
2453
|
+
}
|
|
2454
|
+
},
|
|
2455
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
2456
|
+
switch (target) {
|
|
2457
|
+
case "node":
|
|
2458
|
+
return `$setAll("${p}") \u306F\u518D\u5E30\u306E\u69CB\u9020\u305D\u306E\u3082\u306E\uFF08\u30CE\u30FC\u30C9\uFF09\u3092\u66F8\u304D\u63DB\u3048\u307E\u3059\u3002\u521D\u7248\u306F\u8449\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u3078\u306E\u30D6\u30ED\u30FC\u30C9\u30AD\u30E3\u30B9\u30C8\u306E\u307F\u3067\u3059 \u2014 \u30CE\u30FC\u30C9\u3092\u7F6E\u304D\u63DB\u3048\u308B\u3068\u3053\u306E\u66F8\u304D\u8FBC\u307F\u306E\u305F\u3081\u306B\u78BA\u5B9A\u6E08\u307F\u306E\u5B50\u30A2\u30C9\u30EC\u30B9\u304C\u7121\u52B9\u306B\u306A\u308A\u307E\u3059`;
|
|
2459
|
+
case "branch":
|
|
2460
|
+
return `$setAll("${p}") \u306F\u518D\u5E30\u306E\u69CB\u9020\u305D\u306E\u3082\u306E\uFF08"${repeatList}" \u30EA\u30B9\u30C8\u3078\u81F3\u308B\u9014\u4E2D\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\uFF09\u3092\u66F8\u304D\u63DB\u3048\u307E\u3059\u3002\u521D\u7248\u306F\u8449\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u3078\u306E\u30D6\u30ED\u30FC\u30C9\u30AD\u30E3\u30B9\u30C8\u306E\u307F\u3067\u3059 \u2014 \u7F6E\u304D\u63DB\u3048\u308B\u3068\u305D\u306E\u4E0B\u306E\u78BA\u5B9A\u6E08\u307F\u306E\u5B50\u30A2\u30C9\u30EC\u30B9\u304C\u7121\u52B9\u306B\u306A\u308A\u307E\u3059`;
|
|
2461
|
+
case "length":
|
|
2462
|
+
return `$setAll("${p}") \u306F\u518D\u5E30\u306E\u69CB\u9020\u305D\u306E\u3082\u306E\uFF08"${repeatList}" \u30EA\u30B9\u30C8\u306E length\uFF09\u3092\u66F8\u304D\u63DB\u3048\u307E\u3059\u3002length \u3078\u306E\u4EE3\u5165\u306F\u914D\u5217\u3092\u5207\u308A\u8A70\u3081\u308B\u306E\u3067\u3001\u30EA\u30B9\u30C8\u306E\u7F6E\u63DB\u3068\u540C\u3058\u304F\u305D\u306E\u4E0B\u306E\u78BA\u5B9A\u6E08\u307F\u306E\u5B50\u30A2\u30C9\u30EC\u30B9\u304C\u7121\u52B9\u306B\u306A\u308A\u307E\u3059`;
|
|
2463
|
+
default:
|
|
2464
|
+
return `$setAll("${p}") \u306F\u518D\u5E30\u306E\u69CB\u9020\u305D\u306E\u3082\u306E\uFF08"${repeatList}" \u30EA\u30B9\u30C8\uFF09\u3092\u66F8\u304D\u63DB\u3048\u307E\u3059\u3002\u521D\u7248\u306F\u8449\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u3078\u306E\u30D6\u30ED\u30FC\u30C9\u30AD\u30E3\u30B9\u30C8\u306E\u307F\u3067\u3059`;
|
|
2465
|
+
}
|
|
2466
|
+
},
|
|
2467
|
+
recursionReadonly: (subject, getterPath) => `${subject} \u306F\u518D\u5E30 getter "${getterPath}" \u306B\u66F8\u304D\u8FBC\u307F\u307E\u3059\uFF08setter \u306F\u521D\u7248\u3067\u306F\u6301\u3066\u307E\u305B\u3093\u3002\u3053\u306E\u7DB4\u308A\u306F\u305D\u306E getter \u306E\u3042\u308B\u6DF1\u3055\u306E\u5C55\u958B\u5F62\u304B\u3001\u5C0E\u51FA\u5024\u306E\u5185\u5074\u3067\u3059\uFF09\u3002\u3053\u306E getter \u304C\u5C0E\u51FA\u5143\u306B\u3057\u3066\u3044\u308B\u5024\u306E\u5074\u3092\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`,
|
|
2468
|
+
recursionInVolume: (subject, mountPath) => `${subject} \u306F\u30DC\u30EA\u30E5\u30FC\u30E0\uFF08mount="${mountPath}"\uFF09\u3067\u306F\u5BA3\u8A00\u3067\u304D\u307E\u305B\u3093\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u63A5\u304E\u6728\u306E\u524D\u306B throw \u3057\u307E\u3059\uFF09\u3002\u518D\u5E30\u306E\u5BA3\u8A00\u3068 "**" getter \u306F\u30EB\u30FC\u30C8\u306E state \u306B\u7F6E\u3044\u3066\u304F\u3060\u3055\u3044 \u2014 \u30A2\u30F3\u30AB\u30FC\u306E\u30D1\u30B9\u306F\u30EB\u30FC\u30C8\u306E\u6728\u306B\u5BFE\u3057\u3066\u89E3\u6C7A\u3055\u308C\u307E\u3059`,
|
|
2469
|
+
recursionNotObject: () => `$recursion \u306F\u300C\u30A2\u30F3\u30AB\u30FC \u2192 \u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9\u300D\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u4F8B: { "nodes.*": "children.*" }\u3002\u3053\u306E\u5F62\u306F\u30E9\u30F3\u30BF\u30A4\u30E0\u304C\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
|
|
2470
|
+
recursionAnchorCount: (count) => count === 0 ? `$recursion \u306B\u306F\u30A2\u30F3\u30AB\u30FC\u304C\u3061\u3087\u3046\u3069 1 \u3064\u5FC5\u8981\u3067\u3059\uFF08\u7A7A\u306E\u5BA3\u8A00\u3067\u3059\uFF09` : `$recursion \u304C ${count} \u500B\u306E\u30A2\u30F3\u30AB\u30FC\u3092\u5BA3\u8A00\u3057\u3066\u3044\u307E\u3059\u3002\u521D\u7248\u306F state \u3054\u3068\u306B\u3061\u3087\u3046\u3069 1 \u3064\u306E\u81EA\u5DF1\u518D\u5E30\u306E\u307F\u5BFE\u5FDC\u3057\u307E\u3059`,
|
|
2471
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
2472
|
+
const subject = kind === "anchor" ? "$recursion \u306E\u30A2\u30F3\u30AB\u30FC" : "$recursion \u306E\u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9";
|
|
2473
|
+
switch (problem) {
|
|
2474
|
+
case "empty":
|
|
2475
|
+
return `${subject}\u306F\u7A7A\u3067\u306A\u3044\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`;
|
|
2476
|
+
case "emptySegment":
|
|
2477
|
+
return `${subject} "${path}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`;
|
|
2478
|
+
case "notElement":
|
|
2479
|
+
return `${subject} "${path}" \u306F\u30EA\u30B9\u30C8\u306E\u8981\u7D20\u3092\u6307\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 \u2014 \u672B\u5C3E\u304C ".*" \u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u30D1\u30B9\uFF08\u4F8B: "nodes.*"\uFF09\u306B\u3057\u3066\u304F\u3060\u3055\u3044`;
|
|
2480
|
+
case "reservedRoot":
|
|
2481
|
+
return `${subject} "${path}" \u306F "$" \u3067\u59CB\u3081\u3089\u308C\u307E\u305B\u3093\uFF08\u4E88\u7D04\u540D\u524D\u7A7A\u9593\uFF09`;
|
|
2482
|
+
case "reservedMount":
|
|
2483
|
+
return `${subject} "${path}" \u306B "#" \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08\u30DE\u30A6\u30F3\u30C8\u7528\u306E\u4E88\u7D04\u30BB\u30B0\u30E1\u30F3\u30C8\uFF09`;
|
|
2484
|
+
case "midWildcard":
|
|
2485
|
+
return `${subject} "${path}" \u306E "*" \u306F\u672B\u5C3E\u306B\u3061\u3087\u3046\u3069 1 \u3064\u3060\u3051\u7F6E\u3051\u307E\u3059\uFF08\u9014\u4E2D\u306E\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u306F\u521D\u7248\u3067\u306F\u672A\u5BFE\u5FDC\uFF09`;
|
|
2486
|
+
case "indexSegment":
|
|
2487
|
+
return `${subject} "${path}" \u306B\u6DFB\u5B57\u30BB\u30B0\u30E1\u30F3\u30C8\u306F\u542B\u3081\u3089\u308C\u307E\u305B\u3093\uFF08\u518D\u5E30\u306F\u6728\u306E\u5F62\u306B\u5BFE\u3059\u308B\u5BA3\u8A00\u3067\u3042\u3063\u3066\u30011 \u884C\u306B\u5BFE\u3059\u308B\u5BA3\u8A00\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF09`;
|
|
2488
|
+
default:
|
|
2489
|
+
return `${subject} "${path}" \u306B "**" \u306F\u542B\u3081\u3089\u308C\u307E\u305B\u3093\uFF08"**" \u306B\u610F\u5473\u3092\u4E0E\u3048\u308B\u306E\u304C\u3053\u306E\u5BA3\u8A00\u305D\u306E\u3082\u306E\u3067\u3059\uFF09`;
|
|
2490
|
+
}
|
|
2491
|
+
},
|
|
2492
|
+
recursionRepeatNotString: (anchor) => `$recursion \u306E\u30A8\u30F3\u30C8\u30EA "${anchor}" \u306E\u5024\u306F\u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9\u306E\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u4F8B: "children.*"\uFF09`,
|
|
2493
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
2494
|
+
switch (problem) {
|
|
2495
|
+
case "setter":
|
|
2496
|
+
return `\u518D\u5E30 setter \u306F\u521D\u7248\u3067\u306F\u672A\u5BFE\u5FDC\u3067\u3059: "${key}"\u3002\u901A\u5E38\u306E\u30D1\u30B9 setter \u3092\u5BA3\u8A00\u3059\u308B\u304B\u3001\u5177\u4F53\u30D1\u30B9\u7D4C\u7531\u3067\u66F8\u304D\u8FBC\u3093\u3067\u304F\u3060\u3055\u3044`;
|
|
2497
|
+
case "notGetter":
|
|
2498
|
+
return `"${key}" \u306F "**" \u3092\u542B\u307F\u307E\u3059\u304C getter \u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002"**" \u306F\u8A08\u7B97\u30D1\u30B9\u306E\u65CF\u3092\u540D\u6307\u3059\u8A18\u53F7\u3067\u3059`;
|
|
2499
|
+
case "structural":
|
|
2500
|
+
return `"${key}" \u306F\u518D\u5E30\u306E\u69CB\u9020\u305D\u306E\u3082\u306E\uFF08\u30CE\u30FC\u30C9\u30FB\u5B50\u30EA\u30B9\u30C8\u30FB\u305D\u306E length\u30FB\u5B50\u30EA\u30B9\u30C8\u3078\u81F3\u308B\u9014\u4E2D\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\uFF09\u3092\u540D\u6307\u3057\u3066\u3044\u307E\u3059\u3002\u518D\u5E30 getter \u306F\u5168\u6DF1\u3055\u3067\u5B9F\u30C7\u30FC\u30BF\u306E\u5B50\u30EA\u30B9\u30C8\u3092\u5F71\u306B\u3057\u3066\u3057\u307E\u3044\u307E\u3059\u3002"**" \u306F\u30CE\u30FC\u30C9\u306E\u4E0B\u306E\u8A08\u7B97\u30D1\u30B9\uFF08\u4F8B: "${anchor}.total"\uFF09\u3092\u540D\u6307\u3059\u8A18\u53F7\u3067\u3059`;
|
|
2501
|
+
default:
|
|
2502
|
+
return `"${key}" \u306F\u518D\u5E30\u30CE\u30FC\u30C9\u81EA\u8EAB\u3092\u540D\u6307\u3057\u3066\u3044\u307E\u3059\u3002"**" \u306F\u30CE\u30FC\u30C9\u306E\u4E0B\u306E\u8A08\u7B97\u30D1\u30B9\uFF08\u4F8B: "${anchor}.total"\uFF09\u3092\u540D\u6307\u3059\u8A18\u53F7\u3067\u3001\u30CE\u30FC\u30C9\u305D\u306E\u3082\u306E\u3067\u306F\u3042\u308A\u307E\u305B\u3093`;
|
|
2503
|
+
}
|
|
2504
|
+
},
|
|
2505
|
+
recursionGetterCollision: (a, b, repeat) => `"${a}" \u3068 "${b}" \u306F\u7570\u306A\u308B\u6DF1\u3055\u3067\u540C\u3058\u5177\u4F53\u30D1\u30B9\u3078\u5C55\u958B\u3057\u307E\u3059\uFF08\u5DEE\u304C "${repeat}" \u306E\u6574\u6570\u56DE\u3076\u3093\u3067\u3059\uFF09\u3002\u3069\u3061\u3089\u304B\u306E\u540D\u524D\u3092\u5909\u3048\u3066\u304F\u3060\u3055\u3044`,
|
|
2506
|
+
recursionConcreteCollision: (concreteKey, recursiveKey) => `"${concreteKey}" \u306F state \u306B\u5B9A\u7FA9\u6E08\u307F\u306A\u306E\u3067\u3001\u518D\u5E30 getter "${recursiveKey}" \u306F\u305D\u3053\u3078\u5C55\u958B\u3067\u304D\u307E\u305B\u3093\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u5BA3\u8A00\u3092\u8AAD\u3093\u3060\u6642\u70B9\u3067 throw \u3057\u307E\u3059\uFF09\u3002\u3069\u3061\u3089\u304B\u306E\u540D\u524D\u3092\u5909\u3048\u3066\u304F\u3060\u3055\u3044`,
|
|
2507
|
+
recursionInMountedComponent: (subject) => `${subject} \u306F\u30DE\u30A6\u30F3\u30C8\u3055\u308C\u305F\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\uFF08bind-component\uFF09\u3067\u306F\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F wcs/mount-dollar-declaration \u3067\u8B66\u544A\u3057\u3001\u9ED9\u3063\u3066\u6368\u3066\u307E\u3059\uFF09\u3002$recursion \u3068 "**" getter \u306F\u30EB\u30FC\u30C8\u306E state \u306B\u7F6E\u3044\u3066\u304F\u3060\u3055\u3044 \u2014 \u30A2\u30F3\u30AB\u30FC\u306E\u30D1\u30B9\u306F\u30EB\u30FC\u30C8\u306E\u6728\u306B\u5BFE\u3057\u3066\u89E3\u6C7A\u3055\u308C\u307E\u3059`
|
|
2104
2508
|
};
|
|
2105
2509
|
var EN_EXPECTED_LABEL = {
|
|
2106
2510
|
array: "an array-typed path",
|
|
@@ -2172,7 +2576,94 @@ var en = {
|
|
|
2172
2576
|
default:
|
|
2173
2577
|
return `"mount" path "${mountPath}" must not use reserved characters ($, #, @).`;
|
|
2174
2578
|
}
|
|
2175
|
-
}
|
|
2579
|
+
},
|
|
2580
|
+
recursionUnsupported: (p, where) => {
|
|
2581
|
+
switch (where) {
|
|
2582
|
+
case "binding":
|
|
2583
|
+
return `"**" in "${p}" cannot be used in data-wcs. "**" is only meaningful in a $recursion declaration, in a recursive getter key, and in the path argument of $getAll / $setAll (the runtime throws when the binding is established). Write the expanded concrete path in HTML instead`;
|
|
2584
|
+
case "watch":
|
|
2585
|
+
return `$watch key "${p}" cannot contain "**" \u2014 watching is defined against a concrete path (a fixed number of "*")`;
|
|
2586
|
+
case "resolve":
|
|
2587
|
+
return `$resolve("${p}") cannot take "**" \u2014 it accepts only an expanded concrete path with an exactly matching index tuple`;
|
|
2588
|
+
case "assignment":
|
|
2589
|
+
return `this["${p}"] = \u2026 cannot use "**" (there are no recursive setters in this version; an assignment only resolves against an expanded concrete path). Broadcast with $setAll("${p}", [], value), or write a concrete path`;
|
|
2590
|
+
case "postUpdate":
|
|
2591
|
+
return `$postUpdate("${p}") cannot take "**" \u2014 a notification is defined against a concrete path (a fixed number of "*")`;
|
|
2592
|
+
case "trackDependency":
|
|
2593
|
+
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
2594
|
+
case "listKeys":
|
|
2595
|
+
return `$listKeys key "${p}" cannot contain "**" \u2014 a keyed list is one concrete list path. Declare the key per depth instead (for example "nodes.*.children")`;
|
|
2596
|
+
default:
|
|
2597
|
+
return `"${p}" contains "**" but this state declares no $recursion anchor. Declare $recursion = { "<anchor>": "<repeat>" } (for example { "nodes.*": "children.*" }) \u2014 without it a "**" key is silently ignored`;
|
|
2598
|
+
}
|
|
2599
|
+
},
|
|
2600
|
+
recursionAnchorMismatch: (p, anchor) => `"${p}" does not match the declared recursion anchor "${anchor}" (this version supports exactly one self-recursive anchor per state, and "**" must be followed by a well-formed suffix: no second "**", no empty segment, no bare "*" right after "**")`,
|
|
2601
|
+
recursionGetAllForm: (p, problem) => problem === "notArray" ? `$getAll("${p}", indexes) with "**" takes either no indexes (to read the depth of the recursive getter being evaluated) or [] (to walk every depth) \u2014 not null or a non-array value` : `$getAll("${p}", indexes) with "**" takes no partial prefix: a prefix cannot say which depth it applies to. Omit the indexes to read the depth of the recursive getter being evaluated, or pass [] to walk every depth`,
|
|
2602
|
+
recursionSetAllForm: (p, problem) => {
|
|
2603
|
+
switch (problem) {
|
|
2604
|
+
case "prefix":
|
|
2605
|
+
return `$setAll("${p}", indexes, \u2026) with "**" takes no partial prefix: a prefix cannot say which depth it applies to. Pass [] to broadcast to every depth`;
|
|
2606
|
+
case "noIndexes":
|
|
2607
|
+
return `$setAll("${p}", \u2026) with "**" requires an explicit empty indexes array ([]) \u2014 the write API takes no context`;
|
|
2608
|
+
case "mapper":
|
|
2609
|
+
return `$setAll("${p}", \u2026) with "**" does not take a mapper \u2014 the index tuple has a different length at each depth. Pass a constant value`;
|
|
2610
|
+
default:
|
|
2611
|
+
return `$setAll("${p}", \u2026) with "**" does not take { spread: true } \u2014 handing a flat array to a tree needs the author to know the walk order, which is not a usable contract`;
|
|
2612
|
+
}
|
|
2613
|
+
},
|
|
2614
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
2615
|
+
switch (target) {
|
|
2616
|
+
case "node":
|
|
2617
|
+
return `$setAll("${p}") writes the recursion structure itself (a node). This version broadcasts to leaf properties only \u2014 replacing a node would invalidate the child addresses already resolved for this write`;
|
|
2618
|
+
case "branch":
|
|
2619
|
+
return `$setAll("${p}") writes the recursion structure itself (an object on the way to the "${repeatList}" list). This version broadcasts to leaf properties only \u2014 replacing it would invalidate the child addresses already resolved below it`;
|
|
2620
|
+
case "length":
|
|
2621
|
+
return `$setAll("${p}") writes the recursion structure itself (the length of the "${repeatList}" list). Assigning length truncates the array, which invalidates the child addresses already resolved below it just like replacing the list`;
|
|
2622
|
+
default:
|
|
2623
|
+
return `$setAll("${p}") writes the recursion structure itself (the "${repeatList}" list). This version broadcasts to leaf properties only`;
|
|
2624
|
+
}
|
|
2625
|
+
},
|
|
2626
|
+
recursionReadonly: (subject, getterPath) => `${subject} writes into the recursive getter "${getterPath}", which has no setter in this version (this spelling is that getter at one depth, or a path inside the value it derives). Write the values it derives from instead`,
|
|
2627
|
+
recursionInVolume: (subject, mountPath) => `${subject} cannot be declared in a volume (mount="${mountPath}"); the runtime throws before grafting. Declare the recursion and its "**" getters on the root state \u2014 the anchor path is resolved against the root tree`,
|
|
2628
|
+
recursionNotObject: () => `$recursion must be an object mapping one anchor path to its repeating sub-path (for example { "nodes.*": "children.*" }; the runtime throws at load time for this shape)`,
|
|
2629
|
+
recursionAnchorCount: (count) => count === 0 ? `$recursion must declare exactly one anchor; it is empty` : `$recursion declares ${count} anchors. This version supports exactly one self-recursive anchor per state`,
|
|
2630
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
2631
|
+
const subject = kind === "anchor" ? "$recursion anchor" : "$recursion repeating sub-path";
|
|
2632
|
+
switch (problem) {
|
|
2633
|
+
case "empty":
|
|
2634
|
+
return `${subject} must be a non-empty string`;
|
|
2635
|
+
case "emptySegment":
|
|
2636
|
+
return `${subject} "${path}" must not contain empty path segments`;
|
|
2637
|
+
case "notElement":
|
|
2638
|
+
return `${subject} "${path}" must name a list element: a property path ending with ".*" (for example "nodes.*")`;
|
|
2639
|
+
case "reservedRoot":
|
|
2640
|
+
return `${subject} "${path}" must not start with "$" \u2014 that namespace is reserved`;
|
|
2641
|
+
case "reservedMount":
|
|
2642
|
+
return `${subject} "${path}" must not contain "#" \u2014 that segment is reserved for mounts`;
|
|
2643
|
+
case "midWildcard":
|
|
2644
|
+
return `${subject} "${path}" must have exactly one "*", at the end (wildcards in the middle are not supported in this version)`;
|
|
2645
|
+
case "indexSegment":
|
|
2646
|
+
return `${subject} "${path}" must not contain an index segment \u2014 the recursion is declared over the shape of the tree, not over one row`;
|
|
2647
|
+
default:
|
|
2648
|
+
return `${subject} "${path}" must not contain "**" \u2014 the declaration is what gives "**" its meaning`;
|
|
2649
|
+
}
|
|
2650
|
+
},
|
|
2651
|
+
recursionRepeatNotString: (anchor) => `$recursion entry "${anchor}" must map to the repeating sub-path as a string (for example "children.*")`,
|
|
2652
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
2653
|
+
switch (problem) {
|
|
2654
|
+
case "setter":
|
|
2655
|
+
return `Recursive setters are not supported in this version: "${key}". Declare a plain path setter, or write through the concrete path`;
|
|
2656
|
+
case "notGetter":
|
|
2657
|
+
return `"${key}" contains "**" but is not a getter. The recursion wildcard only names a family of computed paths`;
|
|
2658
|
+
case "structural":
|
|
2659
|
+
return `"${key}" names the recursion structure itself (a node, its child list, that list's length, or an object on the way to the list). A recursive getter would hide the real child list at every depth \u2014 "**" names a computed leaf under a node (for example "${anchor}.total")`;
|
|
2660
|
+
default:
|
|
2661
|
+
return `"${key}" names the recursive node itself. "**" names a computed path under a node (for example "${anchor}.total"), not the node`;
|
|
2662
|
+
}
|
|
2663
|
+
},
|
|
2664
|
+
recursionGetterCollision: (a, b, repeat) => `"${a}" and "${b}" expand to the same concrete path at different depths (they differ by whole repetitions of "${repeat}"). Rename one of them`,
|
|
2665
|
+
recursionConcreteCollision: (concreteKey, recursiveKey) => `"${concreteKey}" is already defined on the state, so the recursive getter "${recursiveKey}" cannot expand to it (the runtime throws when the declaration is read). Rename one of them`,
|
|
2666
|
+
recursionInMountedComponent: (subject) => `${subject} is not run by a mounted component (bind-component); the runtime warns with wcs/mount-dollar-declaration and drops it. Declare $recursion and "**" getters on the root state \u2014 the anchor path is resolved against the root tree`
|
|
2176
2667
|
};
|
|
2177
2668
|
var CATALOGS = { ja, en };
|
|
2178
2669
|
function getMessages(locale3) {
|
|
@@ -2494,7 +2985,11 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2494
2985
|
}
|
|
2495
2986
|
if (checkPath) {
|
|
2496
2987
|
const schema = applicationSchema;
|
|
2497
|
-
const verdict =
|
|
2988
|
+
const verdict = hasRecursionWildcard(checkPath) ? {
|
|
2989
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
2990
|
+
message: msgs.recursionUnsupported(checkPath, "binding"),
|
|
2991
|
+
severity: "error"
|
|
2992
|
+
} : schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2498
2993
|
if (verdict) {
|
|
2499
2994
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2500
2995
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -2513,7 +3008,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2513
3008
|
const pathTrimmed = parsed.path.trim();
|
|
2514
3009
|
const prop = parsed.property.replace(/#.*$/, "");
|
|
2515
3010
|
const insideFor = isInsideForTemplate(html, attr.valueStart, attrName);
|
|
2516
|
-
if (pathTrimmed && !prop.startsWith("on")) {
|
|
3011
|
+
if (pathTrimmed && !prop.startsWith("on") && !hasRecursionWildcard(pathTrimmed)) {
|
|
2517
3012
|
if (!insideFor && pathTrimmed.includes("*")) {
|
|
2518
3013
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2519
3014
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -2793,11 +3288,16 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
|
|
|
2793
3288
|
}
|
|
2794
3289
|
return null;
|
|
2795
3290
|
}
|
|
2796
|
-
if (!scopedPathSet.has(checkPath)) {
|
|
3291
|
+
if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
|
|
2797
3292
|
return msgs.pathMissing(displayPath);
|
|
2798
3293
|
}
|
|
2799
3294
|
return null;
|
|
2800
3295
|
}
|
|
3296
|
+
function matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet) {
|
|
3297
|
+
const specs = collectRecursionSpecs(scopedPaths);
|
|
3298
|
+
if (specs.length === 0) return false;
|
|
3299
|
+
return matchesRecursion(specs, checkPath, (candidate) => scopedPathSet.has(candidate));
|
|
3300
|
+
}
|
|
2801
3301
|
function toMissingVerdict(message) {
|
|
2802
3302
|
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
2803
3303
|
}
|
|
@@ -2806,6 +3306,7 @@ function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scoped
|
|
|
2806
3306
|
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2807
3307
|
}
|
|
2808
3308
|
if (scopedPathSet.has(checkPath)) return null;
|
|
3309
|
+
if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
|
|
2809
3310
|
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
2810
3311
|
if (resolution.kind === "nonexistent") {
|
|
2811
3312
|
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
@@ -3219,6 +3720,13 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
3219
3720
|
const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
|
|
3220
3721
|
const defaultSchema = applicationSchema;
|
|
3221
3722
|
const missingVerdict = (path, displayPath, pathSet2, scoped) => {
|
|
3723
|
+
if (hasRecursionWildcard(path)) {
|
|
3724
|
+
return {
|
|
3725
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
3726
|
+
severity: "error",
|
|
3727
|
+
message: msgs.recursionUnsupported(path, "binding")
|
|
3728
|
+
};
|
|
3729
|
+
}
|
|
3222
3730
|
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
3223
3731
|
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
3224
3732
|
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
@@ -3351,7 +3859,7 @@ function isValidTemplatePath(path, pathSet, scopedPaths) {
|
|
|
3351
3859
|
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
3352
3860
|
return !hasNamespace || pathSet.has(path);
|
|
3353
3861
|
}
|
|
3354
|
-
return pathSet.has(path);
|
|
3862
|
+
return pathSet.has(path) || matchesRecursionCandidates(scopedPaths, path, pathSet);
|
|
3355
3863
|
}
|
|
3356
3864
|
|
|
3357
3865
|
// src/service/generated/builtinTags.generated.ts
|
|
@@ -5077,7 +5585,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
|
5077
5585
|
const paths = analyzeStatePaths(block.content);
|
|
5078
5586
|
const pathSet = new Set(paths.map((p) => p.path));
|
|
5079
5587
|
for (const entry of entries) {
|
|
5080
|
-
const diagnostic = validateEntry(entry, pathSet, msgs);
|
|
5588
|
+
const diagnostic = validateEntry(entry, pathSet, paths, msgs);
|
|
5081
5589
|
if (diagnostic === null) continue;
|
|
5082
5590
|
out.push({
|
|
5083
5591
|
code: diagnostic.code,
|
|
@@ -5090,7 +5598,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
|
5090
5598
|
}
|
|
5091
5599
|
return out;
|
|
5092
5600
|
}
|
|
5093
|
-
function validateEntry(entry, pathSet, msgs) {
|
|
5601
|
+
function validateEntry(entry, pathSet, paths, msgs) {
|
|
5094
5602
|
const { key } = entry;
|
|
5095
5603
|
const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
|
|
5096
5604
|
if (key.includes(STATE_NAME_SEPARATOR)) {
|
|
@@ -5102,10 +5610,17 @@ function validateEntry(entry, pathSet, msgs) {
|
|
|
5102
5610
|
if (key.split(".").some((segment) => segment.length === 0)) {
|
|
5103
5611
|
return invalid(msgs.watchKeyEmptySegment(key));
|
|
5104
5612
|
}
|
|
5613
|
+
if (hasRecursionWildcard(key)) {
|
|
5614
|
+
return {
|
|
5615
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
5616
|
+
message: msgs.recursionUnsupported(key, "watch"),
|
|
5617
|
+
severity: "error"
|
|
5618
|
+
};
|
|
5619
|
+
}
|
|
5105
5620
|
if (entry.definitelyNotFunction) {
|
|
5106
5621
|
return invalid(msgs.watchHandlerNotFunction(key));
|
|
5107
5622
|
}
|
|
5108
|
-
if (pathSet.size > 0 && !pathSet.has(key)) {
|
|
5623
|
+
if (pathSet.size > 0 && !pathSet.has(key) && !matchesRecursion(collectRecursionSpecs(paths), key, (p) => pathSet.has(p))) {
|
|
5109
5624
|
return {
|
|
5110
5625
|
code: WcsDiagnosticCode.WatchPathMissing,
|
|
5111
5626
|
message: msgs.watchPathMissing(key),
|
|
@@ -5115,6 +5630,542 @@ function validateEntry(entry, pathSet, msgs) {
|
|
|
5115
5630
|
return null;
|
|
5116
5631
|
}
|
|
5117
5632
|
|
|
5633
|
+
// src/service/scriptCallArgs.ts
|
|
5634
|
+
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
5635
|
+
var TEMPLATE_NO_SUBST = /^\s*`((?:\\.|[^\\`$]|\$(?!\{))*)`\s*$/;
|
|
5636
|
+
function splitCallArgs(source, open) {
|
|
5637
|
+
const args = [];
|
|
5638
|
+
const starts = [];
|
|
5639
|
+
let depth = 0;
|
|
5640
|
+
let argStart = open;
|
|
5641
|
+
let i = open;
|
|
5642
|
+
while (i < source.length) {
|
|
5643
|
+
const ch = source[i];
|
|
5644
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
5645
|
+
const quote = ch;
|
|
5646
|
+
i++;
|
|
5647
|
+
while (i < source.length) {
|
|
5648
|
+
if (source[i] === "\\") {
|
|
5649
|
+
i += 2;
|
|
5650
|
+
continue;
|
|
5651
|
+
}
|
|
5652
|
+
if (source[i] === quote) {
|
|
5653
|
+
i++;
|
|
5654
|
+
break;
|
|
5655
|
+
}
|
|
5656
|
+
i++;
|
|
5657
|
+
}
|
|
5658
|
+
continue;
|
|
5659
|
+
}
|
|
5660
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
5661
|
+
depth++;
|
|
5662
|
+
i++;
|
|
5663
|
+
continue;
|
|
5664
|
+
}
|
|
5665
|
+
if (ch === ")" && depth === 0) {
|
|
5666
|
+
args.push(source.slice(argStart, i));
|
|
5667
|
+
starts.push(argStart);
|
|
5668
|
+
return { args, starts, end: i + 1 };
|
|
5669
|
+
}
|
|
5670
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
5671
|
+
depth--;
|
|
5672
|
+
i++;
|
|
5673
|
+
continue;
|
|
5674
|
+
}
|
|
5675
|
+
if (ch === "," && depth === 0) {
|
|
5676
|
+
args.push(source.slice(argStart, i));
|
|
5677
|
+
starts.push(argStart);
|
|
5678
|
+
argStart = i + 1;
|
|
5679
|
+
i++;
|
|
5680
|
+
continue;
|
|
5681
|
+
}
|
|
5682
|
+
i++;
|
|
5683
|
+
}
|
|
5684
|
+
return null;
|
|
5685
|
+
}
|
|
5686
|
+
function literalString(arg) {
|
|
5687
|
+
const match = STRING_LITERAL.exec(arg);
|
|
5688
|
+
if (match !== null) return match[2];
|
|
5689
|
+
const template = TEMPLATE_NO_SUBST.exec(arg);
|
|
5690
|
+
return template === null ? null : template[1];
|
|
5691
|
+
}
|
|
5692
|
+
function literalArrayLength(arg) {
|
|
5693
|
+
const trimmed = arg.trim();
|
|
5694
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
5695
|
+
const inner = trimmed.slice(1, -1);
|
|
5696
|
+
if (inner.trim().length === 0) return 0;
|
|
5697
|
+
if (/(^|[^.])\.\.\./.test(inner)) return null;
|
|
5698
|
+
const parts = splitCallArgs(`${inner})`, 0);
|
|
5699
|
+
if (parts === null) return null;
|
|
5700
|
+
return parts.args.filter((part) => part.trim().length > 0).length;
|
|
5701
|
+
}
|
|
5702
|
+
function blankComments(source) {
|
|
5703
|
+
const out = source.split("");
|
|
5704
|
+
let i = 0;
|
|
5705
|
+
while (i < source.length) {
|
|
5706
|
+
const ch = source[i];
|
|
5707
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
5708
|
+
const quote = ch;
|
|
5709
|
+
i++;
|
|
5710
|
+
while (i < source.length) {
|
|
5711
|
+
if (source[i] === "\\") {
|
|
5712
|
+
i += 2;
|
|
5713
|
+
continue;
|
|
5714
|
+
}
|
|
5715
|
+
if (source[i] === quote) {
|
|
5716
|
+
i++;
|
|
5717
|
+
break;
|
|
5718
|
+
}
|
|
5719
|
+
i++;
|
|
5720
|
+
}
|
|
5721
|
+
continue;
|
|
5722
|
+
}
|
|
5723
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
5724
|
+
while (i < source.length && source[i] !== "\n") {
|
|
5725
|
+
out[i] = " ";
|
|
5726
|
+
i++;
|
|
5727
|
+
}
|
|
5728
|
+
continue;
|
|
5729
|
+
}
|
|
5730
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
5731
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
5732
|
+
out[i] = " ";
|
|
5733
|
+
i++;
|
|
5734
|
+
}
|
|
5735
|
+
if (i < source.length) {
|
|
5736
|
+
out[i] = " ";
|
|
5737
|
+
out[i + 1] = " ";
|
|
5738
|
+
i += 2;
|
|
5739
|
+
}
|
|
5740
|
+
continue;
|
|
5741
|
+
}
|
|
5742
|
+
i++;
|
|
5743
|
+
}
|
|
5744
|
+
return out.join("");
|
|
5745
|
+
}
|
|
5746
|
+
function createApiCallRegex(apis) {
|
|
5747
|
+
return new RegExp(`\\.\\s*\\$(${apis.join("|")})\\s*\\(`, "g");
|
|
5748
|
+
}
|
|
5749
|
+
|
|
5750
|
+
// src/service/recursionValidator.ts
|
|
5751
|
+
var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "trackDependency"];
|
|
5752
|
+
var UNSUPPORTED_API_SITE = {
|
|
5753
|
+
$resolve: "resolve",
|
|
5754
|
+
$postUpdate: "postUpdate",
|
|
5755
|
+
$trackDependency: "trackDependency"
|
|
5756
|
+
};
|
|
5757
|
+
var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
5758
|
+
var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
5759
|
+
function validateRecursion(html, stateTagName = "wcs-state", locale3) {
|
|
5760
|
+
const msgs = getMessages(locale3);
|
|
5761
|
+
const out = [];
|
|
5762
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
5763
|
+
const mounted = /\sbind-component\b/i.test(html.slice(element.tagStart, element.tagEnd));
|
|
5764
|
+
for (const block of element.scriptBlocks) {
|
|
5765
|
+
if (!hasRecursionWildcard(block.content) && block.content.indexOf("$recursion") === -1) continue;
|
|
5766
|
+
const declaration = analyzeRecursionDeclaration(block.content);
|
|
5767
|
+
let spec = null;
|
|
5768
|
+
let undeclared = false;
|
|
5769
|
+
let getterSuffixes = [];
|
|
5770
|
+
if (block.mountPath !== null) {
|
|
5771
|
+
validateVolumeBlock(block.content, block.contentStart, block.mountPath, declaration, msgs, out);
|
|
5772
|
+
} else if (mounted) {
|
|
5773
|
+
validateMountedComponentBlock(block.content, block.contentStart, declaration, msgs, out);
|
|
5774
|
+
} else {
|
|
5775
|
+
spec = validateDeclaration(declaration, block.contentStart, msgs, out);
|
|
5776
|
+
undeclared = declaration === null && hasDefaultExportObject(block.content) && !hasTopLevelSpread(block.content);
|
|
5777
|
+
getterSuffixes = validateRecursiveGetters(block.content, block.contentStart, spec, undeclared, msgs, out);
|
|
5778
|
+
}
|
|
5779
|
+
validateListKeys(block.content, block.contentStart, msgs, out);
|
|
5780
|
+
validateApiCalls(block.content, block.contentStart, spec, getterSuffixes, undeclared, msgs, out);
|
|
5781
|
+
validateAssignments(block.content, block.contentStart, spec, getterSuffixes, msgs, out);
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
return out;
|
|
5785
|
+
}
|
|
5786
|
+
function push(out, code, start, end, message, severity = "error") {
|
|
5787
|
+
out.push({ code, start, end, message, severity });
|
|
5788
|
+
}
|
|
5789
|
+
function validateVolumeBlock(script, offset2, mountPath, declaration, msgs, out) {
|
|
5790
|
+
if (declaration !== null) {
|
|
5791
|
+
push(
|
|
5792
|
+
out,
|
|
5793
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5794
|
+
offset2 + declaration.start,
|
|
5795
|
+
offset2 + declaration.end,
|
|
5796
|
+
msgs.recursionInVolume("$recursion", mountPath)
|
|
5797
|
+
);
|
|
5798
|
+
}
|
|
5799
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5800
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
5801
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
5802
|
+
seen.add(span.name);
|
|
5803
|
+
push(
|
|
5804
|
+
out,
|
|
5805
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5806
|
+
offset2 + span.start,
|
|
5807
|
+
offset2 + span.end,
|
|
5808
|
+
msgs.recursionInVolume(`"${span.name}"`, mountPath)
|
|
5809
|
+
);
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
function validateDeclaration(declaration, offset2, msgs, out) {
|
|
5813
|
+
if (declaration === null) return null;
|
|
5814
|
+
const code = WcsDiagnosticCode.RecursionDeclarationInvalid;
|
|
5815
|
+
if (declaration.notObject) {
|
|
5816
|
+
push(out, code, offset2 + declaration.start, offset2 + declaration.end, msgs.recursionNotObject());
|
|
5817
|
+
return null;
|
|
5818
|
+
}
|
|
5819
|
+
if (declaration.entries.length !== 1) {
|
|
5820
|
+
if (!declaration.objectLiteral) return null;
|
|
5821
|
+
push(
|
|
5822
|
+
out,
|
|
5823
|
+
code,
|
|
5824
|
+
offset2 + declaration.start,
|
|
5825
|
+
offset2 + declaration.end,
|
|
5826
|
+
msgs.recursionAnchorCount(declaration.entries.length)
|
|
5827
|
+
);
|
|
5828
|
+
return null;
|
|
5829
|
+
}
|
|
5830
|
+
const entry = declaration.entries[0];
|
|
5831
|
+
const anchorProblem = checkNodePath(entry.anchor);
|
|
5832
|
+
if (anchorProblem !== null) {
|
|
5833
|
+
push(
|
|
5834
|
+
out,
|
|
5835
|
+
code,
|
|
5836
|
+
offset2 + entry.start,
|
|
5837
|
+
offset2 + entry.end,
|
|
5838
|
+
msgs.recursionNodePathInvalid("anchor", entry.anchor, anchorProblem)
|
|
5839
|
+
);
|
|
5840
|
+
return null;
|
|
5841
|
+
}
|
|
5842
|
+
if (entry.repeat === null) {
|
|
5843
|
+
if (entry.repeatDefinitelyNotString) {
|
|
5844
|
+
push(
|
|
5845
|
+
out,
|
|
5846
|
+
code,
|
|
5847
|
+
offset2 + entry.valueStart,
|
|
5848
|
+
offset2 + entry.valueEnd,
|
|
5849
|
+
msgs.recursionRepeatNotString(entry.anchor)
|
|
5850
|
+
);
|
|
5851
|
+
}
|
|
5852
|
+
return null;
|
|
5853
|
+
}
|
|
5854
|
+
const repeatProblem = checkNodePath(entry.repeat);
|
|
5855
|
+
if (repeatProblem !== null) {
|
|
5856
|
+
push(
|
|
5857
|
+
out,
|
|
5858
|
+
code,
|
|
5859
|
+
offset2 + entry.valueStart,
|
|
5860
|
+
offset2 + entry.valueEnd,
|
|
5861
|
+
msgs.recursionNodePathInvalid("repeat", entry.repeat, repeatProblem)
|
|
5862
|
+
);
|
|
5863
|
+
return null;
|
|
5864
|
+
}
|
|
5865
|
+
return makeRecursionSpec(entry.anchor, entry.repeat);
|
|
5866
|
+
}
|
|
5867
|
+
function validateRecursiveGetters(script, offset2, spec, undeclared, msgs, out) {
|
|
5868
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5869
|
+
const spans = analyzeDeclarationSpans(script).filter((s) => hasRecursionWildcard(s.name)).filter((s) => seen.has(s.name) ? false : (seen.add(s.name), true));
|
|
5870
|
+
if (spans.length === 0) return [];
|
|
5871
|
+
const setterNames = new Set(
|
|
5872
|
+
analyzeCallableBodies(script).filter((c) => c.accessor === "set").map((c) => c.name)
|
|
5873
|
+
);
|
|
5874
|
+
const suffixes = [];
|
|
5875
|
+
const accepted = [];
|
|
5876
|
+
for (const span of spans) {
|
|
5877
|
+
const start = offset2 + span.start;
|
|
5878
|
+
const end = offset2 + span.end;
|
|
5879
|
+
if (spec === null) {
|
|
5880
|
+
if (undeclared) {
|
|
5881
|
+
push(
|
|
5882
|
+
out,
|
|
5883
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
5884
|
+
start,
|
|
5885
|
+
end,
|
|
5886
|
+
msgs.recursionUnsupported(span.name, "undeclared"),
|
|
5887
|
+
"warning"
|
|
5888
|
+
);
|
|
5889
|
+
}
|
|
5890
|
+
continue;
|
|
5891
|
+
}
|
|
5892
|
+
if (span.kind !== "getter") {
|
|
5893
|
+
push(
|
|
5894
|
+
out,
|
|
5895
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5896
|
+
start,
|
|
5897
|
+
end,
|
|
5898
|
+
msgs.recursionGetterInvalid(span.name, "notGetter", spec.recursiveAnchor)
|
|
5899
|
+
);
|
|
5900
|
+
continue;
|
|
5901
|
+
}
|
|
5902
|
+
if (setterNames.has(span.name)) {
|
|
5903
|
+
push(
|
|
5904
|
+
out,
|
|
5905
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5906
|
+
start,
|
|
5907
|
+
end,
|
|
5908
|
+
msgs.recursionGetterInvalid(span.name, "setter", spec.recursiveAnchor)
|
|
5909
|
+
);
|
|
5910
|
+
continue;
|
|
5911
|
+
}
|
|
5912
|
+
const suffix = splitRecursivePath(spec, span.name);
|
|
5913
|
+
if (suffix === null) {
|
|
5914
|
+
push(
|
|
5915
|
+
out,
|
|
5916
|
+
WcsDiagnosticCode.RecursionAnchor,
|
|
5917
|
+
start,
|
|
5918
|
+
end,
|
|
5919
|
+
msgs.recursionAnchorMismatch(span.name, spec.recursiveAnchor)
|
|
5920
|
+
);
|
|
5921
|
+
continue;
|
|
5922
|
+
}
|
|
5923
|
+
if (suffix.length === 0) {
|
|
5924
|
+
push(
|
|
5925
|
+
out,
|
|
5926
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5927
|
+
start,
|
|
5928
|
+
end,
|
|
5929
|
+
msgs.recursionGetterInvalid(span.name, "nodeItself", spec.recursiveAnchor)
|
|
5930
|
+
);
|
|
5931
|
+
continue;
|
|
5932
|
+
}
|
|
5933
|
+
if (structuralWriteTarget(spec, foldSuffixIndexes(suffix)) !== null) {
|
|
5934
|
+
push(
|
|
5935
|
+
out,
|
|
5936
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5937
|
+
start,
|
|
5938
|
+
end,
|
|
5939
|
+
msgs.recursionGetterInvalid(span.name, "structural", spec.recursiveAnchor)
|
|
5940
|
+
);
|
|
5941
|
+
continue;
|
|
5942
|
+
}
|
|
5943
|
+
const collision = accepted.find((other) => sameFamily(spec, other.suffix, suffix));
|
|
5944
|
+
if (collision !== void 0) {
|
|
5945
|
+
push(
|
|
5946
|
+
out,
|
|
5947
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5948
|
+
start,
|
|
5949
|
+
end,
|
|
5950
|
+
msgs.recursionGetterCollision(collision.name, span.name, spec.repeat)
|
|
5951
|
+
);
|
|
5952
|
+
continue;
|
|
5953
|
+
}
|
|
5954
|
+
accepted.push({ name: span.name, suffix, start, end });
|
|
5955
|
+
suffixes.push(suffix);
|
|
5956
|
+
}
|
|
5957
|
+
if (spec !== null && suffixes.length > 0) {
|
|
5958
|
+
const reported = /* @__PURE__ */ new Set();
|
|
5959
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
5960
|
+
if (hasRecursionWildcard(span.name) || reported.has(span.name)) continue;
|
|
5961
|
+
const suffix = concreteExpansionSuffix(spec, suffixes, span.name);
|
|
5962
|
+
if (suffix === null) continue;
|
|
5963
|
+
reported.add(span.name);
|
|
5964
|
+
push(
|
|
5965
|
+
out,
|
|
5966
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5967
|
+
offset2 + span.start,
|
|
5968
|
+
offset2 + span.end,
|
|
5969
|
+
msgs.recursionConcreteCollision(span.name, spec.recursiveAnchor + suffix)
|
|
5970
|
+
);
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
return suffixes;
|
|
5974
|
+
}
|
|
5975
|
+
function validateMountedComponentBlock(script, offset2, declaration, msgs, out) {
|
|
5976
|
+
if (declaration !== null) {
|
|
5977
|
+
push(
|
|
5978
|
+
out,
|
|
5979
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5980
|
+
offset2 + declaration.start,
|
|
5981
|
+
offset2 + declaration.end,
|
|
5982
|
+
msgs.recursionInMountedComponent("$recursion"),
|
|
5983
|
+
"warning"
|
|
5984
|
+
);
|
|
5985
|
+
}
|
|
5986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5987
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
5988
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
5989
|
+
seen.add(span.name);
|
|
5990
|
+
push(
|
|
5991
|
+
out,
|
|
5992
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
5993
|
+
offset2 + span.start,
|
|
5994
|
+
offset2 + span.end,
|
|
5995
|
+
msgs.recursionInMountedComponent(`"${span.name}"`),
|
|
5996
|
+
"warning"
|
|
5997
|
+
);
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
function validateListKeys(script, offset2, msgs, out) {
|
|
6001
|
+
for (const entry of analyzeListKeyEntries(script)) {
|
|
6002
|
+
if (!hasRecursionWildcard(entry.key)) continue;
|
|
6003
|
+
push(
|
|
6004
|
+
out,
|
|
6005
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6006
|
+
offset2 + entry.start,
|
|
6007
|
+
offset2 + entry.end,
|
|
6008
|
+
msgs.recursionUnsupported(entry.key, "listKeys")
|
|
6009
|
+
);
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msgs, out) {
|
|
6013
|
+
const scan = blankComments(script);
|
|
6014
|
+
const regex = createApiCallRegex(RECURSION_APIS);
|
|
6015
|
+
let match;
|
|
6016
|
+
while ((match = regex.exec(scan)) !== null) {
|
|
6017
|
+
const api = `$${match[1]}`;
|
|
6018
|
+
const parsed = splitCallArgs(scan, match.index + match[0].length);
|
|
6019
|
+
if (parsed === null) continue;
|
|
6020
|
+
regex.lastIndex = parsed.end;
|
|
6021
|
+
if (parsed.args.length === 0) continue;
|
|
6022
|
+
const pathArg = parsed.args[0];
|
|
6023
|
+
const path = literalString(pathArg);
|
|
6024
|
+
if (path === null) continue;
|
|
6025
|
+
const leading = pathArg.length - pathArg.trimStart().length;
|
|
6026
|
+
const start = offset2 + parsed.starts[0] + leading;
|
|
6027
|
+
const end = offset2 + parsed.starts[0] + pathArg.trimEnd().length;
|
|
6028
|
+
if (!hasRecursionWildcard(path)) {
|
|
6029
|
+
const writes = api === "$setAll" || api === "$resolve" && parsed.args.length >= 3;
|
|
6030
|
+
if (writes && spec !== null) {
|
|
6031
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6032
|
+
if (owning !== null) {
|
|
6033
|
+
push(
|
|
6034
|
+
out,
|
|
6035
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6036
|
+
start,
|
|
6037
|
+
end,
|
|
6038
|
+
msgs.recursionReadonly(`${api}("${path}")`, spec.recursiveAnchor + owning)
|
|
6039
|
+
);
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
continue;
|
|
6043
|
+
}
|
|
6044
|
+
if (api in UNSUPPORTED_API_SITE) {
|
|
6045
|
+
push(
|
|
6046
|
+
out,
|
|
6047
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6048
|
+
start,
|
|
6049
|
+
end,
|
|
6050
|
+
msgs.recursionUnsupported(path, UNSUPPORTED_API_SITE[api])
|
|
6051
|
+
);
|
|
6052
|
+
continue;
|
|
6053
|
+
}
|
|
6054
|
+
if (spec === null) {
|
|
6055
|
+
if (undeclared) {
|
|
6056
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "undeclared"));
|
|
6057
|
+
}
|
|
6058
|
+
continue;
|
|
6059
|
+
}
|
|
6060
|
+
const suffix = splitRecursivePath(spec, path);
|
|
6061
|
+
if (suffix === null) {
|
|
6062
|
+
push(out, WcsDiagnosticCode.RecursionAnchor, start, end, msgs.recursionAnchorMismatch(path, spec.recursiveAnchor));
|
|
6063
|
+
continue;
|
|
6064
|
+
}
|
|
6065
|
+
if (api === "$getAll") {
|
|
6066
|
+
if (parsed.args.length > 1) {
|
|
6067
|
+
const indexesArg = parsed.args[1];
|
|
6068
|
+
const indexes = literalArrayLength(indexesArg);
|
|
6069
|
+
if (indexes !== null && indexes > 0) {
|
|
6070
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "prefix"));
|
|
6071
|
+
} else if (indexes === null && isDefiniteNonArrayLiteral(indexesArg)) {
|
|
6072
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "notArray"));
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
continue;
|
|
6076
|
+
}
|
|
6077
|
+
validateSetAllForm(path, suffix, parsed.args, spec, getterSuffixes, start, end, msgs, out);
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
function validateSetAllForm(path, suffix, args, spec, getterSuffixes, start, end, msgs, out) {
|
|
6081
|
+
const formCode = WcsDiagnosticCode.RecursionSetAllForm;
|
|
6082
|
+
const indexesArg = args.length > 1 ? args[1].trim() : "";
|
|
6083
|
+
if (args.length < 2 || indexesArg === "undefined" || indexesArg === "null") {
|
|
6084
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "noIndexes"));
|
|
6085
|
+
return;
|
|
6086
|
+
}
|
|
6087
|
+
const indexes = literalArrayLength(args[1]);
|
|
6088
|
+
if (indexes !== null && indexes > 0) {
|
|
6089
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "prefix"));
|
|
6090
|
+
return;
|
|
6091
|
+
}
|
|
6092
|
+
if (args.length > 2 && isFunctionLiteral(args[2])) {
|
|
6093
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "mapper"));
|
|
6094
|
+
return;
|
|
6095
|
+
}
|
|
6096
|
+
if (args.length > 3 && /\bspread\s*:\s*true\b/.test(args[3])) {
|
|
6097
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "spread"));
|
|
6098
|
+
return;
|
|
6099
|
+
}
|
|
6100
|
+
const checkedSuffix = foldSuffixIndexes(suffix);
|
|
6101
|
+
const structural = structuralWriteTarget(spec, checkedSuffix);
|
|
6102
|
+
if (structural !== null) {
|
|
6103
|
+
push(
|
|
6104
|
+
out,
|
|
6105
|
+
WcsDiagnosticCode.RecursionStructuralWrite,
|
|
6106
|
+
start,
|
|
6107
|
+
end,
|
|
6108
|
+
msgs.recursionStructuralWrite(path, structural, spec.repeatList)
|
|
6109
|
+
);
|
|
6110
|
+
return;
|
|
6111
|
+
}
|
|
6112
|
+
const conflicting = conflictingGetterSuffix(spec, getterSuffixes, checkedSuffix);
|
|
6113
|
+
if (conflicting !== null) {
|
|
6114
|
+
push(
|
|
6115
|
+
out,
|
|
6116
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6117
|
+
start,
|
|
6118
|
+
end,
|
|
6119
|
+
msgs.recursionReadonly(`$setAll("${path}")`, spec.recursiveAnchor + conflicting)
|
|
6120
|
+
);
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
function validateAssignments(script, offset2, spec, getterSuffixes, msgs, out) {
|
|
6124
|
+
const masked = maskCommentsAndStrings(script);
|
|
6125
|
+
const found = [];
|
|
6126
|
+
for (const source of [BRACKET_ASSIGNMENT, PRE_BRACKET_INCDEC]) {
|
|
6127
|
+
const regex = new RegExp(source.source, "g");
|
|
6128
|
+
let match;
|
|
6129
|
+
while ((match = regex.exec(masked)) !== null) {
|
|
6130
|
+
const pathStart = match.index + match[0].search(/["']/) + 1;
|
|
6131
|
+
found.push({ pathStart, length: match[1].length });
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
found.sort((a, b) => a.pathStart - b.pathStart);
|
|
6135
|
+
let last = -1;
|
|
6136
|
+
for (const { pathStart, length } of found) {
|
|
6137
|
+
if (pathStart === last) continue;
|
|
6138
|
+
last = pathStart;
|
|
6139
|
+
const path = script.slice(pathStart, pathStart + length);
|
|
6140
|
+
const start = offset2 + pathStart;
|
|
6141
|
+
const end = start + path.length;
|
|
6142
|
+
if (hasRecursionWildcard(path)) {
|
|
6143
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "assignment"));
|
|
6144
|
+
continue;
|
|
6145
|
+
}
|
|
6146
|
+
if (spec === null) continue;
|
|
6147
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6148
|
+
if (owning !== null) {
|
|
6149
|
+
push(
|
|
6150
|
+
out,
|
|
6151
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6152
|
+
start,
|
|
6153
|
+
end,
|
|
6154
|
+
msgs.recursionReadonly(`this["${path}"] = \u2026`, spec.recursiveAnchor + owning)
|
|
6155
|
+
);
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
function isDefiniteNonArrayLiteral(arg) {
|
|
6160
|
+
const trimmed = arg.trim();
|
|
6161
|
+
return trimmed === "null" || /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false)$/.test(trimmed) || trimmed.startsWith("{");
|
|
6162
|
+
}
|
|
6163
|
+
function isFunctionLiteral(arg) {
|
|
6164
|
+
const trimmed = arg.trim();
|
|
6165
|
+
if (trimmed.length === 0) return false;
|
|
6166
|
+
return /^(?:async\s+)?function\b/.test(trimmed) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(trimmed) || /^(?:async\s+)?[$\w]+\s*=>/.test(trimmed);
|
|
6167
|
+
}
|
|
6168
|
+
|
|
5118
6169
|
// src/service/namedStateValidator.ts
|
|
5119
6170
|
function findStateSelector(expr, embedded = false) {
|
|
5120
6171
|
const colon = embedded ? -1 : expr.indexOf(":");
|
|
@@ -11039,7 +12090,7 @@ function enterFunction(fn, outer, thisIsState) {
|
|
|
11039
12090
|
function isThisRoot(node, scope) {
|
|
11040
12091
|
return node.type === "ThisExpression" && scope.thisIsState || node.type === "Identifier" && scope.aliases.has(node.name);
|
|
11041
12092
|
}
|
|
11042
|
-
function
|
|
12093
|
+
function literalString2(node) {
|
|
11043
12094
|
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
11044
12095
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
11045
12096
|
return node.quasis[0].value.cooked ?? null;
|
|
@@ -11054,7 +12105,7 @@ function segmentOf(member) {
|
|
|
11054
12105
|
if (property.type === "Literal" && typeof property.value === "number") {
|
|
11055
12106
|
return { text: String(property.value), dynamic: null };
|
|
11056
12107
|
}
|
|
11057
|
-
const text =
|
|
12108
|
+
const text = literalString2(property);
|
|
11058
12109
|
if (text !== null) return { text, dynamic: null };
|
|
11059
12110
|
return property.type === "PrivateIdentifier" ? { text: null, dynamic: null } : { text: null, dynamic: property };
|
|
11060
12111
|
}
|
|
@@ -11150,7 +12201,7 @@ function visitCall(node, scope, out) {
|
|
|
11150
12201
|
if (api === UNTRACK_API) return;
|
|
11151
12202
|
if (PATH_ARG_APIS.has(api)) {
|
|
11152
12203
|
const first = node.arguments[0];
|
|
11153
|
-
const path = first !== void 0 && first.type !== "SpreadElement" ?
|
|
12204
|
+
const path = first !== void 0 && first.type !== "SpreadElement" ? literalString2(first) : null;
|
|
11154
12205
|
if (path !== null && path.length > 0 && !path.startsWith("$")) {
|
|
11155
12206
|
out.push({
|
|
11156
12207
|
path,
|
|
@@ -11216,7 +12267,7 @@ function visitDestructure(pattern, prefix, scope, out) {
|
|
|
11216
12267
|
if (property.type === "RestElement") continue;
|
|
11217
12268
|
let key = null;
|
|
11218
12269
|
if (!property.computed && property.key.type === "Identifier") key = property.key.name;
|
|
11219
|
-
else key =
|
|
12270
|
+
else key = literalString2(property.key);
|
|
11220
12271
|
let value = property.value;
|
|
11221
12272
|
if (value.type === "AssignmentPattern") {
|
|
11222
12273
|
visit(value.right, scope, out);
|
|
@@ -11249,6 +12300,10 @@ for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
|
|
|
11249
12300
|
tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
|
|
11250
12301
|
}
|
|
11251
12302
|
Object.freeze(tmpIndexByIndexName2);
|
|
12303
|
+
var RECURSION_WILDCARD2 = "**";
|
|
12304
|
+
function raiseError2(message) {
|
|
12305
|
+
throw new Error(`[@wcstack/state] ${message}`);
|
|
12306
|
+
}
|
|
11252
12307
|
var _cache = /* @__PURE__ */ new Map();
|
|
11253
12308
|
function clearPathInfoCacheForTooling() {
|
|
11254
12309
|
_cache.clear();
|
|
@@ -11259,6 +12314,9 @@ function getPathInfo(path) {
|
|
|
11259
12314
|
if (typeof pathInfo !== "undefined") {
|
|
11260
12315
|
return pathInfo;
|
|
11261
12316
|
}
|
|
12317
|
+
if (path.indexOf(RECURSION_WILDCARD2) !== -1) {
|
|
12318
|
+
raiseError2(`[wcs/recursion-unsupported] "${path}" uses "${RECURSION_WILDCARD2}", which is not accepted here. It is only meaningful in a $recursion declaration, in a recursive getter key, and in the path argument of $getAll / $setAll \u2014 and only when the state declares a $recursion anchor.`);
|
|
12319
|
+
}
|
|
11262
12320
|
pathInfo = Object.freeze(new PathInfo(path));
|
|
11263
12321
|
_cache.set(path, pathInfo);
|
|
11264
12322
|
return pathInfo;
|
|
@@ -11382,9 +12440,6 @@ function didYouMean(input, candidates) {
|
|
|
11382
12440
|
return best !== null ? ` Did you mean "${best}"?` : "";
|
|
11383
12441
|
}
|
|
11384
12442
|
var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
11385
|
-
function raiseError2(message) {
|
|
11386
|
-
throw new Error(`[@wcstack/state] ${message}`);
|
|
11387
|
-
}
|
|
11388
12443
|
var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
|
|
11389
12444
|
"if",
|
|
11390
12445
|
"elseif",
|
|
@@ -12388,71 +13443,6 @@ function buildReferenceIndex(html, options = {}) {
|
|
|
12388
13443
|
// src/service/semanticValidator.ts
|
|
12389
13444
|
var STATE_UPDATED_CALLBACK = "$updatedCallback";
|
|
12390
13445
|
var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
|
|
12391
|
-
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
12392
|
-
function splitCallArgs(source, open) {
|
|
12393
|
-
const args = [];
|
|
12394
|
-
const starts = [];
|
|
12395
|
-
let depth = 0;
|
|
12396
|
-
let argStart = open;
|
|
12397
|
-
let i = open;
|
|
12398
|
-
while (i < source.length) {
|
|
12399
|
-
const ch = source[i];
|
|
12400
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
12401
|
-
const quote = ch;
|
|
12402
|
-
i++;
|
|
12403
|
-
while (i < source.length) {
|
|
12404
|
-
if (source[i] === "\\") {
|
|
12405
|
-
i += 2;
|
|
12406
|
-
continue;
|
|
12407
|
-
}
|
|
12408
|
-
if (source[i] === quote) {
|
|
12409
|
-
i++;
|
|
12410
|
-
break;
|
|
12411
|
-
}
|
|
12412
|
-
i++;
|
|
12413
|
-
}
|
|
12414
|
-
continue;
|
|
12415
|
-
}
|
|
12416
|
-
if (ch === "(" || ch === "[" || ch === "{") {
|
|
12417
|
-
depth++;
|
|
12418
|
-
i++;
|
|
12419
|
-
continue;
|
|
12420
|
-
}
|
|
12421
|
-
if (ch === ")" && depth === 0) {
|
|
12422
|
-
args.push(source.slice(argStart, i));
|
|
12423
|
-
starts.push(argStart);
|
|
12424
|
-
return { args, starts, end: i + 1 };
|
|
12425
|
-
}
|
|
12426
|
-
if (ch === ")" || ch === "]" || ch === "}") {
|
|
12427
|
-
depth--;
|
|
12428
|
-
i++;
|
|
12429
|
-
continue;
|
|
12430
|
-
}
|
|
12431
|
-
if (ch === "," && depth === 0) {
|
|
12432
|
-
args.push(source.slice(argStart, i));
|
|
12433
|
-
starts.push(argStart);
|
|
12434
|
-
argStart = i + 1;
|
|
12435
|
-
i++;
|
|
12436
|
-
continue;
|
|
12437
|
-
}
|
|
12438
|
-
i++;
|
|
12439
|
-
}
|
|
12440
|
-
return null;
|
|
12441
|
-
}
|
|
12442
|
-
function literalString2(arg) {
|
|
12443
|
-
const match = STRING_LITERAL.exec(arg);
|
|
12444
|
-
return match === null ? null : match[2];
|
|
12445
|
-
}
|
|
12446
|
-
function literalArrayLength(arg) {
|
|
12447
|
-
const trimmed = arg.trim();
|
|
12448
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
12449
|
-
const inner = trimmed.slice(1, -1);
|
|
12450
|
-
if (inner.trim().length === 0) return 0;
|
|
12451
|
-
if (/(^|[^.])\.\.\./.test(inner)) return null;
|
|
12452
|
-
const parts = splitCallArgs(`${inner})`, 0);
|
|
12453
|
-
if (parts === null) return null;
|
|
12454
|
-
return parts.args.filter((part) => part.trim().length > 0).length;
|
|
12455
|
-
}
|
|
12456
13446
|
function validateIndexArity(script, scriptStart, locale3) {
|
|
12457
13447
|
const msgs = getMessages(locale3);
|
|
12458
13448
|
const out = [];
|
|
@@ -12464,8 +13454,9 @@ function validateIndexArity(script, scriptStart, locale3) {
|
|
|
12464
13454
|
if (parsed === null) continue;
|
|
12465
13455
|
API_CALL.lastIndex = parsed.end;
|
|
12466
13456
|
if (parsed.args.length < 2) continue;
|
|
12467
|
-
const path =
|
|
13457
|
+
const path = literalString(parsed.args[0]);
|
|
12468
13458
|
if (path === null) continue;
|
|
13459
|
+
if (hasRecursionWildcard(path)) continue;
|
|
12469
13460
|
const actual = literalArrayLength(parsed.args[1]);
|
|
12470
13461
|
if (actual === null) continue;
|
|
12471
13462
|
const wildcardCount = countWildcardSegments(path);
|
|
@@ -12572,7 +13563,7 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
|
|
|
12572
13563
|
}
|
|
12573
13564
|
var TWO_WAY_PROPS = /* @__PURE__ */ new Set(["value", "checked"]);
|
|
12574
13565
|
var BRACKET_WRITE = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
12575
|
-
var
|
|
13566
|
+
var PRE_BRACKET_INCDEC2 = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
12576
13567
|
function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
12577
13568
|
const roots = /* @__PURE__ */ new Set();
|
|
12578
13569
|
const addPrefixes = (path, inclusive) => {
|
|
@@ -12608,7 +13599,7 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12608
13599
|
for (const block of blocks) {
|
|
12609
13600
|
if (block.mountPath !== null) addPrefixes(block.mountPath, true);
|
|
12610
13601
|
const scan = blankComments(block.content);
|
|
12611
|
-
for (const regex of [BRACKET_WRITE,
|
|
13602
|
+
for (const regex of [BRACKET_WRITE, PRE_BRACKET_INCDEC2]) {
|
|
12612
13603
|
regex.lastIndex = 0;
|
|
12613
13604
|
let match;
|
|
12614
13605
|
while ((match = regex.exec(scan)) !== null) addPrefixes(match[1], false);
|
|
@@ -12621,57 +13612,13 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12621
13612
|
const parsed = splitCallArgs(scan, call.index + call[0].length);
|
|
12622
13613
|
if (parsed === null) continue;
|
|
12623
13614
|
API_CALL.lastIndex = parsed.end;
|
|
12624
|
-
const path = parsed.args.length > 0 ?
|
|
13615
|
+
const path = parsed.args.length > 0 ? literalString(parsed.args[0]) : null;
|
|
12625
13616
|
if (path === null) continue;
|
|
12626
13617
|
if (api === "setAll" || parsed.args.length >= 3) addPrefixes(path, false);
|
|
12627
13618
|
}
|
|
12628
13619
|
}
|
|
12629
13620
|
return roots;
|
|
12630
13621
|
}
|
|
12631
|
-
function blankComments(source) {
|
|
12632
|
-
const out = source.split("");
|
|
12633
|
-
let i = 0;
|
|
12634
|
-
while (i < source.length) {
|
|
12635
|
-
const ch = source[i];
|
|
12636
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
12637
|
-
const quote = ch;
|
|
12638
|
-
i++;
|
|
12639
|
-
while (i < source.length) {
|
|
12640
|
-
if (source[i] === "\\") {
|
|
12641
|
-
i += 2;
|
|
12642
|
-
continue;
|
|
12643
|
-
}
|
|
12644
|
-
if (source[i] === quote) {
|
|
12645
|
-
i++;
|
|
12646
|
-
break;
|
|
12647
|
-
}
|
|
12648
|
-
i++;
|
|
12649
|
-
}
|
|
12650
|
-
continue;
|
|
12651
|
-
}
|
|
12652
|
-
if (ch === "/" && source[i + 1] === "/") {
|
|
12653
|
-
while (i < source.length && source[i] !== "\n") {
|
|
12654
|
-
out[i] = " ";
|
|
12655
|
-
i++;
|
|
12656
|
-
}
|
|
12657
|
-
continue;
|
|
12658
|
-
}
|
|
12659
|
-
if (ch === "/" && source[i + 1] === "*") {
|
|
12660
|
-
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
12661
|
-
out[i] = " ";
|
|
12662
|
-
i++;
|
|
12663
|
-
}
|
|
12664
|
-
if (i < source.length) {
|
|
12665
|
-
out[i] = " ";
|
|
12666
|
-
out[i + 1] = " ";
|
|
12667
|
-
i += 2;
|
|
12668
|
-
}
|
|
12669
|
-
continue;
|
|
12670
|
-
}
|
|
12671
|
-
i++;
|
|
12672
|
-
}
|
|
12673
|
-
return out.join("");
|
|
12674
|
-
}
|
|
12675
13622
|
var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
|
|
12676
13623
|
function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
|
|
12677
13624
|
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
@@ -13139,6 +14086,7 @@ function validateDocument(text, options = {}) {
|
|
|
13139
14086
|
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
13140
14087
|
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
13141
14088
|
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
14089
|
+
out.push(...validateRecursion(text, stateTagName, locale3));
|
|
13142
14090
|
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
13143
14091
|
out.push(...validateMountAttributes(text, stateTagName, locale3));
|
|
13144
14092
|
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|