@wcstack/lint 2.2.0 → 2.4.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 +1674 -224
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -147,6 +147,44 @@ var WcsDiagnosticCode = {
|
|
|
147
147
|
// `$watch` のキーが状態定義に存在しない。バインディング側と違い黙って発火しない
|
|
148
148
|
// だけなので気づけない。severity は binding-path-missing に揃える(warning)。
|
|
149
149
|
WatchPathMissing: "wcs/watch-path-missing",
|
|
150
|
+
// --- <wcs-state> script: $scan declaration ---
|
|
151
|
+
// ランタイム(scan/processScanDeclaration.ts)が raiseError で落とす宣言の形。出力名・source の本数・
|
|
152
|
+
// initial / fold の欠落・from / resetOn のパスの形・未宣言トークン・自出力の読み。
|
|
153
|
+
ScanDeclarationInvalid: "wcs/scan-declaration-invalid",
|
|
154
|
+
// from / resetOn が getter(またはその配下)。畳むと出来事ではなく再評価の回数を数える。ランタイムも raise。
|
|
155
|
+
ScanSourceComputed: "wcs/scan-source-computed",
|
|
156
|
+
// from / resetOn のパスが状態定義に存在しない。黙って一度も畳まれない。severity は watch-path-missing に揃える。
|
|
157
|
+
ScanPathMissing: "wcs/scan-path-missing",
|
|
158
|
+
// --- <wcs-state> script: $recursion declaration / `**` paths ---
|
|
159
|
+
// ランタイムと同じ code 語彙(@wcstack/state src/recursion/ が正本。
|
|
160
|
+
// docs/state-recursive-path-impl-plan.md §7)。静的に出すのは**パス文字列と宣言だけで
|
|
161
|
+
// 決まる**ものに限る。データを見ないと決まらない wcs/recursion-shared-list /
|
|
162
|
+
// wcs/recursion-cycle / wcs/recursion-depth-exceeded、および評価時の呼び出し文脈に
|
|
163
|
+
// 依存する wcs/recursion-context は runtime 専用(静的側は出さない)。
|
|
164
|
+
//
|
|
165
|
+
// `**` を解釈しない場所へ `**` が渡った(data-wcs / mustache / $watch キー / $listKeys
|
|
166
|
+
// キー / $resolve / $postUpdate / $trackDependency / 代入)、または `$recursion` 宣言が
|
|
167
|
+
// 無いのに `**` を使った。runtime は PathInfo の不変条件として raiseError するか
|
|
168
|
+
//(API 経由)、getter を黙って無視する(宣言なしの `**` getter)。
|
|
169
|
+
RecursionUnsupported: "wcs/recursion-unsupported",
|
|
170
|
+
// 宣言済みアンカーと合致しない `**`(綴り違い・2 つ目の `**`)、または `**` の後ろが
|
|
171
|
+
// 整形されていない(空セグメント・`**` 直後の素の `*`)。
|
|
172
|
+
RecursionAnchor: "wcs/recursion-anchor",
|
|
173
|
+
// `$getAll` の添字の形が `**` に対して定義できない(非空の接頭辞 / 配列でない値)。
|
|
174
|
+
RecursionGetAllForm: "wcs/recursion-getall-form",
|
|
175
|
+
// `$setAll` の添字・値の形が `**` に対して定義できない
|
|
176
|
+
//(非空の接頭辞 / 添字省略 / mapper / spread)。
|
|
177
|
+
RecursionSetAllForm: "wcs/recursion-setall-form",
|
|
178
|
+
// ノード自身・子リスト・子ノード・子リストの length・多段の反復サブパスなら子リストへ
|
|
179
|
+
// 至る途中のオブジェクトへの一括書き込み(確定済みの子アドレスを壊す)。
|
|
180
|
+
RecursionStructuralWrite: "wcs/recursion-structural-write",
|
|
181
|
+
// 再帰 getter(およびその派生値の中)への書き込み。setter は初版では持てない。
|
|
182
|
+
RecursionReadonly: "wcs/recursion-readonly",
|
|
183
|
+
// `$recursion` 宣言そのもの、または `**` getter の宣言の形が不正(アンカー / 反復
|
|
184
|
+
// サブパスの形・複数宣言・setter・getter でない・ノード自身・構造を名指す接尾辞・
|
|
185
|
+
// 展開形と同名の具体 getter・ボリューム / マウント下での宣言)。
|
|
186
|
+
//(ランタイムは初期化時に raiseError)。wcs/watch-declaration-invalid の再帰版。
|
|
187
|
+
RecursionDeclarationInvalid: "wcs/recursion-declaration-invalid",
|
|
150
188
|
TypeAnnotation: "wcs/type-annotation",
|
|
151
189
|
TemplateSyntax: "wcs/template-syntax",
|
|
152
190
|
// --- <wcs-state> script: array reactivity hazards ---
|
|
@@ -843,6 +881,8 @@ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
|
843
881
|
var STATE_ON_NAME = "$on";
|
|
844
882
|
var STATE_STREAMS_NAME = "$streams";
|
|
845
883
|
var STATE_WATCH_NAME = "$watch";
|
|
884
|
+
var STATE_SCAN_NAME = "$scan";
|
|
885
|
+
var STATE_RECURSION_NAME = "$recursion";
|
|
846
886
|
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
847
887
|
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
848
888
|
var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
@@ -904,7 +944,9 @@ function getWcsManifest() {
|
|
|
904
944
|
STATE_ON_NAME,
|
|
905
945
|
STATE_STREAMS_NAME,
|
|
906
946
|
STATE_WATCH_NAME,
|
|
947
|
+
STATE_SCAN_NAME,
|
|
907
948
|
STATE_LIST_KEYS_NAME,
|
|
949
|
+
STATE_RECURSION_NAME,
|
|
908
950
|
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
909
951
|
STATE_STREAM_ERROR_NAMESPACE_NAME
|
|
910
952
|
]
|
|
@@ -926,12 +968,174 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
|
|
|
926
968
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
927
969
|
}));
|
|
928
970
|
|
|
971
|
+
// src/service/recursionPaths.ts
|
|
972
|
+
var RECURSION_WILDCARD = "**";
|
|
973
|
+
var RECURSION_KEY = "$recursion";
|
|
974
|
+
function hasRecursionWildcard(path) {
|
|
975
|
+
return path.indexOf(RECURSION_WILDCARD) !== -1;
|
|
976
|
+
}
|
|
977
|
+
function checkNodePath(path) {
|
|
978
|
+
if (typeof path !== "string" || path.length === 0) return "empty";
|
|
979
|
+
const segments = path.split(".");
|
|
980
|
+
if (segments.some((segment) => segment.length === 0)) return "emptySegment";
|
|
981
|
+
if (segments.length < 2 || segments[segments.length - 1] !== "*") return "notElement";
|
|
982
|
+
if (segments[0].startsWith("$")) return "reservedRoot";
|
|
983
|
+
if (path.indexOf("#") !== -1) return "reservedMount";
|
|
984
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
985
|
+
if (segments[i] === "*") return "midWildcard";
|
|
986
|
+
if (segments[i] === RECURSION_WILDCARD) return "nestedRecursion";
|
|
987
|
+
if (!isNaN(Number(segments[i]))) return "indexSegment";
|
|
988
|
+
}
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
function makeRecursionSpec(anchor, repeat) {
|
|
992
|
+
return Object.freeze({
|
|
993
|
+
anchor,
|
|
994
|
+
repeat,
|
|
995
|
+
recursiveAnchor: anchor.slice(0, anchor.lastIndexOf(".")) + "." + RECURSION_WILDCARD,
|
|
996
|
+
anchorList: anchor.slice(0, anchor.lastIndexOf(".")),
|
|
997
|
+
repeatList: repeat.slice(0, repeat.lastIndexOf("."))
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
function splitRecursivePath(spec, path) {
|
|
1001
|
+
if (path === spec.recursiveAnchor) return "";
|
|
1002
|
+
if (!path.startsWith(spec.recursiveAnchor + ".")) return null;
|
|
1003
|
+
const suffix = path.slice(spec.recursiveAnchor.length);
|
|
1004
|
+
if (hasRecursionWildcard(suffix)) return null;
|
|
1005
|
+
const segments = suffix.slice(1).split(".");
|
|
1006
|
+
if (segments[0] === "*" || segments.some((segment) => segment.length === 0)) return null;
|
|
1007
|
+
return suffix;
|
|
1008
|
+
}
|
|
1009
|
+
function foldSuffixIndexes(suffix) {
|
|
1010
|
+
return suffix.length === 0 ? suffix : "." + indexSegmentsToWildcard(suffix.slice(1));
|
|
1011
|
+
}
|
|
1012
|
+
function foldRecursion(spec, path) {
|
|
1013
|
+
if (!path.startsWith(spec.anchor)) return null;
|
|
1014
|
+
const unit3 = "." + spec.repeat;
|
|
1015
|
+
let cursor = spec.anchor.length;
|
|
1016
|
+
let depth = 0;
|
|
1017
|
+
while (path.startsWith(unit3, cursor)) {
|
|
1018
|
+
cursor += unit3.length;
|
|
1019
|
+
depth++;
|
|
1020
|
+
}
|
|
1021
|
+
if (cursor !== path.length && path.charCodeAt(cursor) !== 46) return null;
|
|
1022
|
+
return { depth, rest: path.slice(cursor) };
|
|
1023
|
+
}
|
|
1024
|
+
function matchesRecursion(specs, path, has) {
|
|
1025
|
+
for (const spec of specs) {
|
|
1026
|
+
const folded = foldRecursion(spec, path);
|
|
1027
|
+
if (folded === null) continue;
|
|
1028
|
+
const unit3 = "." + spec.repeat;
|
|
1029
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1030
|
+
const rest = unit3.repeat(folded.depth - depth) + folded.rest;
|
|
1031
|
+
if (has(spec.anchor + rest)) return true;
|
|
1032
|
+
if (has(spec.recursiveAnchor + rest)) return true;
|
|
1033
|
+
for (let dot = rest.lastIndexOf("."); dot > 0; dot = rest.lastIndexOf(".", dot - 1)) {
|
|
1034
|
+
if (has(spec.recursiveAnchor + rest.slice(0, dot))) return true;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
function owningGetterSuffix(spec, getterSuffixes, path) {
|
|
1041
|
+
const pattern = indexSegmentsToWildcard(path);
|
|
1042
|
+
const folded = foldRecursion(spec, pattern);
|
|
1043
|
+
if (folded === null) return null;
|
|
1044
|
+
const unit3 = "." + spec.repeat;
|
|
1045
|
+
for (const suffix of getterSuffixes) {
|
|
1046
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1047
|
+
const expansion = spec.anchor + unit3.repeat(depth) + suffix;
|
|
1048
|
+
if (pattern === expansion || pattern.startsWith(expansion + ".")) return suffix;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
function indexSegmentsToWildcard(path) {
|
|
1054
|
+
return path.split(".").map((segment) => segment !== "*" && !Number.isNaN(Number(segment)) ? "*" : segment).join(".");
|
|
1055
|
+
}
|
|
1056
|
+
function concreteExpansionSuffix(spec, getterSuffixes, key) {
|
|
1057
|
+
const folded = foldRecursion(spec, key);
|
|
1058
|
+
if (folded === null) return null;
|
|
1059
|
+
const unit3 = "." + spec.repeat;
|
|
1060
|
+
for (const suffix of getterSuffixes) {
|
|
1061
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1062
|
+
if (key === spec.anchor + unit3.repeat(depth) + suffix) return suffix;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
1067
|
+
function collectRecursionSpecs(candidates) {
|
|
1068
|
+
const out = [];
|
|
1069
|
+
for (const candidate of candidates) {
|
|
1070
|
+
if (candidate.kind !== "recursionAnchor" || typeof candidate.repeat !== "string") continue;
|
|
1071
|
+
if (!candidate.path.endsWith("." + RECURSION_WILDCARD)) continue;
|
|
1072
|
+
const anchor = candidate.path.slice(0, candidate.path.length - RECURSION_WILDCARD.length) + "*";
|
|
1073
|
+
if (out.some((spec) => spec.anchor === anchor && spec.repeat === candidate.repeat)) continue;
|
|
1074
|
+
out.push(makeRecursionSpec(anchor, candidate.repeat));
|
|
1075
|
+
}
|
|
1076
|
+
return out;
|
|
1077
|
+
}
|
|
1078
|
+
function impliedStructurePaths(spec) {
|
|
1079
|
+
const out = [
|
|
1080
|
+
{ path: spec.anchorList, kind: "data", typeHint: "array" },
|
|
1081
|
+
{ path: spec.anchor, kind: "list" },
|
|
1082
|
+
{ path: `${spec.anchorList}.length`, kind: "data", typeHint: "number" }
|
|
1083
|
+
];
|
|
1084
|
+
const repeatSegments = spec.repeatList.split(".");
|
|
1085
|
+
for (let i = 1; i < repeatSegments.length; i++) {
|
|
1086
|
+
out.push({ path: `${spec.anchor}.${repeatSegments.slice(0, i).join(".")}`, kind: "data" });
|
|
1087
|
+
}
|
|
1088
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}`, kind: "data", typeHint: "array" });
|
|
1089
|
+
out.push({ path: `${spec.anchor}.${spec.repeat}`, kind: "list" });
|
|
1090
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}.length`, kind: "data", typeHint: "number" });
|
|
1091
|
+
return out;
|
|
1092
|
+
}
|
|
1093
|
+
function structuralWriteTarget(spec, suffix) {
|
|
1094
|
+
const unit3 = "." + spec.repeat;
|
|
1095
|
+
let rest = suffix;
|
|
1096
|
+
while (rest.startsWith(unit3)) rest = rest.slice(unit3.length);
|
|
1097
|
+
if (rest.length === 0) return "node";
|
|
1098
|
+
if (rest === "." + spec.repeatList + ".length") return "length";
|
|
1099
|
+
const segments = spec.repeatList.split(".");
|
|
1100
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
1101
|
+
if (rest === "." + segments.slice(0, i).join(".")) return i === segments.length ? "list" : "branch";
|
|
1102
|
+
}
|
|
1103
|
+
return null;
|
|
1104
|
+
}
|
|
1105
|
+
function sameFamily(spec, a, b) {
|
|
1106
|
+
const unit3 = "." + spec.repeat;
|
|
1107
|
+
const shorter = a.length <= b.length ? a : b;
|
|
1108
|
+
const longer = a.length <= b.length ? b : a;
|
|
1109
|
+
if (!longer.endsWith(shorter)) return false;
|
|
1110
|
+
const gap = longer.slice(0, longer.length - shorter.length);
|
|
1111
|
+
if (gap.length === 0) return true;
|
|
1112
|
+
if (gap.length % unit3.length !== 0) return false;
|
|
1113
|
+
for (let cursor = 0; cursor < gap.length; cursor += unit3.length) {
|
|
1114
|
+
if (!gap.startsWith(unit3, cursor)) return false;
|
|
1115
|
+
}
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
function conflictingGetterSuffix(spec, getterSuffixes, suffix) {
|
|
1119
|
+
for (const declared of getterSuffixes) {
|
|
1120
|
+
if (coversSuffix(spec, declared, suffix)) return declared;
|
|
1121
|
+
}
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
function coversSuffix(spec, familySuffix, suffix) {
|
|
1125
|
+
for (let end = suffix.length; end > 0; end = suffix.lastIndexOf(".", end - 1)) {
|
|
1126
|
+
if (sameFamily(spec, familySuffix, suffix.slice(0, end))) return true;
|
|
1127
|
+
}
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
929
1131
|
// src/service/stateAnalyzer.ts
|
|
930
1132
|
var RESERVED_STREAMS_KEY = "$streams";
|
|
931
1133
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
932
1134
|
var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
|
|
933
1135
|
var RESERVED_LIST_KEYS_KEY = "$listKeys";
|
|
934
1136
|
var RESERVED_WATCH_KEY = "$watch";
|
|
1137
|
+
var RESERVED_SCAN_KEY = "$scan";
|
|
1138
|
+
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
935
1139
|
function analyzeStatePaths(scriptContent) {
|
|
936
1140
|
const objectContent = extractDefaultExportObject(scriptContent);
|
|
937
1141
|
if (!objectContent) return [];
|
|
@@ -939,51 +1143,290 @@ function analyzeStatePaths(scriptContent) {
|
|
|
939
1143
|
const topLevelProps = parseTopLevelProperties(objectContent);
|
|
940
1144
|
const pendingStreamValues = [];
|
|
941
1145
|
const pendingListKeys = [];
|
|
1146
|
+
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
1147
|
+
const effectiveDescriptors = effectiveTopLevelDescriptors(topLevelProps);
|
|
942
1148
|
for (const prop of topLevelProps) {
|
|
943
1149
|
if (prop.name.startsWith("$")) {
|
|
944
1150
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
945
1151
|
continue;
|
|
946
1152
|
}
|
|
1153
|
+
const effective = effectiveDescriptors.get(prop.name);
|
|
1154
|
+
if (prop.kind === "getter" !== (effective !== "data")) {
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
947
1157
|
if (prop.kind === "method") {
|
|
948
1158
|
paths.push({ path: prop.name, kind: "method" });
|
|
949
1159
|
continue;
|
|
950
1160
|
}
|
|
951
1161
|
if (prop.kind === "getter") {
|
|
952
|
-
|
|
953
|
-
|
|
1162
|
+
const { get, set } = effective;
|
|
1163
|
+
const writeOnly = set && !get;
|
|
1164
|
+
const declared = paths.find((p) => p.path === prop.name);
|
|
1165
|
+
if (declared === void 0) {
|
|
1166
|
+
const kind = hasRecursionWildcard(prop.name) ? "recursive" : "computed";
|
|
1167
|
+
paths.push(writeOnly ? { path: prop.name, kind, writeOnly: true } : { path: prop.name, kind });
|
|
1168
|
+
} else if (writeOnly) {
|
|
1169
|
+
declared.writeOnly = true;
|
|
954
1170
|
}
|
|
955
1171
|
continue;
|
|
956
1172
|
}
|
|
957
1173
|
pushDataPropertyPaths(prop, paths);
|
|
958
1174
|
}
|
|
959
1175
|
for (const streamValue of pendingStreamValues) {
|
|
960
|
-
if (paths.some((p) => p.path === streamValue.name)) continue;
|
|
1176
|
+
if (paths.some((p) => p.path === streamValue.name && p.kind !== "eventToken")) continue;
|
|
961
1177
|
pushDataPropertyPaths(streamValue, paths);
|
|
962
1178
|
}
|
|
963
1179
|
for (const listKeyEntry of pendingListKeys) {
|
|
964
1180
|
pushListKeyPaths(listKeyEntry, paths);
|
|
965
1181
|
}
|
|
1182
|
+
if (recursionSpec !== null) {
|
|
1183
|
+
if (!paths.some((p) => p.path === recursionSpec.recursiveAnchor && p.kind === "recursionAnchor")) {
|
|
1184
|
+
paths.push({ path: recursionSpec.recursiveAnchor, kind: "recursionAnchor", repeat: recursionSpec.repeat });
|
|
1185
|
+
}
|
|
1186
|
+
for (const implied of impliedStructurePaths(recursionSpec)) {
|
|
1187
|
+
if (paths.some((p) => p.path === implied.path)) continue;
|
|
1188
|
+
paths.push({ path: implied.path, kind: implied.kind, typeHint: implied.typeHint });
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
966
1191
|
collectRowShapesFromAssignments(scriptContent, paths);
|
|
967
1192
|
return paths;
|
|
968
1193
|
}
|
|
1194
|
+
function specFromRecursionValue(prop) {
|
|
1195
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value)) return null;
|
|
1196
|
+
const entries = parseTopLevelProperties(extractObjectContent(prop.value)).filter((e) => e.kind === "data");
|
|
1197
|
+
if (entries.length !== 1) return null;
|
|
1198
|
+
const anchor = entries[0].name;
|
|
1199
|
+
const repeat = extractStringLiteralValue(entries[0].value);
|
|
1200
|
+
if (repeat === null) return null;
|
|
1201
|
+
if (checkNodePath(anchor) !== null || checkNodePath(repeat) !== null) return null;
|
|
1202
|
+
return makeRecursionSpec(anchor, repeat);
|
|
1203
|
+
}
|
|
1204
|
+
function analyzeRecursionDeclaration(scriptContent) {
|
|
1205
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1206
|
+
if (!root) return null;
|
|
1207
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_RECURSION_KEY);
|
|
1208
|
+
if (!prop || prop.nameStart === void 0 || prop.nameEnd === void 0) return null;
|
|
1209
|
+
const span = { start: root.start + prop.nameStart, end: root.start + prop.nameEnd };
|
|
1210
|
+
if (prop.kind === "method") {
|
|
1211
|
+
return { ...span, notObject: true, objectLiteral: false, entries: [], spec: null };
|
|
1212
|
+
}
|
|
1213
|
+
if (prop.kind !== "data" || !prop.value || prop.valueStart === void 0) {
|
|
1214
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1215
|
+
}
|
|
1216
|
+
if (!isObjectLiteral(prop.value)) {
|
|
1217
|
+
const scan = maskCommentsAndStrings(prop.value).trim();
|
|
1218
|
+
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);
|
|
1219
|
+
return { ...span, notObject: definite, objectLiteral: false, entries: [], spec: null };
|
|
1220
|
+
}
|
|
1221
|
+
const objectContent = extractObjectContent(prop.value);
|
|
1222
|
+
if (hasUndecidableEntries(objectContent)) {
|
|
1223
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1224
|
+
}
|
|
1225
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1226
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1227
|
+
const entries = [];
|
|
1228
|
+
for (const entry of parseTopLevelProperties(objectContent)) {
|
|
1229
|
+
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1230
|
+
const valueStart = entry.valueStart === void 0 ? innerStart + entry.nameEnd : innerStart + entry.valueStart + (entry.value ? entry.value.length - entry.value.trimStart().length : 0);
|
|
1231
|
+
entries.push({
|
|
1232
|
+
anchor: entry.name,
|
|
1233
|
+
repeat: entry.kind === "data" ? extractStringLiteralValue(entry.value) : null,
|
|
1234
|
+
repeatDefinitelyNotString: entry.kind !== "data" || isDefiniteNonStringLiteral(entry.value),
|
|
1235
|
+
start: innerStart + entry.nameStart,
|
|
1236
|
+
end: innerStart + entry.nameEnd,
|
|
1237
|
+
valueStart,
|
|
1238
|
+
valueEnd: valueStart + (entry.value?.trim().length ?? 0)
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
return { ...span, notObject: false, objectLiteral: true, entries, spec: specFromRecursionValue(prop) };
|
|
1242
|
+
}
|
|
969
1243
|
function analyzeWatchEntries(scriptContent) {
|
|
1244
|
+
return analyzeObjectEntries(scriptContent, RESERVED_WATCH_KEY).map((entry) => ({
|
|
1245
|
+
key: entry.key,
|
|
1246
|
+
start: entry.start,
|
|
1247
|
+
end: entry.end,
|
|
1248
|
+
// メソッド短縮記法は関数。data は値リテラルの形で判定し、識別子参照は疑わない。
|
|
1249
|
+
definitelyNotFunction: entry.kind === "data" && isNonFunctionLiteral(entry.value)
|
|
1250
|
+
}));
|
|
1251
|
+
}
|
|
1252
|
+
function analyzeListKeyEntries(scriptContent) {
|
|
1253
|
+
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1254
|
+
}
|
|
1255
|
+
function analyzeScanEntries(scriptContent) {
|
|
1256
|
+
return analyzeObjectEntries(scriptContent, RESERVED_SCAN_KEY, true).map(describeScanEntry);
|
|
1257
|
+
}
|
|
1258
|
+
function describeScanEntry(entry) {
|
|
1259
|
+
const unreadable = {
|
|
1260
|
+
name: entry.key,
|
|
1261
|
+
start: entry.start,
|
|
1262
|
+
end: entry.end,
|
|
1263
|
+
notObject: false,
|
|
1264
|
+
readable: false,
|
|
1265
|
+
hasFrom: false,
|
|
1266
|
+
hasOn: false,
|
|
1267
|
+
from: null,
|
|
1268
|
+
on: null,
|
|
1269
|
+
hasInitial: false,
|
|
1270
|
+
foldMissingOrNotFunction: false,
|
|
1271
|
+
resetOn: null,
|
|
1272
|
+
resetOnNotArray: false,
|
|
1273
|
+
fromNotString: false,
|
|
1274
|
+
onNotString: false,
|
|
1275
|
+
resetOnHasNonString: false
|
|
1276
|
+
};
|
|
1277
|
+
if (entry.kind === "method") {
|
|
1278
|
+
return { ...unreadable, notObject: true };
|
|
1279
|
+
}
|
|
1280
|
+
const value = entry.value;
|
|
1281
|
+
if (entry.kind !== "data" || value === void 0 || entry.valueStart === void 0) {
|
|
1282
|
+
return unreadable;
|
|
1283
|
+
}
|
|
1284
|
+
if (!isObjectLiteral(value)) {
|
|
1285
|
+
return { ...unreadable, notObject: isDefiniteNonObjectLiteral(value) };
|
|
1286
|
+
}
|
|
1287
|
+
const content = extractObjectContent(value);
|
|
1288
|
+
if (hasUndecidableEntries(content)) {
|
|
1289
|
+
return unreadable;
|
|
1290
|
+
}
|
|
1291
|
+
const contentStart = entry.valueStart + 1;
|
|
1292
|
+
const props = parseTopLevelProperties(content);
|
|
1293
|
+
const find = (name) => props.find((p) => p.name === name && !(p.kind === "data" && p.value?.trim() === "undefined"));
|
|
1294
|
+
const foldProp = find("fold");
|
|
1295
|
+
const resetProp = find("resetOn");
|
|
1296
|
+
return {
|
|
1297
|
+
...unreadable,
|
|
1298
|
+
readable: true,
|
|
1299
|
+
hasFrom: find("from") !== void 0,
|
|
1300
|
+
hasOn: find("on") !== void 0,
|
|
1301
|
+
from: scanStringField(find("from"), contentStart),
|
|
1302
|
+
on: scanStringField(find("on"), contentStart),
|
|
1303
|
+
hasInitial: props.some((p) => p.name === "initial"),
|
|
1304
|
+
foldMissingOrNotFunction: foldProp === void 0 || foldProp.kind === "data" && isNonFunctionLiteral(foldProp.value),
|
|
1305
|
+
resetOn: resetProp === void 0 ? null : scanStringArrayFields(resetProp, contentStart),
|
|
1306
|
+
resetOnNotArray: resetProp !== void 0 && resetProp.kind === "data" && isDefiniteNonArrayLiteral(resetProp.value),
|
|
1307
|
+
fromNotString: isDefiniteNonPathValue(find("from")),
|
|
1308
|
+
onNotString: isDefiniteNonPathValue(find("on")),
|
|
1309
|
+
resetOnHasNonString: resetProp !== void 0 && resetProp.kind === "data" && resetProp.value !== void 0 && hasDefiniteNonStringElement(resetProp.value)
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function isDefiniteNonPathValue(prop) {
|
|
1313
|
+
if (prop === void 0 || prop.kind === "getter") return false;
|
|
1314
|
+
if (prop.kind === "method") return true;
|
|
1315
|
+
return prop.value !== void 0 && isDefiniteNonPathLiteral(prop.value, true);
|
|
1316
|
+
}
|
|
1317
|
+
function isDefiniteNonPathLiteral(value, emptyIsInvalid) {
|
|
1318
|
+
const text = value.trim();
|
|
1319
|
+
if (/^(["'`])\1$/.test(text)) return emptyIsInvalid;
|
|
1320
|
+
const scan = maskCommentsAndStrings(text).trim();
|
|
1321
|
+
return /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || isWholeBracketLiteral(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1322
|
+
}
|
|
1323
|
+
function isWholeBracketLiteral(scan) {
|
|
1324
|
+
if (scan[0] !== "[" && scan[0] !== "{") return false;
|
|
1325
|
+
let depth = 0;
|
|
1326
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1327
|
+
const ch = scan[i];
|
|
1328
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
1329
|
+
depth++;
|
|
1330
|
+
} else if (ch === ")" || ch === "]" || ch === "}") {
|
|
1331
|
+
depth--;
|
|
1332
|
+
if (depth === 0) return i === scan.length - 1;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
function hasDefiniteNonStringElement(value) {
|
|
1338
|
+
const text = value.trim();
|
|
1339
|
+
const scan = maskCommentsAndStrings(text);
|
|
1340
|
+
if (scan[0] !== "[" || !isWholeBracketLiteral(scan)) return false;
|
|
1341
|
+
const elements = [];
|
|
1342
|
+
let depth = 0;
|
|
1343
|
+
let start = 1;
|
|
1344
|
+
for (let i = 1; i < scan.length - 1; i++) {
|
|
1345
|
+
const ch = scan[i];
|
|
1346
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
1347
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
1348
|
+
else if (ch === "," && depth === 0) {
|
|
1349
|
+
elements.push(text.slice(start, i));
|
|
1350
|
+
start = i + 1;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
elements.push(text.slice(start, scan.length - 1));
|
|
1354
|
+
return elements.some((element) => element.trim().length > 0 && isDefiniteNonPathLiteral(element, false));
|
|
1355
|
+
}
|
|
1356
|
+
function scanStringField(prop, contentStart) {
|
|
1357
|
+
if (!prop || prop.kind !== "data" || prop.value === void 0 || prop.valueStart === void 0) return null;
|
|
1358
|
+
const literal2 = extractStringLiteralValue(prop.value);
|
|
1359
|
+
if (literal2 === null) return null;
|
|
1360
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1361
|
+
const start = contentStart + prop.valueStart + leading + 1;
|
|
1362
|
+
return { value: literal2, start, end: start + literal2.length };
|
|
1363
|
+
}
|
|
1364
|
+
function scanStringArrayFields(prop, contentStart) {
|
|
1365
|
+
if (prop.kind !== "data" || prop.value === void 0 || prop.valueStart === void 0) return null;
|
|
1366
|
+
const text = prop.value.trim();
|
|
1367
|
+
if (!isArrayLiteral(text)) return null;
|
|
1368
|
+
const literal2 = /(["'])([^"'\\\n]*)\1/g;
|
|
1369
|
+
if (text.replace(literal2, "").replace(/[\s,]/g, "") !== "[]") return null;
|
|
1370
|
+
const base = contentStart + prop.valueStart + (prop.value.length - prop.value.trimStart().length);
|
|
1371
|
+
const fields = [];
|
|
1372
|
+
for (const match of text.matchAll(literal2)) {
|
|
1373
|
+
const start = base + match.index + 1;
|
|
1374
|
+
fields.push({ value: match[2], start, end: start + match[2].length });
|
|
1375
|
+
}
|
|
1376
|
+
return fields;
|
|
1377
|
+
}
|
|
1378
|
+
function isDefiniteNonObjectLiteral(value) {
|
|
1379
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1380
|
+
return /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || scan[0] === "[" && isWholeBracketLiteral(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1381
|
+
}
|
|
1382
|
+
function isDefiniteNonArrayLiteral(value) {
|
|
1383
|
+
if (value === void 0) return false;
|
|
1384
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1385
|
+
return /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^\{/.test(scan);
|
|
1386
|
+
}
|
|
1387
|
+
function readEventTokenNames(scriptContent) {
|
|
1388
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1389
|
+
if (!root || hasTopLevelSpread(scriptContent)) return null;
|
|
1390
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_EVENT_TOKENS_KEY);
|
|
1391
|
+
if (!prop) return /* @__PURE__ */ new Set();
|
|
1392
|
+
if (prop.kind !== "data" || prop.value === void 0 || !isArrayLiteral(prop.value)) return null;
|
|
1393
|
+
return new Set(extractStringArrayItems(prop.value));
|
|
1394
|
+
}
|
|
1395
|
+
function hasDefaultExportObject(scriptContent) {
|
|
1396
|
+
return locateDefaultExportObject(scriptContent) !== null;
|
|
1397
|
+
}
|
|
1398
|
+
function hasTopLevelSpread(scriptContent) {
|
|
1399
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1400
|
+
if (!root) return false;
|
|
1401
|
+
const scan = maskCommentsAndStrings(root.content);
|
|
1402
|
+
let depth = 0;
|
|
1403
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1404
|
+
const ch = scan[i];
|
|
1405
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
1406
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
1407
|
+
else if (depth === 0 && ch === "." && scan.startsWith("...", i)) return true;
|
|
1408
|
+
}
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
function analyzeObjectEntries(scriptContent, key, allowEmptyKeys = false) {
|
|
970
1412
|
const root = locateDefaultExportObject(scriptContent);
|
|
971
1413
|
if (!root) return [];
|
|
972
|
-
const
|
|
973
|
-
if (!
|
|
1414
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1415
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
|
|
974
1416
|
return [];
|
|
975
1417
|
}
|
|
976
|
-
const leading =
|
|
977
|
-
const innerStart = root.start +
|
|
1418
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1419
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
978
1420
|
const entries = [];
|
|
979
|
-
for (const entry of parseTopLevelProperties(extractObjectContent(
|
|
1421
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value), allowEmptyKeys)) {
|
|
980
1422
|
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
981
1423
|
entries.push({
|
|
982
1424
|
key: entry.name,
|
|
983
1425
|
start: innerStart + entry.nameStart,
|
|
984
1426
|
end: innerStart + entry.nameEnd,
|
|
985
|
-
|
|
986
|
-
|
|
1427
|
+
kind: entry.kind,
|
|
1428
|
+
value: entry.value,
|
|
1429
|
+
valueStart: entry.valueStart === void 0 || entry.value === void 0 ? void 0 : innerStart + entry.valueStart + (entry.value.length - entry.value.trimStart().length)
|
|
987
1430
|
});
|
|
988
1431
|
}
|
|
989
1432
|
return entries;
|
|
@@ -1031,20 +1474,27 @@ function isNonFunctionLiteral(value) {
|
|
|
1031
1474
|
return /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false|null|undefined)\b/.test(trimmed) || trimmed.startsWith("[") || trimmed.startsWith("{");
|
|
1032
1475
|
}
|
|
1033
1476
|
function findNonObjectWatch(scriptContent) {
|
|
1477
|
+
return findNonObjectDeclaration(scriptContent, RESERVED_WATCH_KEY);
|
|
1478
|
+
}
|
|
1479
|
+
function findNonObjectScan(scriptContent) {
|
|
1480
|
+
return findNonObjectDeclaration(scriptContent, RESERVED_SCAN_KEY, true);
|
|
1481
|
+
}
|
|
1482
|
+
function findNonObjectDeclaration(scriptContent, key, rejectArray = false) {
|
|
1034
1483
|
const root = locateDefaultExportObject(scriptContent);
|
|
1035
1484
|
if (!root) return null;
|
|
1036
|
-
const
|
|
1037
|
-
if (!
|
|
1485
|
+
const declarationProp = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1486
|
+
if (!declarationProp || declarationProp.nameStart === void 0 || declarationProp.nameEnd === void 0) {
|
|
1038
1487
|
return null;
|
|
1039
1488
|
}
|
|
1040
|
-
const span = { start: root.start +
|
|
1041
|
-
if (
|
|
1489
|
+
const span = { start: root.start + declarationProp.nameStart, end: root.start + declarationProp.nameEnd };
|
|
1490
|
+
if (declarationProp.kind === "method") {
|
|
1042
1491
|
return span;
|
|
1043
1492
|
}
|
|
1044
|
-
if (
|
|
1045
|
-
const trimmed =
|
|
1493
|
+
if (declarationProp.kind !== "data" || !declarationProp.value) return null;
|
|
1494
|
+
const trimmed = declarationProp.value.trim();
|
|
1046
1495
|
if (trimmed.startsWith("{")) return null;
|
|
1047
1496
|
const scan = maskCommentsAndStrings(trimmed).trim();
|
|
1497
|
+
if (rejectArray && scan.startsWith("[") && isWholeBracketLiteral(scan)) return span;
|
|
1048
1498
|
const isArrowFunction = /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1049
1499
|
const isWholeLiteral = /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan);
|
|
1050
1500
|
if (!isArrowFunction && !isWholeLiteral) return null;
|
|
@@ -1067,6 +1517,19 @@ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKe
|
|
|
1067
1517
|
}
|
|
1068
1518
|
return;
|
|
1069
1519
|
}
|
|
1520
|
+
if (prop.name === RESERVED_SCAN_KEY && prop.kind === "data" && prop.value && isObjectLiteral(prop.value)) {
|
|
1521
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value), true)) {
|
|
1522
|
+
if (entry.kind !== "data" || !isFlatScanOutputName(entry.name)) continue;
|
|
1523
|
+
const initial = entry.value && isObjectLiteral(entry.value) ? findStreamInitialProperty(entry.value) : void 0;
|
|
1524
|
+
pendingStreamValues.push({
|
|
1525
|
+
name: entry.name,
|
|
1526
|
+
kind: "data",
|
|
1527
|
+
value: initial?.value,
|
|
1528
|
+
typeHint: initial?.typeHint
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1070
1533
|
if (prop.name === RESERVED_COMMAND_TOKENS_KEY && prop.value) {
|
|
1071
1534
|
for (const name of extractStringArrayItems(prop.value)) {
|
|
1072
1535
|
paths.push({ path: `$command.${name}`, kind: "command" });
|
|
@@ -1093,6 +1556,9 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1093
1556
|
if (listPath.length === 0 || segments.some((s) => s.length === 0) || segments[segments.length - 1] === "*") {
|
|
1094
1557
|
return;
|
|
1095
1558
|
}
|
|
1559
|
+
if (hasRecursionWildcard(listPath)) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1096
1562
|
const has = (path) => paths.some((p) => p.path === path);
|
|
1097
1563
|
if (!has(listPath)) paths.push({ path: listPath, kind: "data", typeHint: "array" });
|
|
1098
1564
|
if (!has(`${listPath}.*`)) paths.push({ path: `${listPath}.*`, kind: "list" });
|
|
@@ -1107,8 +1573,42 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1107
1573
|
}
|
|
1108
1574
|
function extractStringLiteralValue(value) {
|
|
1109
1575
|
if (!value) return null;
|
|
1110
|
-
const match = value.trim().match(/^["']([^"'\\]*)["']$/);
|
|
1111
|
-
|
|
1576
|
+
const match = value.trim().match(/^(?:["']([^"'\\]*)["']|`([^`\\$]*)`)$/);
|
|
1577
|
+
const literal2 = match ? match[1] ?? match[2] : null;
|
|
1578
|
+
return literal2 !== null && literal2 !== void 0 && literal2.length > 0 ? literal2 : null;
|
|
1579
|
+
}
|
|
1580
|
+
function hasUndecidableEntries(objectContent) {
|
|
1581
|
+
const scan = maskCommentsAndStrings(objectContent);
|
|
1582
|
+
let depth = 0;
|
|
1583
|
+
let atKey = true;
|
|
1584
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1585
|
+
const ch = scan[i];
|
|
1586
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
1587
|
+
if (depth === 0 && atKey && (ch === "[" || scan.startsWith("...", i))) return true;
|
|
1588
|
+
depth++;
|
|
1589
|
+
atKey = false;
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
1593
|
+
depth--;
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
if (depth !== 0) continue;
|
|
1597
|
+
if (ch === ",") {
|
|
1598
|
+
atKey = true;
|
|
1599
|
+
continue;
|
|
1600
|
+
}
|
|
1601
|
+
if (/\s/.test(ch)) continue;
|
|
1602
|
+
if (atKey && scan.startsWith("...", i)) return true;
|
|
1603
|
+
atKey = false;
|
|
1604
|
+
}
|
|
1605
|
+
return false;
|
|
1606
|
+
}
|
|
1607
|
+
function isDefiniteNonStringLiteral(value) {
|
|
1608
|
+
if (!value) return false;
|
|
1609
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1610
|
+
if (scan.length === 0) return false;
|
|
1611
|
+
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
1612
|
}
|
|
1113
1613
|
var ROW_ASSIGN = new RegExp(
|
|
1114
1614
|
String.raw`\bthis\s*(?:\.\s*([$\w]+)|\[\s*["']([^"']+)["']\s*\])\s*=(?![=>])\s*(?:(\[)|(?:[^;={}]|=>)*?\.\s*(?:concat|toSpliced|with)\s*(\())`,
|
|
@@ -1203,6 +1703,9 @@ function findStreamInitialProperty(entryValue) {
|
|
|
1203
1703
|
const defProps = parseTopLevelProperties(extractObjectContent(entryValue));
|
|
1204
1704
|
return defProps.find((p) => p.kind === "data" && p.name === "initial");
|
|
1205
1705
|
}
|
|
1706
|
+
function isFlatScanOutputName(name) {
|
|
1707
|
+
return name.length > 0 && !name.startsWith("$") && !name.includes(".") && !name.includes("*");
|
|
1708
|
+
}
|
|
1206
1709
|
function extractStringArrayItems(value) {
|
|
1207
1710
|
if (!isArrayLiteral(value)) return [];
|
|
1208
1711
|
const items = [];
|
|
@@ -1214,6 +1717,19 @@ function extractStringArrayItems(value) {
|
|
|
1214
1717
|
return items;
|
|
1215
1718
|
}
|
|
1216
1719
|
var MAX_OBJECT_NEST_DEPTH = 5;
|
|
1720
|
+
function effectiveTopLevelDescriptors(props) {
|
|
1721
|
+
const effective = /* @__PURE__ */ new Map();
|
|
1722
|
+
for (const prop of props) {
|
|
1723
|
+
if (prop.kind !== "getter") {
|
|
1724
|
+
effective.set(prop.name, "data");
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
const current2 = effective.get(prop.name);
|
|
1728
|
+
const pair = current2 === void 0 || current2 === "data" ? { get: false, set: false } : current2;
|
|
1729
|
+
effective.set(prop.name, prop.accessor === "set" ? { ...pair, set: true } : { ...pair, get: true });
|
|
1730
|
+
}
|
|
1731
|
+
return effective;
|
|
1732
|
+
}
|
|
1217
1733
|
function pushDataPropertyPaths(prop, paths) {
|
|
1218
1734
|
pushDataPropertyPathsAt(prop.name, prop, paths, 0);
|
|
1219
1735
|
}
|
|
@@ -1292,10 +1808,10 @@ function locateDefaultExportObject(script) {
|
|
|
1292
1808
|
function extractDefaultExportObject(script) {
|
|
1293
1809
|
return locateDefaultExportObject(script)?.content ?? null;
|
|
1294
1810
|
}
|
|
1295
|
-
function parseTopLevelProperties(objectContent) {
|
|
1811
|
+
function parseTopLevelProperties(objectContent, allowEmptyDataKeys = false) {
|
|
1296
1812
|
const props = [];
|
|
1297
1813
|
const scan = maskCommentsAndStrings(objectContent);
|
|
1298
|
-
const regex = /(?:(?:get|set)\s+(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:async\s+)?(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*:\s*)/gd;
|
|
1814
|
+
const regex = allowEmptyDataKeys ? /(?:(?:get|set)\s+(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:async\s+)?(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:"([^"]*)"|'([^']*)'|([$\w]+))\s*:\s*)/gd : /(?:(?:get|set)\s+(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:async\s+)?(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*:\s*)/gd;
|
|
1299
1815
|
let match;
|
|
1300
1816
|
while ((match = regex.exec(scan)) !== null) {
|
|
1301
1817
|
const indices = match.indices;
|
|
@@ -1341,7 +1857,7 @@ function parseTopLevelProperties(objectContent) {
|
|
|
1341
1857
|
continue;
|
|
1342
1858
|
}
|
|
1343
1859
|
const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
|
|
1344
|
-
if (propName) {
|
|
1860
|
+
if (propName !== void 0) {
|
|
1345
1861
|
const valueStartIndex = match.index + match[0].length;
|
|
1346
1862
|
const value = extractFullValue(objectContent, scan, valueStartIndex);
|
|
1347
1863
|
const jsdocType = extractJsDocType(objectContent, match.index);
|
|
@@ -1702,6 +2218,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
1702
2218
|
continue;
|
|
1703
2219
|
}
|
|
1704
2220
|
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
2221
|
+
const bindComponent = parseAttributeNames(wcsMatch.tagContent).has("bind-component");
|
|
1705
2222
|
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
1706
2223
|
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
1707
2224
|
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
@@ -1743,7 +2260,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
1743
2260
|
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1744
2261
|
if (pos === 0) break;
|
|
1745
2262
|
}
|
|
1746
|
-
elements.push({ mountPath, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2263
|
+
elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
1747
2264
|
pos = wcsEnd;
|
|
1748
2265
|
if (wcsCloseIdx !== -1) {
|
|
1749
2266
|
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
@@ -1833,6 +2350,15 @@ function findCloseTag(html, startPos, tagName) {
|
|
|
1833
2350
|
}
|
|
1834
2351
|
return -1;
|
|
1835
2352
|
}
|
|
2353
|
+
function parseAttributeNames(tagContent) {
|
|
2354
|
+
const names = /* @__PURE__ */ new Set();
|
|
2355
|
+
const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>`=]+))?/g;
|
|
2356
|
+
let match;
|
|
2357
|
+
while ((match = attribute.exec(tagContent)) !== null) {
|
|
2358
|
+
names.add(match[1].toLowerCase());
|
|
2359
|
+
}
|
|
2360
|
+
return names;
|
|
2361
|
+
}
|
|
1836
2362
|
function extractAttribute(tagContent, attrName) {
|
|
1837
2363
|
const regex = new RegExp(
|
|
1838
2364
|
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
@@ -2072,6 +2598,33 @@ var ja = {
|
|
|
2072
2598
|
watchKeyEmptySegment: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`,
|
|
2073
2599
|
watchHandlerNotFunction: (k) => `$watch \u306E\u30A8\u30F3\u30C8\u30EA "${k}" \u306E\u5024\u306F\u95A2\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2074
2600
|
watchPathMissing: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093\uFF08\u4E00\u5EA6\u3082\u767A\u706B\u3057\u307E\u305B\u3093\uFF09`,
|
|
2601
|
+
scanNotObject: () => `$scan \u306F\u300C\u51FA\u529B\u540D \u2192 { from | on, initial, fold, resetOn? }\u300D\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08\u3053\u306E\u5F62\u306F\u30E9\u30F3\u30BF\u30A4\u30E0\u304C\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
|
|
2602
|
+
scanOutputInvalid: (n) => `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F\u5E73\u5766\u306A\u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08"."\u30FB"*"\u30FB\u5148\u982D\u306E "$" \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF09`,
|
|
2603
|
+
scanOutputReserved: (n) => `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F Object.prototype \u304B\u3089\u7D99\u627F\u3055\u308C\u308B\u540D\u524D\u3067\u3059\uFF08"constructor" \u306A\u3069\uFF09`,
|
|
2604
|
+
scanOutputEmpty: () => `$scan \u306E\u51FA\u529B\u540D\u306F\u7A7A\u3067\u306A\u3044\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2605
|
+
scanInVolume: (mountPath) => `$scan \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\u3002scan \u306F\u30EB\u30FC\u30C8\u306E state \u306B\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
2606
|
+
scanInMountedComponent: () => `$scan \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\u3002scan \u306F\u30EB\u30FC\u30C8\u306E state \u306B\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
2607
|
+
scanOutputConflict: (n, other) => other === "getter" ? `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F\u540C\u540D\u306E getter / setter \u3068\u885D\u7A81\u3057\u3066\u3044\u307E\u3059\uFF08\u51FA\u529B\u306F\u30E9\u30F3\u30BF\u30A4\u30E0\u304C\u6240\u6709\u3059\u308B\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u3059\uFF09` : other === "method" ? `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F\u540C\u540D\u306E\u30E1\u30BD\u30C3\u30C9\u3068\u885D\u7A81\u3057\u3066\u3044\u307E\u3059\uFF08\u51FA\u529B\u306F\u30E9\u30F3\u30BF\u30A4\u30E0\u304C\u6240\u6709\u3059\u308B\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u3001\u7573\u3093\u3060\u5024\u304C\u30E1\u30BD\u30C3\u30C9\u3092\u4E0A\u66F8\u304D\u3057\u307E\u3059\uFF09` : `$scan \u306E\u51FA\u529B\u540D "${n}" \u306F\u540C\u540D\u306E $streams \u30A8\u30F3\u30C8\u30EA\u3068\u885D\u7A81\u3057\u3066\u3044\u307E\u3059\uFF08\u51FA\u529B\u306E\u6301\u3061\u4E3B\u306F 1 \u3064\u3060\u3051\u3067\u3059\uFF09`,
|
|
2608
|
+
scanEntryNotObject: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306F { from | on, initial, fold, resetOn? } \u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2609
|
+
scanSourceCount: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306B\u306F "from"\uFF08state \u30D1\u30B9\uFF09\u304B "on"\uFF08\u30A4\u30D9\u30F3\u30C8\u30C8\u30FC\u30AF\u30F3\u540D\uFF09\u306E\u3069\u3061\u3089\u304B 1 \u3064\u3060\u3051\u3092\u66F8\u304D\u307E\u3059`,
|
|
2610
|
+
scanFromNotString: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E "from" \u306F\u7A7A\u3067\u306A\u3044 state \u30D1\u30B9\u306E\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2611
|
+
scanOnNotString: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E "on" \u306F\u7A7A\u3067\u306A\u3044\u30A4\u30D9\u30F3\u30C8\u30C8\u30FC\u30AF\u30F3\u540D\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2612
|
+
scanResetNotString: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E "resetOn" \u306B\u306F state \u30D1\u30B9\u306E\u6587\u5B57\u5217\u3060\u3051\u3092\u66F8\u304D\u307E\u3059`,
|
|
2613
|
+
scanOutputCycle: (chain) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA ${chain.map((c) => `"${c}"`).join(" \u2192 ")} \u306F from \u3092\u901A\u3058\u3066\u4E92\u3044\u3092\u7573\u307F\u5408\u3063\u3066\u3044\u307E\u3059\uFF08\u4E92\u3044\u306E\u66F8\u304D\u8FBC\u307F\u3067\u6C38\u4E45\u306B\u7573\u307F\u7D9A\u3051\u307E\u3059\uFF09`,
|
|
2614
|
+
scanInitialMissing: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306B "initial" \u304C\u3042\u308A\u307E\u305B\u3093\uFF08\u7D2F\u7A4D\u306E\u7A2E\u3067\u3042\u308A\u3001resetOn \u306E\u623B\u308A\u5148\u3067\u3059\uFF09`,
|
|
2615
|
+
scanFoldNotFunction: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E "fold" \u306F\u95A2\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2616
|
+
scanOnUndeclared: (n, t) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E on "${t}" \u306F $eventTokens \u306B\u5BA3\u8A00\u3055\u308C\u3066\u3044\u307E\u305B\u3093`,
|
|
2617
|
+
scanPathInvalid: (n, f, p) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E ${f} "${p}" \u306F state \u30D1\u30B9\u3068\u3057\u3066\u6210\u7ACB\u3057\u307E\u305B\u3093\uFF08\u5148\u982D\u306E "$"\u30FB"@"\u30FB\u7A7A\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u306F\u4F7F\u3048\u307E\u305B\u3093\uFF09`,
|
|
2618
|
+
scanPathReserved: (n, f, p) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E ${f} "${p}" \u306F Object.prototype \u304B\u3089\u7D99\u627F\u3055\u308C\u308B\u540D\u524D\u3067\u3059\uFF08"constructor" \u306A\u3069\uFF09`,
|
|
2619
|
+
scanFromSelf: (n, p) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E from "${p}" \u306F\u81EA\u5206\u306E\u51FA\u529B\u3092\u8AAD\u3093\u3067\u3044\u307E\u3059\uFF08\u81EA\u5206\u306E\u66F8\u304D\u8FBC\u307F\u3092\u6C38\u4E45\u306B\u7573\u307F\u7D9A\u3051\u307E\u3059\uFF09`,
|
|
2620
|
+
scanResetNotArray: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E "resetOn" \u306F state \u30D1\u30B9\u306E\u914D\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2621
|
+
scanResetWildcard: (n, p) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E resetOn "${p}" \u306B "*" \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08reset \u306F\u51FA\u529B\u5168\u4F53\u3092 initial \u306B\u623B\u3057\u307E\u3059\uFF09`,
|
|
2622
|
+
scanResetIsFrom: (n, p) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E resetOn "${p}" \u306F\u81EA\u5206\u306E from \u3068\u540C\u3058\u3067\u3059\uFF08\u5909\u5316\u306E\u305F\u3073\u306B\u7573\u307E\u305A\u306B reset \u3057\u307E\u3059\uFF09`,
|
|
2623
|
+
scanResetUnderFrom: (n, p, from) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E resetOn "${p}" \u306F\u81EA\u5206\u306E from "${from}" \u306E\u914D\u4E0B\u3067\u3059\uFF08from \u3092\u66F8\u304F\u305F\u3073\u306B\u540C\u3058\u30D0\u30C3\u30C1\u306B\u8F09\u308A\u3001reset \u304C\u6BCE\u56DE\u52DD\u3063\u3066\u4E00\u5EA6\u3082\u7573\u307E\u308C\u307E\u305B\u3093\uFF09`,
|
|
2624
|
+
scanResetReadsOutput: (n, p, o) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E resetOn "${p}" \u306F $scan \u306E\u51FA\u529B "${o}" \u3092\u8AAD\u3093\u3067\u3044\u307E\u3059\uFF08\u7D2F\u7A4D\u3067\u7D2F\u7A4D\u3092\u6D88\u3059\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF\u306B\u306A\u308A\u307E\u3059\uFF09\u3002\u7D20\u306E\u5165\u529B\u3067 reset \u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
2625
|
+
scanSourceComputed: (n, f, p, g) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E ${f} "${p}" \u306F${g === p ? " getter" : g.includes("**") ? `\u518D\u5E30 getter "${g}" \u304C\u8A08\u7B97\u3059\u308B\u30D1\u30B9` : ` getter "${g}" \u306E\u914D\u4E0B`}\u3067\u3059\u3002getter \u306F\u5165\u529B\u304C\u5909\u308F\u308B\u305F\u3073\u306B\u518D\u8A55\u4FA1\u3055\u308C\u308B\u306E\u3067\u3001\u7573\u3080\u3068\u51FA\u6765\u4E8B\u3067\u306F\u306A\u304F\u518D\u8A55\u4FA1\u306E\u56DE\u6570\u3092\u6570\u3048\u307E\u3059\u3002getter \u304C\u8AAD\u3080\u7D20\u306E\u5024\u3092\u6307\u3059\u304B\u3001on \u3067\u30A4\u30D9\u30F3\u30C8\u3092\u53D7\u3051\u3066\u304F\u3060\u3055\u3044`,
|
|
2626
|
+
scanFromWriteOnly: (n, p, s) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E from "${p}" \u306F${s === p ? " getter \u306E\u7121\u3044 setter" : ` getter \u306E\u7121\u3044 setter "${s}" \u306E\u914D\u4E0B`}\u3067\u3059\u3002\u8AAD\u3080\u3068\u5E38\u306B undefined \u306A\u306E\u3067\u3001fold \u306F\u6BCE\u56DE undefined \u3092\u53D7\u3051\u53D6\u308A\u307E\u3059\u3002setter \u304C\u66F8\u304F\u7D20\u306E\u5024\u3092\u6307\u3059\u304B\u3001on \u3067\u30A4\u30D9\u30F3\u30C8\u3092\u53D7\u3051\u3066\u304F\u3060\u3055\u3044`,
|
|
2627
|
+
scanPathMissing: (n, f, p) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E ${f} "${p}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093\uFF08${f === "from" ? "\u4E00\u5EA6\u3082\u7573\u307E\u308C\u307E\u305B\u3093" : "\u4E00\u5EA6\u3082 reset \u3055\u308C\u307E\u305B\u3093"}\uFF09`,
|
|
2075
2628
|
typeAnnotationIncompatible: (vt, rt) => `\u578B "${vt}" \u306F @type {${rt}} \u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093`,
|
|
2076
2629
|
arrayMutation: (m, alt) => `\u914D\u5217\u306E\u7834\u58CA\u7684\u30E1\u30BD\u30C3\u30C9 "${m}" \u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\uFF08\u540C\u4E00\u53C2\u7167\u306E\u81EA\u5DF1\u518D\u4EE3\u5165\u3067\u3082\u8981\u7D20\u306E\u8FFD\u52A0\u30FB\u524A\u9664\u306F\u53CD\u6620\u3055\u308C\u307E\u305B\u3093\uFF09\u3002\u975E\u7834\u58CA\u30E1\u30BD\u30C3\u30C9\u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: ${alt}\uFF09\u3002`,
|
|
2077
2630
|
arrayIndexAssign: (sp) => `\u914D\u5217\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3078\u306E\u76F4\u63A5\u4EE3\u5165\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002this["${sp}"] \u306E\u3088\u3046\u306A\u30C9\u30C3\u30C8\u30D1\u30B9\u4EE3\u5165\u3001\u307E\u305F\u306F with() \u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
|
|
@@ -2100,7 +2653,96 @@ var ja = {
|
|
|
2100
2653
|
default:
|
|
2101
2654
|
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
2655
|
}
|
|
2103
|
-
}
|
|
2656
|
+
},
|
|
2657
|
+
recursionUnsupported: (p, where) => {
|
|
2658
|
+
switch (where) {
|
|
2659
|
+
case "binding":
|
|
2660
|
+
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`;
|
|
2661
|
+
case "watch":
|
|
2662
|
+
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`;
|
|
2663
|
+
case "resolve":
|
|
2664
|
+
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`;
|
|
2665
|
+
case "assignment":
|
|
2666
|
+
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`;
|
|
2667
|
+
case "postUpdate":
|
|
2668
|
+
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`;
|
|
2669
|
+
case "trackDependency":
|
|
2670
|
+
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`;
|
|
2671
|
+
case "listKeys":
|
|
2672
|
+
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`;
|
|
2673
|
+
case "scan":
|
|
2674
|
+
return `$scan \u306E\u30D1\u30B9 "${p}" \u306B "**" \u306F\u4F7F\u3048\u307E\u305B\u3093\u3002from / resetOn \u306F\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u3092\u6307\u3057\u307E\u3059`;
|
|
2675
|
+
default:
|
|
2676
|
+
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`;
|
|
2677
|
+
}
|
|
2678
|
+
},
|
|
2679
|
+
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`,
|
|
2680
|
+
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`,
|
|
2681
|
+
recursionSetAllForm: (p, problem) => {
|
|
2682
|
+
switch (problem) {
|
|
2683
|
+
case "prefix":
|
|
2684
|
+
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`;
|
|
2685
|
+
case "noIndexes":
|
|
2686
|
+
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`;
|
|
2687
|
+
case "mapper":
|
|
2688
|
+
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`;
|
|
2689
|
+
default:
|
|
2690
|
+
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`;
|
|
2691
|
+
}
|
|
2692
|
+
},
|
|
2693
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
2694
|
+
switch (target) {
|
|
2695
|
+
case "node":
|
|
2696
|
+
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`;
|
|
2697
|
+
case "branch":
|
|
2698
|
+
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`;
|
|
2699
|
+
case "length":
|
|
2700
|
+
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`;
|
|
2701
|
+
default:
|
|
2702
|
+
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`;
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
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`,
|
|
2706
|
+
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`,
|
|
2707
|
+
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`,
|
|
2708
|
+
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`,
|
|
2709
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
2710
|
+
const subject = kind === "anchor" ? "$recursion \u306E\u30A2\u30F3\u30AB\u30FC" : "$recursion \u306E\u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9";
|
|
2711
|
+
switch (problem) {
|
|
2712
|
+
case "empty":
|
|
2713
|
+
return `${subject}\u306F\u7A7A\u3067\u306A\u3044\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`;
|
|
2714
|
+
case "emptySegment":
|
|
2715
|
+
return `${subject} "${path}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`;
|
|
2716
|
+
case "notElement":
|
|
2717
|
+
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`;
|
|
2718
|
+
case "reservedRoot":
|
|
2719
|
+
return `${subject} "${path}" \u306F "$" \u3067\u59CB\u3081\u3089\u308C\u307E\u305B\u3093\uFF08\u4E88\u7D04\u540D\u524D\u7A7A\u9593\uFF09`;
|
|
2720
|
+
case "reservedMount":
|
|
2721
|
+
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`;
|
|
2722
|
+
case "midWildcard":
|
|
2723
|
+
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`;
|
|
2724
|
+
case "indexSegment":
|
|
2725
|
+
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`;
|
|
2726
|
+
default:
|
|
2727
|
+
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`;
|
|
2728
|
+
}
|
|
2729
|
+
},
|
|
2730
|
+
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`,
|
|
2731
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
2732
|
+
switch (problem) {
|
|
2733
|
+
case "setter":
|
|
2734
|
+
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`;
|
|
2735
|
+
case "notGetter":
|
|
2736
|
+
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`;
|
|
2737
|
+
case "structural":
|
|
2738
|
+
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`;
|
|
2739
|
+
default:
|
|
2740
|
+
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`;
|
|
2741
|
+
}
|
|
2742
|
+
},
|
|
2743
|
+
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`,
|
|
2744
|
+
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`,
|
|
2745
|
+
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
2746
|
};
|
|
2105
2747
|
var EN_EXPECTED_LABEL = {
|
|
2106
2748
|
array: "an array-typed path",
|
|
@@ -2144,6 +2786,33 @@ var en = {
|
|
|
2144
2786
|
watchKeyEmptySegment: (k) => `$watch key "${k}" has an empty path segment`,
|
|
2145
2787
|
watchHandlerNotFunction: (k) => `The value of $watch entry "${k}" must be a function`,
|
|
2146
2788
|
watchPathMissing: (k) => `$watch key "${k}" does not exist in the state definition (it will never fire)`,
|
|
2789
|
+
scanNotObject: () => `$scan must be an object mapping output names to { from | on, initial, fold, resetOn? } (the runtime throws on this shape at load time)`,
|
|
2790
|
+
scanOutputInvalid: (n) => `$scan output name "${n}" must be a flat property name ("." and "*" and a leading "$" are not allowed)`,
|
|
2791
|
+
scanOutputReserved: (n) => `$scan output name "${n}" is a property name inherited from Object.prototype (e.g. "constructor")`,
|
|
2792
|
+
scanOutputEmpty: () => `$scan output name must be a non-empty string`,
|
|
2793
|
+
scanInVolume: (mountPath) => `$scan cannot be declared in a volume (mount="${mountPath}"); the runtime throws before grafting. Declare the scan on the root state`,
|
|
2794
|
+
scanInMountedComponent: () => `$scan is not run by a mounted component (bind-component); the runtime warns with wcs/mount-dollar-declaration and drops it. Declare the scan on the root state`,
|
|
2795
|
+
scanOutputConflict: (n, other) => other === "getter" ? `$scan output "${n}" conflicts with a getter or setter of the same name (the output is a property the runtime owns)` : other === "method" ? `$scan output "${n}" conflicts with a method of the same name (the output is a property the runtime owns, so the folded value would overwrite the method)` : `$scan output "${n}" conflicts with the $streams entry of the same name (each output has exactly one owner)`,
|
|
2796
|
+
scanEntryNotObject: (n) => `$scan entry "${n}" must be an object { from | on, initial, fold, resetOn? }`,
|
|
2797
|
+
scanSourceCount: (n) => `$scan entry "${n}" must declare exactly one of "from" (a state path) or "on" (an event-token name)`,
|
|
2798
|
+
scanFromNotString: (n) => `$scan entry "${n}" "from" must be a non-empty state path string`,
|
|
2799
|
+
scanOnNotString: (n) => `$scan entry "${n}" "on" must be a non-empty event-token name`,
|
|
2800
|
+
scanResetNotString: (n) => `$scan entry "${n}" "resetOn" must contain only state path strings`,
|
|
2801
|
+
scanOutputCycle: (chain) => `$scan entries ${chain.map((c) => `"${c}"`).join(" \u2192 ")} feed each other through "from" (each fold would re-trigger the next forever)`,
|
|
2802
|
+
scanInitialMissing: (n) => `$scan entry "${n}" requires "initial" (the seed of the accumulator and the value resetOn returns to)`,
|
|
2803
|
+
scanFoldNotFunction: (n) => `$scan entry "${n}" fold must be a function`,
|
|
2804
|
+
scanOnUndeclared: (n, t) => `$scan entry "${n}" on "${t}" is not declared in $eventTokens`,
|
|
2805
|
+
scanPathInvalid: (n, f, p) => `$scan entry "${n}" ${f} "${p}" is not a valid state path (a leading "$", "@" and empty segments are not allowed)`,
|
|
2806
|
+
scanPathReserved: (n, f, p) => `$scan entry "${n}" ${f} "${p}" is a property name inherited from Object.prototype (e.g. "constructor")`,
|
|
2807
|
+
scanFromSelf: (n, p) => `$scan entry "${n}" from "${p}" reads the entry's own output (it would fold its own writes forever)`,
|
|
2808
|
+
scanResetNotArray: (n) => `$scan entry "${n}" "resetOn" must be an array of state paths`,
|
|
2809
|
+
scanResetWildcard: (n, p) => `$scan entry "${n}" resetOn "${p}" must not contain "*" (a reset returns the whole output to initial)`,
|
|
2810
|
+
scanResetIsFrom: (n, p) => `$scan entry "${n}" resetOn "${p}" is the entry's own from (every change would reset instead of fold)`,
|
|
2811
|
+
scanResetUnderFrom: (n, p, from) => `$scan entry "${n}" resetOn "${p}" sits under the entry's own from "${from}" (every write of from also lands it, so the reset would win every time and nothing would fold)`,
|
|
2812
|
+
scanResetReadsOutput: (n, p, o) => `$scan entry "${n}" resetOn "${p}" reads the $scan output "${o}" (a reset driven by an accumulator is a feedback loop). Reset on the plain inputs instead`,
|
|
2813
|
+
scanSourceComputed: (n, f, p, g) => `$scan entry "${n}" ${f} "${p}" ${g === p ? "is a getter" : g.includes("**") ? `is computed by the recursive getter "${g}"` : `is under the getter "${g}"`}. A getter re-evaluates whenever its inputs change, so folding it counts re-evaluations, not events. Point at the plain value the getter reads, or use "on" with an event token`,
|
|
2814
|
+
scanFromWriteOnly: (n, p, s) => `$scan entry "${n}" from "${p}" ${s === p ? "is a setter without a getter" : `is under the setter without a getter "${s}"`}, so it always reads undefined and every fold would receive undefined. Point at the plain value the setter writes, or use "on" with an event token`,
|
|
2815
|
+
scanPathMissing: (n, f, p) => `$scan entry "${n}" ${f} "${p}" does not exist in the state definition (${f === "from" ? "it will never fold" : "it will never reset"})`,
|
|
2147
2816
|
typeAnnotationIncompatible: (vt, rt) => `Type "${vt}" is not compatible with @type {${rt}}`,
|
|
2148
2817
|
arrayMutation: (m, alt) => `Destructive array method "${m}" does not trigger a reactive update (re-assigning the same reference does not reflect added/removed elements either). Use a non-destructive method with reassignment (e.g. ${alt}).`,
|
|
2149
2818
|
arrayIndexAssign: (sp) => `Assigning directly to an array index does not trigger a reactive update. Use a dot-path assignment like this["${sp}"], or with() plus reassignment.`,
|
|
@@ -2172,7 +2841,96 @@ var en = {
|
|
|
2172
2841
|
default:
|
|
2173
2842
|
return `"mount" path "${mountPath}" must not use reserved characters ($, #, @).`;
|
|
2174
2843
|
}
|
|
2175
|
-
}
|
|
2844
|
+
},
|
|
2845
|
+
recursionUnsupported: (p, where) => {
|
|
2846
|
+
switch (where) {
|
|
2847
|
+
case "binding":
|
|
2848
|
+
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`;
|
|
2849
|
+
case "watch":
|
|
2850
|
+
return `$watch key "${p}" cannot contain "**" \u2014 watching is defined against a concrete path (a fixed number of "*")`;
|
|
2851
|
+
case "resolve":
|
|
2852
|
+
return `$resolve("${p}") cannot take "**" \u2014 it accepts only an expanded concrete path with an exactly matching index tuple`;
|
|
2853
|
+
case "assignment":
|
|
2854
|
+
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`;
|
|
2855
|
+
case "postUpdate":
|
|
2856
|
+
return `$postUpdate("${p}") cannot take "**" \u2014 a notification is defined against a concrete path (a fixed number of "*")`;
|
|
2857
|
+
case "trackDependency":
|
|
2858
|
+
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
2859
|
+
case "listKeys":
|
|
2860
|
+
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")`;
|
|
2861
|
+
case "scan":
|
|
2862
|
+
return `$scan path "${p}" cannot contain "**" \u2014 "from" and "resetOn" name a concrete path (a fixed number of "*")`;
|
|
2863
|
+
default:
|
|
2864
|
+
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`;
|
|
2865
|
+
}
|
|
2866
|
+
},
|
|
2867
|
+
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 "**")`,
|
|
2868
|
+
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`,
|
|
2869
|
+
recursionSetAllForm: (p, problem) => {
|
|
2870
|
+
switch (problem) {
|
|
2871
|
+
case "prefix":
|
|
2872
|
+
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`;
|
|
2873
|
+
case "noIndexes":
|
|
2874
|
+
return `$setAll("${p}", \u2026) with "**" requires an explicit empty indexes array ([]) \u2014 the write API takes no context`;
|
|
2875
|
+
case "mapper":
|
|
2876
|
+
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`;
|
|
2877
|
+
default:
|
|
2878
|
+
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`;
|
|
2879
|
+
}
|
|
2880
|
+
},
|
|
2881
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
2882
|
+
switch (target) {
|
|
2883
|
+
case "node":
|
|
2884
|
+
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`;
|
|
2885
|
+
case "branch":
|
|
2886
|
+
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`;
|
|
2887
|
+
case "length":
|
|
2888
|
+
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`;
|
|
2889
|
+
default:
|
|
2890
|
+
return `$setAll("${p}") writes the recursion structure itself (the "${repeatList}" list). This version broadcasts to leaf properties only`;
|
|
2891
|
+
}
|
|
2892
|
+
},
|
|
2893
|
+
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`,
|
|
2894
|
+
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`,
|
|
2895
|
+
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)`,
|
|
2896
|
+
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`,
|
|
2897
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
2898
|
+
const subject = kind === "anchor" ? "$recursion anchor" : "$recursion repeating sub-path";
|
|
2899
|
+
switch (problem) {
|
|
2900
|
+
case "empty":
|
|
2901
|
+
return `${subject} must be a non-empty string`;
|
|
2902
|
+
case "emptySegment":
|
|
2903
|
+
return `${subject} "${path}" must not contain empty path segments`;
|
|
2904
|
+
case "notElement":
|
|
2905
|
+
return `${subject} "${path}" must name a list element: a property path ending with ".*" (for example "nodes.*")`;
|
|
2906
|
+
case "reservedRoot":
|
|
2907
|
+
return `${subject} "${path}" must not start with "$" \u2014 that namespace is reserved`;
|
|
2908
|
+
case "reservedMount":
|
|
2909
|
+
return `${subject} "${path}" must not contain "#" \u2014 that segment is reserved for mounts`;
|
|
2910
|
+
case "midWildcard":
|
|
2911
|
+
return `${subject} "${path}" must have exactly one "*", at the end (wildcards in the middle are not supported in this version)`;
|
|
2912
|
+
case "indexSegment":
|
|
2913
|
+
return `${subject} "${path}" must not contain an index segment \u2014 the recursion is declared over the shape of the tree, not over one row`;
|
|
2914
|
+
default:
|
|
2915
|
+
return `${subject} "${path}" must not contain "**" \u2014 the declaration is what gives "**" its meaning`;
|
|
2916
|
+
}
|
|
2917
|
+
},
|
|
2918
|
+
recursionRepeatNotString: (anchor) => `$recursion entry "${anchor}" must map to the repeating sub-path as a string (for example "children.*")`,
|
|
2919
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
2920
|
+
switch (problem) {
|
|
2921
|
+
case "setter":
|
|
2922
|
+
return `Recursive setters are not supported in this version: "${key}". Declare a plain path setter, or write through the concrete path`;
|
|
2923
|
+
case "notGetter":
|
|
2924
|
+
return `"${key}" contains "**" but is not a getter. The recursion wildcard only names a family of computed paths`;
|
|
2925
|
+
case "structural":
|
|
2926
|
+
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")`;
|
|
2927
|
+
default:
|
|
2928
|
+
return `"${key}" names the recursive node itself. "**" names a computed path under a node (for example "${anchor}.total"), not the node`;
|
|
2929
|
+
}
|
|
2930
|
+
},
|
|
2931
|
+
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`,
|
|
2932
|
+
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`,
|
|
2933
|
+
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
2934
|
};
|
|
2177
2935
|
var CATALOGS = { ja, en };
|
|
2178
2936
|
function getMessages(locale3) {
|
|
@@ -2494,7 +3252,11 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2494
3252
|
}
|
|
2495
3253
|
if (checkPath) {
|
|
2496
3254
|
const schema = applicationSchema;
|
|
2497
|
-
const verdict =
|
|
3255
|
+
const verdict = hasRecursionWildcard(checkPath) ? {
|
|
3256
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
3257
|
+
message: msgs.recursionUnsupported(checkPath, "binding"),
|
|
3258
|
+
severity: "error"
|
|
3259
|
+
} : schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2498
3260
|
if (verdict) {
|
|
2499
3261
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2500
3262
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -2513,7 +3275,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2513
3275
|
const pathTrimmed = parsed.path.trim();
|
|
2514
3276
|
const prop = parsed.property.replace(/#.*$/, "");
|
|
2515
3277
|
const insideFor = isInsideForTemplate(html, attr.valueStart, attrName);
|
|
2516
|
-
if (pathTrimmed && !prop.startsWith("on")) {
|
|
3278
|
+
if (pathTrimmed && !prop.startsWith("on") && !hasRecursionWildcard(pathTrimmed)) {
|
|
2517
3279
|
if (!insideFor && pathTrimmed.includes("*")) {
|
|
2518
3280
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2519
3281
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -2793,11 +3555,16 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
|
|
|
2793
3555
|
}
|
|
2794
3556
|
return null;
|
|
2795
3557
|
}
|
|
2796
|
-
if (!scopedPathSet.has(checkPath)) {
|
|
3558
|
+
if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
|
|
2797
3559
|
return msgs.pathMissing(displayPath);
|
|
2798
3560
|
}
|
|
2799
3561
|
return null;
|
|
2800
3562
|
}
|
|
3563
|
+
function matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet) {
|
|
3564
|
+
const specs = collectRecursionSpecs(scopedPaths);
|
|
3565
|
+
if (specs.length === 0) return false;
|
|
3566
|
+
return matchesRecursion(specs, checkPath, (candidate) => scopedPathSet.has(candidate));
|
|
3567
|
+
}
|
|
2801
3568
|
function toMissingVerdict(message) {
|
|
2802
3569
|
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
2803
3570
|
}
|
|
@@ -2806,6 +3573,7 @@ function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scoped
|
|
|
2806
3573
|
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2807
3574
|
}
|
|
2808
3575
|
if (scopedPathSet.has(checkPath)) return null;
|
|
3576
|
+
if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
|
|
2809
3577
|
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
2810
3578
|
if (resolution.kind === "nonexistent") {
|
|
2811
3579
|
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
@@ -3219,6 +3987,13 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
3219
3987
|
const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
|
|
3220
3988
|
const defaultSchema = applicationSchema;
|
|
3221
3989
|
const missingVerdict = (path, displayPath, pathSet2, scoped) => {
|
|
3990
|
+
if (hasRecursionWildcard(path)) {
|
|
3991
|
+
return {
|
|
3992
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
3993
|
+
severity: "error",
|
|
3994
|
+
message: msgs.recursionUnsupported(path, "binding")
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3222
3997
|
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
3223
3998
|
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
3224
3999
|
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
@@ -3351,7 +4126,7 @@ function isValidTemplatePath(path, pathSet, scopedPaths) {
|
|
|
3351
4126
|
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
3352
4127
|
return !hasNamespace || pathSet.has(path);
|
|
3353
4128
|
}
|
|
3354
|
-
return pathSet.has(path);
|
|
4129
|
+
return pathSet.has(path) || matchesRecursionCandidates(scopedPaths, path, pathSet);
|
|
3355
4130
|
}
|
|
3356
4131
|
|
|
3357
4132
|
// src/service/generated/builtinTags.generated.ts
|
|
@@ -5023,96 +5798,873 @@ function collectSignalsRefs(html) {
|
|
|
5023
5798
|
}
|
|
5024
5799
|
continue;
|
|
5025
5800
|
}
|
|
5026
|
-
if (!/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
|
|
5027
|
-
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
5028
|
-
const body = blankJsComments(match[2]);
|
|
5029
|
-
const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*@wcstack\/signals[^"']*)\1/g;
|
|
5030
|
-
let im;
|
|
5031
|
-
while ((im = importRegex.exec(body)) !== null) {
|
|
5032
|
-
const kind = classifySignalsSpecifier(im[2]);
|
|
5033
|
-
if (!kind) continue;
|
|
5034
|
-
const specStart = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
|
|
5035
|
-
refs.push({ kind, start: specStart, end: specStart + im[2].length });
|
|
5801
|
+
if (!/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
|
|
5802
|
+
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
5803
|
+
const body = blankJsComments(match[2]);
|
|
5804
|
+
const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*@wcstack\/signals[^"']*)\1/g;
|
|
5805
|
+
let im;
|
|
5806
|
+
while ((im = importRegex.exec(body)) !== null) {
|
|
5807
|
+
const kind = classifySignalsSpecifier(im[2]);
|
|
5808
|
+
if (!kind) continue;
|
|
5809
|
+
const specStart = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
|
|
5810
|
+
refs.push({ kind, start: specStart, end: specStart + im[2].length });
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5813
|
+
return refs;
|
|
5814
|
+
}
|
|
5815
|
+
function classifySignalsSpecifier(spec) {
|
|
5816
|
+
if (!spec.includes("@wcstack/signals")) return null;
|
|
5817
|
+
return /@wcstack\/signals\/dom\b/.test(spec) ? "dom" : "bare";
|
|
5818
|
+
}
|
|
5819
|
+
function extractSrc(openTag) {
|
|
5820
|
+
const srcMatch = /\bsrc\s*=\s*(["'])(.*?)\1/i.exec(openTag);
|
|
5821
|
+
if (!srcMatch) return null;
|
|
5822
|
+
return {
|
|
5823
|
+
value: srcMatch[2],
|
|
5824
|
+
offsetInTag: srcMatch.index + srcMatch[0].indexOf(srcMatch[1]) + 1
|
|
5825
|
+
};
|
|
5826
|
+
}
|
|
5827
|
+
function blankHtmlComments(html) {
|
|
5828
|
+
return html.replace(/<!--[\s\S]*?-->/g, (m) => " ".repeat(m.length));
|
|
5829
|
+
}
|
|
5830
|
+
function blankJsComments(code) {
|
|
5831
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/(^|[^:])\/\/[^\n]*/g, (m, pre) => pre + " ".repeat(m.length - pre.length));
|
|
5832
|
+
}
|
|
5833
|
+
|
|
5834
|
+
// src/service/watchDeclarationValidator.ts
|
|
5835
|
+
var STATE_NAME_SEPARATOR = "@";
|
|
5836
|
+
function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
5837
|
+
const msgs = getMessages(locale3);
|
|
5838
|
+
const out = [];
|
|
5839
|
+
for (const block of parseWcsScriptBlocks(html, stateTagName)) {
|
|
5840
|
+
const nonObject = findNonObjectWatch(block.content);
|
|
5841
|
+
if (nonObject !== null) {
|
|
5842
|
+
out.push({
|
|
5843
|
+
code: WcsDiagnosticCode.WatchDeclarationInvalid,
|
|
5844
|
+
start: block.contentStart + nonObject.start,
|
|
5845
|
+
end: block.contentStart + nonObject.end,
|
|
5846
|
+
message: msgs.watchNotObject(),
|
|
5847
|
+
severity: "error"
|
|
5848
|
+
});
|
|
5849
|
+
}
|
|
5850
|
+
const entries = analyzeWatchEntries(block.content);
|
|
5851
|
+
if (entries.length === 0) continue;
|
|
5852
|
+
const paths = analyzeStatePaths(block.content);
|
|
5853
|
+
const pathSet = new Set(paths.map((p) => p.path));
|
|
5854
|
+
for (const entry of entries) {
|
|
5855
|
+
const diagnostic = validateEntry(entry, pathSet, paths, msgs);
|
|
5856
|
+
if (diagnostic === null) continue;
|
|
5857
|
+
out.push({
|
|
5858
|
+
code: diagnostic.code,
|
|
5859
|
+
start: block.contentStart + entry.start,
|
|
5860
|
+
end: block.contentStart + entry.end,
|
|
5861
|
+
message: diagnostic.message,
|
|
5862
|
+
severity: diagnostic.severity
|
|
5863
|
+
});
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5866
|
+
return out;
|
|
5867
|
+
}
|
|
5868
|
+
function validateEntry(entry, pathSet, paths, msgs) {
|
|
5869
|
+
const { key } = entry;
|
|
5870
|
+
const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
|
|
5871
|
+
if (key.includes(STATE_NAME_SEPARATOR)) {
|
|
5872
|
+
return invalid(msgs.watchKeyCrossState(key));
|
|
5873
|
+
}
|
|
5874
|
+
if (key.startsWith("$")) {
|
|
5875
|
+
return invalid(msgs.watchKeyReserved(key));
|
|
5876
|
+
}
|
|
5877
|
+
if (key.split(".").some((segment) => segment.length === 0)) {
|
|
5878
|
+
return invalid(msgs.watchKeyEmptySegment(key));
|
|
5879
|
+
}
|
|
5880
|
+
if (hasRecursionWildcard(key)) {
|
|
5881
|
+
return {
|
|
5882
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
5883
|
+
message: msgs.recursionUnsupported(key, "watch"),
|
|
5884
|
+
severity: "error"
|
|
5885
|
+
};
|
|
5886
|
+
}
|
|
5887
|
+
if (entry.definitelyNotFunction) {
|
|
5888
|
+
return invalid(msgs.watchHandlerNotFunction(key));
|
|
5889
|
+
}
|
|
5890
|
+
if (pathSet.size > 0 && !pathSet.has(key) && !matchesRecursion(collectRecursionSpecs(paths), key, (p) => pathSet.has(p))) {
|
|
5891
|
+
return {
|
|
5892
|
+
code: WcsDiagnosticCode.WatchPathMissing,
|
|
5893
|
+
message: msgs.watchPathMissing(key),
|
|
5894
|
+
severity: "warning"
|
|
5895
|
+
};
|
|
5896
|
+
}
|
|
5897
|
+
return null;
|
|
5898
|
+
}
|
|
5899
|
+
|
|
5900
|
+
// src/service/scanDeclarationValidator.ts
|
|
5901
|
+
function validateScanDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
5902
|
+
const msgs = getMessages(locale3);
|
|
5903
|
+
const out = [];
|
|
5904
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
5905
|
+
const mounted = element.bindComponent;
|
|
5906
|
+
for (const block of element.scriptBlocks) {
|
|
5907
|
+
if (block.mountPath !== null || mounted) {
|
|
5908
|
+
const declaration = analyzeDeclarationSpans(block.content).find((span) => span.name === "$scan");
|
|
5909
|
+
if (declaration !== void 0) {
|
|
5910
|
+
out.push({
|
|
5911
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
5912
|
+
start: block.contentStart + declaration.start,
|
|
5913
|
+
end: block.contentStart + declaration.end,
|
|
5914
|
+
message: block.mountPath !== null ? msgs.scanInVolume(block.mountPath) : msgs.scanInMountedComponent(),
|
|
5915
|
+
severity: block.mountPath !== null ? "error" : "warning"
|
|
5916
|
+
});
|
|
5917
|
+
}
|
|
5918
|
+
continue;
|
|
5919
|
+
}
|
|
5920
|
+
const nonObject = findNonObjectScan(block.content);
|
|
5921
|
+
if (nonObject !== null) {
|
|
5922
|
+
out.push({
|
|
5923
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
5924
|
+
start: block.contentStart + nonObject.start,
|
|
5925
|
+
end: block.contentStart + nonObject.end,
|
|
5926
|
+
message: msgs.scanNotObject(),
|
|
5927
|
+
severity: "error"
|
|
5928
|
+
});
|
|
5929
|
+
}
|
|
5930
|
+
const entries = analyzeScanEntries(block.content);
|
|
5931
|
+
if (entries.length === 0) continue;
|
|
5932
|
+
const paths = analyzeStatePaths(block.content);
|
|
5933
|
+
const context = {
|
|
5934
|
+
paths,
|
|
5935
|
+
pathSet: new Set(paths.map((p) => p.path)),
|
|
5936
|
+
recursionSpecs: collectRecursionSpecs(paths),
|
|
5937
|
+
tokens: readEventTokenNames(block.content),
|
|
5938
|
+
outputs: new Set(entries.map((entry) => entry.name)),
|
|
5939
|
+
msgs
|
|
5940
|
+
};
|
|
5941
|
+
const diagnostics = [
|
|
5942
|
+
...entries.flatMap((entry) => validateEntry2(entry, context)),
|
|
5943
|
+
...findOutputCycles(entries, context)
|
|
5944
|
+
];
|
|
5945
|
+
for (const diagnostic of diagnostics) {
|
|
5946
|
+
out.push({
|
|
5947
|
+
code: diagnostic.code,
|
|
5948
|
+
start: block.contentStart + diagnostic.start,
|
|
5949
|
+
end: block.contentStart + diagnostic.end,
|
|
5950
|
+
message: diagnostic.message,
|
|
5951
|
+
severity: diagnostic.severity
|
|
5952
|
+
});
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
return out;
|
|
5957
|
+
}
|
|
5958
|
+
function validateOutput(entry, context) {
|
|
5959
|
+
const { name } = entry;
|
|
5960
|
+
const invalid = (message) => ({ code: WcsDiagnosticCode.ScanDeclarationInvalid, message, severity: "error", start: entry.start, end: entry.end });
|
|
5961
|
+
if (name.length === 0) {
|
|
5962
|
+
return { ...invalid(context.msgs.scanOutputEmpty()), start: entry.start - 1, end: entry.end + 1 };
|
|
5963
|
+
}
|
|
5964
|
+
if (name.startsWith("$") || name.includes(".") || name.includes("*")) {
|
|
5965
|
+
return invalid(context.msgs.scanOutputInvalid(name));
|
|
5966
|
+
}
|
|
5967
|
+
if (name in Object.prototype) {
|
|
5968
|
+
return invalid(context.msgs.scanOutputReserved(name));
|
|
5969
|
+
}
|
|
5970
|
+
if (context.paths.some((p) => p.path === name && p.kind === "computed")) {
|
|
5971
|
+
return invalid(context.msgs.scanOutputConflict(name, "getter"));
|
|
5972
|
+
}
|
|
5973
|
+
if (context.paths.some((p) => p.path === `$streamStatus.${name}`)) {
|
|
5974
|
+
return invalid(context.msgs.scanOutputConflict(name, "stream"));
|
|
5975
|
+
}
|
|
5976
|
+
if (entry.notObject) {
|
|
5977
|
+
return invalid(context.msgs.scanEntryNotObject(name));
|
|
5978
|
+
}
|
|
5979
|
+
if (context.paths.some((p) => p.path === name && p.kind === "method")) {
|
|
5980
|
+
return invalid(context.msgs.scanOutputConflict(name, "method"));
|
|
5981
|
+
}
|
|
5982
|
+
return null;
|
|
5983
|
+
}
|
|
5984
|
+
function validateEntry2(entry, context) {
|
|
5985
|
+
const outputProblem = validateOutput(entry, context);
|
|
5986
|
+
if (outputProblem !== null) return [outputProblem];
|
|
5987
|
+
if (!entry.readable) return [];
|
|
5988
|
+
const { name } = entry;
|
|
5989
|
+
const { msgs } = context;
|
|
5990
|
+
const out = [];
|
|
5991
|
+
const atName = (message) => ({ code: WcsDiagnosticCode.ScanDeclarationInvalid, message, severity: "error", start: entry.start, end: entry.end });
|
|
5992
|
+
const atField = (field, message) => ({ code: WcsDiagnosticCode.ScanDeclarationInvalid, message, severity: "error", start: field.start, end: field.end });
|
|
5993
|
+
if (entry.hasFrom === entry.hasOn) {
|
|
5994
|
+
out.push(atName(msgs.scanSourceCount(name)));
|
|
5995
|
+
}
|
|
5996
|
+
if (entry.onNotString) {
|
|
5997
|
+
out.push(atName(msgs.scanOnNotString(name)));
|
|
5998
|
+
}
|
|
5999
|
+
if (entry.fromNotString) {
|
|
6000
|
+
out.push(atName(msgs.scanFromNotString(name)));
|
|
6001
|
+
}
|
|
6002
|
+
if (!entry.hasInitial) {
|
|
6003
|
+
out.push(atName(msgs.scanInitialMissing(name)));
|
|
6004
|
+
}
|
|
6005
|
+
if (entry.foldMissingOrNotFunction) {
|
|
6006
|
+
out.push(atName(msgs.scanFoldNotFunction(name)));
|
|
6007
|
+
}
|
|
6008
|
+
if (entry.on !== null && context.tokens !== null && !context.tokens.has(entry.on.value)) {
|
|
6009
|
+
out.push(atField(entry.on, msgs.scanOnUndeclared(name, entry.on.value)));
|
|
6010
|
+
}
|
|
6011
|
+
const from = entry.from;
|
|
6012
|
+
if (from !== null) {
|
|
6013
|
+
const problem = checkPathForm(name, "from", from, msgs) ?? checkPathComputed(name, "from", from, context) ?? checkFromWriteOnly(name, from, context) ?? (from.value === name || from.value.startsWith(`${name}.`) ? atField(from, msgs.scanFromSelf(name, from.value)) : null) ?? checkPathMissing(name, "from", from, context);
|
|
6014
|
+
if (problem !== null) out.push(problem);
|
|
6015
|
+
}
|
|
6016
|
+
if (entry.resetOnNotArray) {
|
|
6017
|
+
out.push(atName(msgs.scanResetNotArray(name)));
|
|
6018
|
+
}
|
|
6019
|
+
if (entry.resetOnHasNonString) {
|
|
6020
|
+
out.push(atName(msgs.scanResetNotString(name)));
|
|
6021
|
+
}
|
|
6022
|
+
for (const reset2 of entry.resetOn ?? []) {
|
|
6023
|
+
const root = reset2.value.split(".")[0];
|
|
6024
|
+
const problem = checkPathForm(name, "resetOn", reset2, msgs) ?? (reset2.value.includes("*") ? atField(reset2, msgs.scanResetWildcard(name, reset2.value)) : null) ?? checkPathComputed(name, "resetOn", reset2, context) ?? (from !== null && reset2.value === from.value ? atField(reset2, msgs.scanResetIsFrom(name, reset2.value)) : null) ?? (from !== null && reset2.value.startsWith(`${from.value}.`) ? atField(reset2, msgs.scanResetUnderFrom(name, reset2.value, from.value)) : null) ?? (context.outputs.has(root) ? atField(reset2, msgs.scanResetReadsOutput(name, reset2.value, root)) : null) ?? checkPathMissing(name, "resetOn", reset2, context);
|
|
6025
|
+
if (problem !== null) out.push(problem);
|
|
6026
|
+
}
|
|
6027
|
+
return out;
|
|
6028
|
+
}
|
|
6029
|
+
function checkPathForm(name, field, target, msgs) {
|
|
6030
|
+
const path = target.value;
|
|
6031
|
+
const at2 = (code, message) => ({ code, message, severity: "error", start: target.start, end: target.end });
|
|
6032
|
+
if (path.length === 0 || path.startsWith("$") || path.includes("@")) {
|
|
6033
|
+
return at2(WcsDiagnosticCode.ScanDeclarationInvalid, msgs.scanPathInvalid(name, field, path));
|
|
6034
|
+
}
|
|
6035
|
+
if (path in Object.prototype) {
|
|
6036
|
+
return at2(WcsDiagnosticCode.ScanDeclarationInvalid, msgs.scanPathReserved(name, field, path));
|
|
6037
|
+
}
|
|
6038
|
+
if (hasRecursionWildcard(path)) {
|
|
6039
|
+
return at2(WcsDiagnosticCode.RecursionUnsupported, msgs.recursionUnsupported(path, "scan"));
|
|
6040
|
+
}
|
|
6041
|
+
if (path.split(".").some((segment) => segment.length === 0)) {
|
|
6042
|
+
return at2(WcsDiagnosticCode.ScanDeclarationInvalid, msgs.scanPathInvalid(name, field, path));
|
|
6043
|
+
}
|
|
6044
|
+
return null;
|
|
6045
|
+
}
|
|
6046
|
+
function checkPathComputed(name, field, target, context) {
|
|
6047
|
+
const segments = target.value.split(".");
|
|
6048
|
+
const computed = new Set(context.paths.filter((p) => p.kind === "computed" && p.writeOnly !== true).map((p) => p.path));
|
|
6049
|
+
const at2 = (getter) => ({
|
|
6050
|
+
code: WcsDiagnosticCode.ScanSourceComputed,
|
|
6051
|
+
message: context.msgs.scanSourceComputed(name, field, target.value, getter),
|
|
6052
|
+
severity: "error",
|
|
6053
|
+
start: target.start,
|
|
6054
|
+
end: target.end
|
|
6055
|
+
});
|
|
6056
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
6057
|
+
const prefix = segments.slice(0, i).join(".");
|
|
6058
|
+
if (computed.has(prefix)) {
|
|
6059
|
+
return at2(prefix);
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6062
|
+
const recursive = new Set(context.paths.filter((p) => p.kind === "recursive").map((p) => p.path));
|
|
6063
|
+
const found = { getter: null };
|
|
6064
|
+
matchesRecursion(context.recursionSpecs, target.value, (candidate) => {
|
|
6065
|
+
if (!recursive.has(candidate)) return false;
|
|
6066
|
+
found.getter = candidate;
|
|
6067
|
+
return true;
|
|
6068
|
+
});
|
|
6069
|
+
return found.getter === null ? null : at2(found.getter);
|
|
6070
|
+
}
|
|
6071
|
+
function checkFromWriteOnly(name, target, context) {
|
|
6072
|
+
const writeOnly = new Set(context.paths.filter((p) => p.writeOnly === true).map((p) => p.path));
|
|
6073
|
+
const segments = target.value.split(".");
|
|
6074
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
6075
|
+
const prefix = segments.slice(0, i).join(".");
|
|
6076
|
+
if (writeOnly.has(prefix)) {
|
|
6077
|
+
return {
|
|
6078
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
6079
|
+
message: context.msgs.scanFromWriteOnly(name, target.value, prefix),
|
|
6080
|
+
severity: "error",
|
|
6081
|
+
start: target.start,
|
|
6082
|
+
end: target.end
|
|
6083
|
+
};
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
6086
|
+
return null;
|
|
6087
|
+
}
|
|
6088
|
+
function checkPathMissing(name, field, target, context) {
|
|
6089
|
+
const { paths, pathSet, recursionSpecs } = context;
|
|
6090
|
+
if (paths.length > 0 && !pathSet.has(target.value) && // `$recursion` の展開形は、宣言済みの候補へ畳めれば存在する(`$watch` と同じ照合)
|
|
6091
|
+
!matchesRecursion(recursionSpecs, target.value, (candidate) => pathSet.has(candidate))) {
|
|
6092
|
+
return {
|
|
6093
|
+
code: WcsDiagnosticCode.ScanPathMissing,
|
|
6094
|
+
message: context.msgs.scanPathMissing(name, field, target.value),
|
|
6095
|
+
severity: "warning",
|
|
6096
|
+
start: target.start,
|
|
6097
|
+
end: target.end
|
|
6098
|
+
};
|
|
6099
|
+
}
|
|
6100
|
+
return null;
|
|
6101
|
+
}
|
|
6102
|
+
function findOutputCycles(entries, context) {
|
|
6103
|
+
const fromByName = /* @__PURE__ */ new Map();
|
|
6104
|
+
const next = /* @__PURE__ */ new Map();
|
|
6105
|
+
for (const entry of entries) {
|
|
6106
|
+
if (entry.from === null) continue;
|
|
6107
|
+
const root = entry.from.value.split(".")[0];
|
|
6108
|
+
if (root !== entry.name && context.outputs.has(root)) {
|
|
6109
|
+
fromByName.set(entry.name, entry.from);
|
|
6110
|
+
next.set(entry.name, root);
|
|
6111
|
+
}
|
|
6112
|
+
}
|
|
6113
|
+
const out = [];
|
|
6114
|
+
for (const [start, from] of fromByName) {
|
|
6115
|
+
const chain = [start];
|
|
6116
|
+
let current2 = next.get(start);
|
|
6117
|
+
while (current2 !== void 0 && current2 !== start && chain.length <= next.size) {
|
|
6118
|
+
chain.push(current2);
|
|
6119
|
+
current2 = next.get(current2);
|
|
6120
|
+
}
|
|
6121
|
+
if (current2 === start) {
|
|
6122
|
+
out.push({
|
|
6123
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
6124
|
+
message: context.msgs.scanOutputCycle([...chain, start]),
|
|
6125
|
+
severity: "error",
|
|
6126
|
+
start: from.start,
|
|
6127
|
+
end: from.end
|
|
6128
|
+
});
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
return out;
|
|
6132
|
+
}
|
|
6133
|
+
|
|
6134
|
+
// src/service/scriptCallArgs.ts
|
|
6135
|
+
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
6136
|
+
var TEMPLATE_NO_SUBST = /^\s*`((?:\\.|[^\\`$]|\$(?!\{))*)`\s*$/;
|
|
6137
|
+
function splitCallArgs(source, open) {
|
|
6138
|
+
const args = [];
|
|
6139
|
+
const starts = [];
|
|
6140
|
+
let depth = 0;
|
|
6141
|
+
let argStart = open;
|
|
6142
|
+
let i = open;
|
|
6143
|
+
while (i < source.length) {
|
|
6144
|
+
const ch = source[i];
|
|
6145
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
6146
|
+
const quote = ch;
|
|
6147
|
+
i++;
|
|
6148
|
+
while (i < source.length) {
|
|
6149
|
+
if (source[i] === "\\") {
|
|
6150
|
+
i += 2;
|
|
6151
|
+
continue;
|
|
6152
|
+
}
|
|
6153
|
+
if (source[i] === quote) {
|
|
6154
|
+
i++;
|
|
6155
|
+
break;
|
|
6156
|
+
}
|
|
6157
|
+
i++;
|
|
6158
|
+
}
|
|
6159
|
+
continue;
|
|
6160
|
+
}
|
|
6161
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
6162
|
+
depth++;
|
|
6163
|
+
i++;
|
|
6164
|
+
continue;
|
|
6165
|
+
}
|
|
6166
|
+
if (ch === ")" && depth === 0) {
|
|
6167
|
+
args.push(source.slice(argStart, i));
|
|
6168
|
+
starts.push(argStart);
|
|
6169
|
+
return { args, starts, end: i + 1 };
|
|
6170
|
+
}
|
|
6171
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
6172
|
+
depth--;
|
|
6173
|
+
i++;
|
|
6174
|
+
continue;
|
|
6175
|
+
}
|
|
6176
|
+
if (ch === "," && depth === 0) {
|
|
6177
|
+
args.push(source.slice(argStart, i));
|
|
6178
|
+
starts.push(argStart);
|
|
6179
|
+
argStart = i + 1;
|
|
6180
|
+
i++;
|
|
6181
|
+
continue;
|
|
6182
|
+
}
|
|
6183
|
+
i++;
|
|
6184
|
+
}
|
|
6185
|
+
return null;
|
|
6186
|
+
}
|
|
6187
|
+
function literalString(arg) {
|
|
6188
|
+
const match = STRING_LITERAL.exec(arg);
|
|
6189
|
+
if (match !== null) return match[2];
|
|
6190
|
+
const template = TEMPLATE_NO_SUBST.exec(arg);
|
|
6191
|
+
return template === null ? null : template[1];
|
|
6192
|
+
}
|
|
6193
|
+
function literalArrayLength(arg) {
|
|
6194
|
+
const trimmed = arg.trim();
|
|
6195
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
6196
|
+
const inner = trimmed.slice(1, -1);
|
|
6197
|
+
if (inner.trim().length === 0) return 0;
|
|
6198
|
+
if (/(^|[^.])\.\.\./.test(inner)) return null;
|
|
6199
|
+
const parts = splitCallArgs(`${inner})`, 0);
|
|
6200
|
+
if (parts === null) return null;
|
|
6201
|
+
return parts.args.filter((part) => part.trim().length > 0).length;
|
|
6202
|
+
}
|
|
6203
|
+
function blankComments(source) {
|
|
6204
|
+
const out = source.split("");
|
|
6205
|
+
let i = 0;
|
|
6206
|
+
while (i < source.length) {
|
|
6207
|
+
const ch = source[i];
|
|
6208
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
6209
|
+
const quote = ch;
|
|
6210
|
+
i++;
|
|
6211
|
+
while (i < source.length) {
|
|
6212
|
+
if (source[i] === "\\") {
|
|
6213
|
+
i += 2;
|
|
6214
|
+
continue;
|
|
6215
|
+
}
|
|
6216
|
+
if (source[i] === quote) {
|
|
6217
|
+
i++;
|
|
6218
|
+
break;
|
|
6219
|
+
}
|
|
6220
|
+
i++;
|
|
6221
|
+
}
|
|
6222
|
+
continue;
|
|
6223
|
+
}
|
|
6224
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
6225
|
+
while (i < source.length && source[i] !== "\n") {
|
|
6226
|
+
out[i] = " ";
|
|
6227
|
+
i++;
|
|
6228
|
+
}
|
|
6229
|
+
continue;
|
|
6230
|
+
}
|
|
6231
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
6232
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
6233
|
+
out[i] = " ";
|
|
6234
|
+
i++;
|
|
6235
|
+
}
|
|
6236
|
+
if (i < source.length) {
|
|
6237
|
+
out[i] = " ";
|
|
6238
|
+
out[i + 1] = " ";
|
|
6239
|
+
i += 2;
|
|
6240
|
+
}
|
|
6241
|
+
continue;
|
|
6242
|
+
}
|
|
6243
|
+
i++;
|
|
6244
|
+
}
|
|
6245
|
+
return out.join("");
|
|
6246
|
+
}
|
|
6247
|
+
function createApiCallRegex(apis) {
|
|
6248
|
+
return new RegExp(`\\.\\s*\\$(${apis.join("|")})\\s*\\(`, "g");
|
|
6249
|
+
}
|
|
6250
|
+
|
|
6251
|
+
// src/service/recursionValidator.ts
|
|
6252
|
+
var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "trackDependency"];
|
|
6253
|
+
var UNSUPPORTED_API_SITE = {
|
|
6254
|
+
$resolve: "resolve",
|
|
6255
|
+
$postUpdate: "postUpdate",
|
|
6256
|
+
$trackDependency: "trackDependency"
|
|
6257
|
+
};
|
|
6258
|
+
var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
6259
|
+
var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
6260
|
+
function validateRecursion(html, stateTagName = "wcs-state", locale3) {
|
|
6261
|
+
const msgs = getMessages(locale3);
|
|
6262
|
+
const out = [];
|
|
6263
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
6264
|
+
const mounted = element.bindComponent;
|
|
6265
|
+
for (const block of element.scriptBlocks) {
|
|
6266
|
+
if (!hasRecursionWildcard(block.content) && block.content.indexOf("$recursion") === -1) continue;
|
|
6267
|
+
const declaration = analyzeRecursionDeclaration(block.content);
|
|
6268
|
+
let spec = null;
|
|
6269
|
+
let undeclared = false;
|
|
6270
|
+
let getterSuffixes = [];
|
|
6271
|
+
if (block.mountPath !== null) {
|
|
6272
|
+
validateVolumeBlock(block.content, block.contentStart, block.mountPath, declaration, msgs, out);
|
|
6273
|
+
} else if (mounted) {
|
|
6274
|
+
validateMountedComponentBlock(block.content, block.contentStart, declaration, msgs, out);
|
|
6275
|
+
} else {
|
|
6276
|
+
spec = validateDeclaration(declaration, block.contentStart, msgs, out);
|
|
6277
|
+
undeclared = declaration === null && hasDefaultExportObject(block.content) && !hasTopLevelSpread(block.content);
|
|
6278
|
+
getterSuffixes = validateRecursiveGetters(block.content, block.contentStart, spec, undeclared, msgs, out);
|
|
6279
|
+
}
|
|
6280
|
+
validateListKeys(block.content, block.contentStart, msgs, out);
|
|
6281
|
+
validateApiCalls(block.content, block.contentStart, spec, getterSuffixes, undeclared, msgs, out);
|
|
6282
|
+
validateAssignments(block.content, block.contentStart, spec, getterSuffixes, msgs, out);
|
|
6283
|
+
}
|
|
6284
|
+
}
|
|
6285
|
+
return out;
|
|
6286
|
+
}
|
|
6287
|
+
function push(out, code, start, end, message, severity = "error") {
|
|
6288
|
+
out.push({ code, start, end, message, severity });
|
|
6289
|
+
}
|
|
6290
|
+
function validateVolumeBlock(script, offset2, mountPath, declaration, msgs, out) {
|
|
6291
|
+
if (declaration !== null) {
|
|
6292
|
+
push(
|
|
6293
|
+
out,
|
|
6294
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6295
|
+
offset2 + declaration.start,
|
|
6296
|
+
offset2 + declaration.end,
|
|
6297
|
+
msgs.recursionInVolume("$recursion", mountPath)
|
|
6298
|
+
);
|
|
6299
|
+
}
|
|
6300
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6301
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6302
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
6303
|
+
seen.add(span.name);
|
|
6304
|
+
push(
|
|
6305
|
+
out,
|
|
6306
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6307
|
+
offset2 + span.start,
|
|
6308
|
+
offset2 + span.end,
|
|
6309
|
+
msgs.recursionInVolume(`"${span.name}"`, mountPath)
|
|
6310
|
+
);
|
|
6311
|
+
}
|
|
6312
|
+
}
|
|
6313
|
+
function validateDeclaration(declaration, offset2, msgs, out) {
|
|
6314
|
+
if (declaration === null) return null;
|
|
6315
|
+
const code = WcsDiagnosticCode.RecursionDeclarationInvalid;
|
|
6316
|
+
if (declaration.notObject) {
|
|
6317
|
+
push(out, code, offset2 + declaration.start, offset2 + declaration.end, msgs.recursionNotObject());
|
|
6318
|
+
return null;
|
|
6319
|
+
}
|
|
6320
|
+
if (declaration.entries.length !== 1) {
|
|
6321
|
+
if (!declaration.objectLiteral) return null;
|
|
6322
|
+
push(
|
|
6323
|
+
out,
|
|
6324
|
+
code,
|
|
6325
|
+
offset2 + declaration.start,
|
|
6326
|
+
offset2 + declaration.end,
|
|
6327
|
+
msgs.recursionAnchorCount(declaration.entries.length)
|
|
6328
|
+
);
|
|
6329
|
+
return null;
|
|
6330
|
+
}
|
|
6331
|
+
const entry = declaration.entries[0];
|
|
6332
|
+
const anchorProblem = checkNodePath(entry.anchor);
|
|
6333
|
+
if (anchorProblem !== null) {
|
|
6334
|
+
push(
|
|
6335
|
+
out,
|
|
6336
|
+
code,
|
|
6337
|
+
offset2 + entry.start,
|
|
6338
|
+
offset2 + entry.end,
|
|
6339
|
+
msgs.recursionNodePathInvalid("anchor", entry.anchor, anchorProblem)
|
|
6340
|
+
);
|
|
6341
|
+
return null;
|
|
6342
|
+
}
|
|
6343
|
+
if (entry.repeat === null) {
|
|
6344
|
+
if (entry.repeatDefinitelyNotString) {
|
|
6345
|
+
push(
|
|
6346
|
+
out,
|
|
6347
|
+
code,
|
|
6348
|
+
offset2 + entry.valueStart,
|
|
6349
|
+
offset2 + entry.valueEnd,
|
|
6350
|
+
msgs.recursionRepeatNotString(entry.anchor)
|
|
6351
|
+
);
|
|
6352
|
+
}
|
|
6353
|
+
return null;
|
|
6354
|
+
}
|
|
6355
|
+
const repeatProblem = checkNodePath(entry.repeat);
|
|
6356
|
+
if (repeatProblem !== null) {
|
|
6357
|
+
push(
|
|
6358
|
+
out,
|
|
6359
|
+
code,
|
|
6360
|
+
offset2 + entry.valueStart,
|
|
6361
|
+
offset2 + entry.valueEnd,
|
|
6362
|
+
msgs.recursionNodePathInvalid("repeat", entry.repeat, repeatProblem)
|
|
6363
|
+
);
|
|
6364
|
+
return null;
|
|
6365
|
+
}
|
|
6366
|
+
return makeRecursionSpec(entry.anchor, entry.repeat);
|
|
6367
|
+
}
|
|
6368
|
+
function validateRecursiveGetters(script, offset2, spec, undeclared, msgs, out) {
|
|
6369
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6370
|
+
const spans = analyzeDeclarationSpans(script).filter((s) => hasRecursionWildcard(s.name)).filter((s) => seen.has(s.name) ? false : (seen.add(s.name), true));
|
|
6371
|
+
if (spans.length === 0) return [];
|
|
6372
|
+
const setterNames = new Set(
|
|
6373
|
+
analyzeCallableBodies(script).filter((c) => c.accessor === "set").map((c) => c.name)
|
|
6374
|
+
);
|
|
6375
|
+
const suffixes = [];
|
|
6376
|
+
const accepted = [];
|
|
6377
|
+
for (const span of spans) {
|
|
6378
|
+
const start = offset2 + span.start;
|
|
6379
|
+
const end = offset2 + span.end;
|
|
6380
|
+
if (spec === null) {
|
|
6381
|
+
if (undeclared) {
|
|
6382
|
+
push(
|
|
6383
|
+
out,
|
|
6384
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6385
|
+
start,
|
|
6386
|
+
end,
|
|
6387
|
+
msgs.recursionUnsupported(span.name, "undeclared"),
|
|
6388
|
+
"warning"
|
|
6389
|
+
);
|
|
6390
|
+
}
|
|
6391
|
+
continue;
|
|
6392
|
+
}
|
|
6393
|
+
if (span.kind !== "getter") {
|
|
6394
|
+
push(
|
|
6395
|
+
out,
|
|
6396
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6397
|
+
start,
|
|
6398
|
+
end,
|
|
6399
|
+
msgs.recursionGetterInvalid(span.name, "notGetter", spec.recursiveAnchor)
|
|
6400
|
+
);
|
|
6401
|
+
continue;
|
|
6402
|
+
}
|
|
6403
|
+
if (setterNames.has(span.name)) {
|
|
6404
|
+
push(
|
|
6405
|
+
out,
|
|
6406
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6407
|
+
start,
|
|
6408
|
+
end,
|
|
6409
|
+
msgs.recursionGetterInvalid(span.name, "setter", spec.recursiveAnchor)
|
|
6410
|
+
);
|
|
6411
|
+
continue;
|
|
6412
|
+
}
|
|
6413
|
+
const suffix = splitRecursivePath(spec, span.name);
|
|
6414
|
+
if (suffix === null) {
|
|
6415
|
+
push(
|
|
6416
|
+
out,
|
|
6417
|
+
WcsDiagnosticCode.RecursionAnchor,
|
|
6418
|
+
start,
|
|
6419
|
+
end,
|
|
6420
|
+
msgs.recursionAnchorMismatch(span.name, spec.recursiveAnchor)
|
|
6421
|
+
);
|
|
6422
|
+
continue;
|
|
6423
|
+
}
|
|
6424
|
+
if (suffix.length === 0) {
|
|
6425
|
+
push(
|
|
6426
|
+
out,
|
|
6427
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6428
|
+
start,
|
|
6429
|
+
end,
|
|
6430
|
+
msgs.recursionGetterInvalid(span.name, "nodeItself", spec.recursiveAnchor)
|
|
6431
|
+
);
|
|
6432
|
+
continue;
|
|
6433
|
+
}
|
|
6434
|
+
if (structuralWriteTarget(spec, foldSuffixIndexes(suffix)) !== null) {
|
|
6435
|
+
push(
|
|
6436
|
+
out,
|
|
6437
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6438
|
+
start,
|
|
6439
|
+
end,
|
|
6440
|
+
msgs.recursionGetterInvalid(span.name, "structural", spec.recursiveAnchor)
|
|
6441
|
+
);
|
|
6442
|
+
continue;
|
|
6443
|
+
}
|
|
6444
|
+
const collision = accepted.find((other) => sameFamily(spec, other.suffix, suffix));
|
|
6445
|
+
if (collision !== void 0) {
|
|
6446
|
+
push(
|
|
6447
|
+
out,
|
|
6448
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6449
|
+
start,
|
|
6450
|
+
end,
|
|
6451
|
+
msgs.recursionGetterCollision(collision.name, span.name, spec.repeat)
|
|
6452
|
+
);
|
|
6453
|
+
continue;
|
|
5036
6454
|
}
|
|
6455
|
+
accepted.push({ name: span.name, suffix, start, end });
|
|
6456
|
+
suffixes.push(suffix);
|
|
5037
6457
|
}
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
6458
|
+
if (spec !== null && suffixes.length > 0) {
|
|
6459
|
+
const reported = /* @__PURE__ */ new Set();
|
|
6460
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6461
|
+
if (hasRecursionWildcard(span.name) || reported.has(span.name)) continue;
|
|
6462
|
+
const suffix = concreteExpansionSuffix(spec, suffixes, span.name);
|
|
6463
|
+
if (suffix === null) continue;
|
|
6464
|
+
reported.add(span.name);
|
|
6465
|
+
push(
|
|
6466
|
+
out,
|
|
6467
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6468
|
+
offset2 + span.start,
|
|
6469
|
+
offset2 + span.end,
|
|
6470
|
+
msgs.recursionConcreteCollision(span.name, spec.recursiveAnchor + suffix)
|
|
6471
|
+
);
|
|
6472
|
+
}
|
|
6473
|
+
}
|
|
6474
|
+
return suffixes;
|
|
5051
6475
|
}
|
|
5052
|
-
function
|
|
5053
|
-
|
|
6476
|
+
function validateMountedComponentBlock(script, offset2, declaration, msgs, out) {
|
|
6477
|
+
if (declaration !== null) {
|
|
6478
|
+
push(
|
|
6479
|
+
out,
|
|
6480
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6481
|
+
offset2 + declaration.start,
|
|
6482
|
+
offset2 + declaration.end,
|
|
6483
|
+
msgs.recursionInMountedComponent("$recursion"),
|
|
6484
|
+
"warning"
|
|
6485
|
+
);
|
|
6486
|
+
}
|
|
6487
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6488
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6489
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
6490
|
+
seen.add(span.name);
|
|
6491
|
+
push(
|
|
6492
|
+
out,
|
|
6493
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6494
|
+
offset2 + span.start,
|
|
6495
|
+
offset2 + span.end,
|
|
6496
|
+
msgs.recursionInMountedComponent(`"${span.name}"`),
|
|
6497
|
+
"warning"
|
|
6498
|
+
);
|
|
6499
|
+
}
|
|
5054
6500
|
}
|
|
5055
|
-
function
|
|
5056
|
-
|
|
6501
|
+
function validateListKeys(script, offset2, msgs, out) {
|
|
6502
|
+
for (const entry of analyzeListKeyEntries(script)) {
|
|
6503
|
+
if (!hasRecursionWildcard(entry.key)) continue;
|
|
6504
|
+
push(
|
|
6505
|
+
out,
|
|
6506
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6507
|
+
offset2 + entry.start,
|
|
6508
|
+
offset2 + entry.end,
|
|
6509
|
+
msgs.recursionUnsupported(entry.key, "listKeys")
|
|
6510
|
+
);
|
|
6511
|
+
}
|
|
5057
6512
|
}
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
6513
|
+
function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msgs, out) {
|
|
6514
|
+
const scan = blankComments(script);
|
|
6515
|
+
const regex = createApiCallRegex(RECURSION_APIS);
|
|
6516
|
+
let match;
|
|
6517
|
+
while ((match = regex.exec(scan)) !== null) {
|
|
6518
|
+
const api = `$${match[1]}`;
|
|
6519
|
+
const parsed = splitCallArgs(scan, match.index + match[0].length);
|
|
6520
|
+
if (parsed === null) continue;
|
|
6521
|
+
regex.lastIndex = parsed.end;
|
|
6522
|
+
if (parsed.args.length === 0) continue;
|
|
6523
|
+
const pathArg = parsed.args[0];
|
|
6524
|
+
const path = literalString(pathArg);
|
|
6525
|
+
if (path === null) continue;
|
|
6526
|
+
const leading = pathArg.length - pathArg.trimStart().length;
|
|
6527
|
+
const start = offset2 + parsed.starts[0] + leading;
|
|
6528
|
+
const end = offset2 + parsed.starts[0] + pathArg.trimEnd().length;
|
|
6529
|
+
if (!hasRecursionWildcard(path)) {
|
|
6530
|
+
const writes = api === "$setAll" || api === "$resolve" && parsed.args.length >= 3;
|
|
6531
|
+
if (writes && spec !== null) {
|
|
6532
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6533
|
+
if (owning !== null) {
|
|
6534
|
+
push(
|
|
6535
|
+
out,
|
|
6536
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6537
|
+
start,
|
|
6538
|
+
end,
|
|
6539
|
+
msgs.recursionReadonly(`${api}("${path}")`, spec.recursiveAnchor + owning)
|
|
6540
|
+
);
|
|
6541
|
+
}
|
|
6542
|
+
}
|
|
6543
|
+
continue;
|
|
5074
6544
|
}
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
}
|
|
6545
|
+
if (api in UNSUPPORTED_API_SITE) {
|
|
6546
|
+
push(
|
|
6547
|
+
out,
|
|
6548
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6549
|
+
start,
|
|
6550
|
+
end,
|
|
6551
|
+
msgs.recursionUnsupported(path, UNSUPPORTED_API_SITE[api])
|
|
6552
|
+
);
|
|
6553
|
+
continue;
|
|
6554
|
+
}
|
|
6555
|
+
if (spec === null) {
|
|
6556
|
+
if (undeclared) {
|
|
6557
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "undeclared"));
|
|
6558
|
+
}
|
|
6559
|
+
continue;
|
|
6560
|
+
}
|
|
6561
|
+
const suffix = splitRecursivePath(spec, path);
|
|
6562
|
+
if (suffix === null) {
|
|
6563
|
+
push(out, WcsDiagnosticCode.RecursionAnchor, start, end, msgs.recursionAnchorMismatch(path, spec.recursiveAnchor));
|
|
6564
|
+
continue;
|
|
6565
|
+
}
|
|
6566
|
+
if (api === "$getAll") {
|
|
6567
|
+
if (parsed.args.length > 1) {
|
|
6568
|
+
const indexesArg = parsed.args[1];
|
|
6569
|
+
const indexes = literalArrayLength(indexesArg);
|
|
6570
|
+
if (indexes !== null && indexes > 0) {
|
|
6571
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "prefix"));
|
|
6572
|
+
} else if (indexes === null && isDefiniteNonArrayLiteral2(indexesArg)) {
|
|
6573
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "notArray"));
|
|
6574
|
+
}
|
|
6575
|
+
}
|
|
6576
|
+
continue;
|
|
5089
6577
|
}
|
|
6578
|
+
validateSetAllForm(path, suffix, parsed.args, spec, getterSuffixes, start, end, msgs, out);
|
|
5090
6579
|
}
|
|
5091
|
-
return out;
|
|
5092
6580
|
}
|
|
5093
|
-
function
|
|
5094
|
-
const
|
|
5095
|
-
const
|
|
5096
|
-
if (
|
|
5097
|
-
|
|
6581
|
+
function validateSetAllForm(path, suffix, args, spec, getterSuffixes, start, end, msgs, out) {
|
|
6582
|
+
const formCode = WcsDiagnosticCode.RecursionSetAllForm;
|
|
6583
|
+
const indexesArg = args.length > 1 ? args[1].trim() : "";
|
|
6584
|
+
if (args.length < 2 || indexesArg === "undefined" || indexesArg === "null") {
|
|
6585
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "noIndexes"));
|
|
6586
|
+
return;
|
|
5098
6587
|
}
|
|
5099
|
-
|
|
5100
|
-
|
|
6588
|
+
const indexes = literalArrayLength(args[1]);
|
|
6589
|
+
if (indexes !== null && indexes > 0) {
|
|
6590
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "prefix"));
|
|
6591
|
+
return;
|
|
5101
6592
|
}
|
|
5102
|
-
if (
|
|
5103
|
-
|
|
6593
|
+
if (args.length > 2 && isFunctionLiteral(args[2])) {
|
|
6594
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "mapper"));
|
|
6595
|
+
return;
|
|
5104
6596
|
}
|
|
5105
|
-
if (
|
|
5106
|
-
|
|
6597
|
+
if (args.length > 3 && /\bspread\s*:\s*true\b/.test(args[3])) {
|
|
6598
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "spread"));
|
|
6599
|
+
return;
|
|
5107
6600
|
}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
6601
|
+
const checkedSuffix = foldSuffixIndexes(suffix);
|
|
6602
|
+
const structural = structuralWriteTarget(spec, checkedSuffix);
|
|
6603
|
+
if (structural !== null) {
|
|
6604
|
+
push(
|
|
6605
|
+
out,
|
|
6606
|
+
WcsDiagnosticCode.RecursionStructuralWrite,
|
|
6607
|
+
start,
|
|
6608
|
+
end,
|
|
6609
|
+
msgs.recursionStructuralWrite(path, structural, spec.repeatList)
|
|
6610
|
+
);
|
|
6611
|
+
return;
|
|
6612
|
+
}
|
|
6613
|
+
const conflicting = conflictingGetterSuffix(spec, getterSuffixes, checkedSuffix);
|
|
6614
|
+
if (conflicting !== null) {
|
|
6615
|
+
push(
|
|
6616
|
+
out,
|
|
6617
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6618
|
+
start,
|
|
6619
|
+
end,
|
|
6620
|
+
msgs.recursionReadonly(`$setAll("${path}")`, spec.recursiveAnchor + conflicting)
|
|
6621
|
+
);
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
function validateAssignments(script, offset2, spec, getterSuffixes, msgs, out) {
|
|
6625
|
+
const masked = maskCommentsAndStrings(script);
|
|
6626
|
+
const found = [];
|
|
6627
|
+
for (const source of [BRACKET_ASSIGNMENT, PRE_BRACKET_INCDEC]) {
|
|
6628
|
+
const regex = new RegExp(source.source, "g");
|
|
6629
|
+
let match;
|
|
6630
|
+
while ((match = regex.exec(masked)) !== null) {
|
|
6631
|
+
const pathStart = match.index + match[0].search(/["']/) + 1;
|
|
6632
|
+
found.push({ pathStart, length: match[1].length });
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
found.sort((a, b) => a.pathStart - b.pathStart);
|
|
6636
|
+
let last = -1;
|
|
6637
|
+
for (const { pathStart, length } of found) {
|
|
6638
|
+
if (pathStart === last) continue;
|
|
6639
|
+
last = pathStart;
|
|
6640
|
+
const path = script.slice(pathStart, pathStart + length);
|
|
6641
|
+
const start = offset2 + pathStart;
|
|
6642
|
+
const end = start + path.length;
|
|
6643
|
+
if (hasRecursionWildcard(path)) {
|
|
6644
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "assignment"));
|
|
6645
|
+
continue;
|
|
6646
|
+
}
|
|
6647
|
+
if (spec === null) continue;
|
|
6648
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6649
|
+
if (owning !== null) {
|
|
6650
|
+
push(
|
|
6651
|
+
out,
|
|
6652
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6653
|
+
start,
|
|
6654
|
+
end,
|
|
6655
|
+
msgs.recursionReadonly(`this["${path}"] = \u2026`, spec.recursiveAnchor + owning)
|
|
6656
|
+
);
|
|
6657
|
+
}
|
|
5114
6658
|
}
|
|
5115
|
-
|
|
6659
|
+
}
|
|
6660
|
+
function isDefiniteNonArrayLiteral2(arg) {
|
|
6661
|
+
const trimmed = arg.trim();
|
|
6662
|
+
return trimmed === "null" || /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false)$/.test(trimmed) || trimmed.startsWith("{");
|
|
6663
|
+
}
|
|
6664
|
+
function isFunctionLiteral(arg) {
|
|
6665
|
+
const trimmed = arg.trim();
|
|
6666
|
+
if (trimmed.length === 0) return false;
|
|
6667
|
+
return /^(?:async\s+)?function\b/.test(trimmed) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(trimmed) || /^(?:async\s+)?[$\w]+\s*=>/.test(trimmed);
|
|
5116
6668
|
}
|
|
5117
6669
|
|
|
5118
6670
|
// src/service/namedStateValidator.ts
|
|
@@ -11039,7 +12591,7 @@ function enterFunction(fn, outer, thisIsState) {
|
|
|
11039
12591
|
function isThisRoot(node, scope) {
|
|
11040
12592
|
return node.type === "ThisExpression" && scope.thisIsState || node.type === "Identifier" && scope.aliases.has(node.name);
|
|
11041
12593
|
}
|
|
11042
|
-
function
|
|
12594
|
+
function literalString2(node) {
|
|
11043
12595
|
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
11044
12596
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
11045
12597
|
return node.quasis[0].value.cooked ?? null;
|
|
@@ -11054,7 +12606,7 @@ function segmentOf(member) {
|
|
|
11054
12606
|
if (property.type === "Literal" && typeof property.value === "number") {
|
|
11055
12607
|
return { text: String(property.value), dynamic: null };
|
|
11056
12608
|
}
|
|
11057
|
-
const text =
|
|
12609
|
+
const text = literalString2(property);
|
|
11058
12610
|
if (text !== null) return { text, dynamic: null };
|
|
11059
12611
|
return property.type === "PrivateIdentifier" ? { text: null, dynamic: null } : { text: null, dynamic: property };
|
|
11060
12612
|
}
|
|
@@ -11150,7 +12702,7 @@ function visitCall(node, scope, out) {
|
|
|
11150
12702
|
if (api === UNTRACK_API) return;
|
|
11151
12703
|
if (PATH_ARG_APIS.has(api)) {
|
|
11152
12704
|
const first = node.arguments[0];
|
|
11153
|
-
const path = first !== void 0 && first.type !== "SpreadElement" ?
|
|
12705
|
+
const path = first !== void 0 && first.type !== "SpreadElement" ? literalString2(first) : null;
|
|
11154
12706
|
if (path !== null && path.length > 0 && !path.startsWith("$")) {
|
|
11155
12707
|
out.push({
|
|
11156
12708
|
path,
|
|
@@ -11216,7 +12768,7 @@ function visitDestructure(pattern, prefix, scope, out) {
|
|
|
11216
12768
|
if (property.type === "RestElement") continue;
|
|
11217
12769
|
let key = null;
|
|
11218
12770
|
if (!property.computed && property.key.type === "Identifier") key = property.key.name;
|
|
11219
|
-
else key =
|
|
12771
|
+
else key = literalString2(property.key);
|
|
11220
12772
|
let value = property.value;
|
|
11221
12773
|
if (value.type === "AssignmentPattern") {
|
|
11222
12774
|
visit(value.right, scope, out);
|
|
@@ -11249,6 +12801,10 @@ for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
|
|
|
11249
12801
|
tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
|
|
11250
12802
|
}
|
|
11251
12803
|
Object.freeze(tmpIndexByIndexName2);
|
|
12804
|
+
var RECURSION_WILDCARD2 = "**";
|
|
12805
|
+
function raiseError2(message) {
|
|
12806
|
+
throw new Error(`[@wcstack/state] ${message}`);
|
|
12807
|
+
}
|
|
11252
12808
|
var _cache = /* @__PURE__ */ new Map();
|
|
11253
12809
|
function clearPathInfoCacheForTooling() {
|
|
11254
12810
|
_cache.clear();
|
|
@@ -11259,6 +12815,9 @@ function getPathInfo(path) {
|
|
|
11259
12815
|
if (typeof pathInfo !== "undefined") {
|
|
11260
12816
|
return pathInfo;
|
|
11261
12817
|
}
|
|
12818
|
+
if (path.indexOf(RECURSION_WILDCARD2) !== -1) {
|
|
12819
|
+
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.`);
|
|
12820
|
+
}
|
|
11262
12821
|
pathInfo = Object.freeze(new PathInfo(path));
|
|
11263
12822
|
_cache.set(path, pathInfo);
|
|
11264
12823
|
return pathInfo;
|
|
@@ -11382,9 +12941,6 @@ function didYouMean(input, candidates) {
|
|
|
11382
12941
|
return best !== null ? ` Did you mean "${best}"?` : "";
|
|
11383
12942
|
}
|
|
11384
12943
|
var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
11385
|
-
function raiseError2(message) {
|
|
11386
|
-
throw new Error(`[@wcstack/state] ${message}`);
|
|
11387
|
-
}
|
|
11388
12944
|
var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
|
|
11389
12945
|
"if",
|
|
11390
12946
|
"elseif",
|
|
@@ -12388,71 +13944,6 @@ function buildReferenceIndex(html, options = {}) {
|
|
|
12388
13944
|
// src/service/semanticValidator.ts
|
|
12389
13945
|
var STATE_UPDATED_CALLBACK = "$updatedCallback";
|
|
12390
13946
|
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
13947
|
function validateIndexArity(script, scriptStart, locale3) {
|
|
12457
13948
|
const msgs = getMessages(locale3);
|
|
12458
13949
|
const out = [];
|
|
@@ -12464,8 +13955,9 @@ function validateIndexArity(script, scriptStart, locale3) {
|
|
|
12464
13955
|
if (parsed === null) continue;
|
|
12465
13956
|
API_CALL.lastIndex = parsed.end;
|
|
12466
13957
|
if (parsed.args.length < 2) continue;
|
|
12467
|
-
const path =
|
|
13958
|
+
const path = literalString(parsed.args[0]);
|
|
12468
13959
|
if (path === null) continue;
|
|
13960
|
+
if (hasRecursionWildcard(path)) continue;
|
|
12469
13961
|
const actual = literalArrayLength(parsed.args[1]);
|
|
12470
13962
|
if (actual === null) continue;
|
|
12471
13963
|
const wildcardCount = countWildcardSegments(path);
|
|
@@ -12572,7 +14064,7 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
|
|
|
12572
14064
|
}
|
|
12573
14065
|
var TWO_WAY_PROPS = /* @__PURE__ */ new Set(["value", "checked"]);
|
|
12574
14066
|
var BRACKET_WRITE = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
12575
|
-
var
|
|
14067
|
+
var PRE_BRACKET_INCDEC2 = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
12576
14068
|
function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
12577
14069
|
const roots = /* @__PURE__ */ new Set();
|
|
12578
14070
|
const addPrefixes = (path, inclusive) => {
|
|
@@ -12608,7 +14100,7 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12608
14100
|
for (const block of blocks) {
|
|
12609
14101
|
if (block.mountPath !== null) addPrefixes(block.mountPath, true);
|
|
12610
14102
|
const scan = blankComments(block.content);
|
|
12611
|
-
for (const regex of [BRACKET_WRITE,
|
|
14103
|
+
for (const regex of [BRACKET_WRITE, PRE_BRACKET_INCDEC2]) {
|
|
12612
14104
|
regex.lastIndex = 0;
|
|
12613
14105
|
let match;
|
|
12614
14106
|
while ((match = regex.exec(scan)) !== null) addPrefixes(match[1], false);
|
|
@@ -12621,57 +14113,13 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12621
14113
|
const parsed = splitCallArgs(scan, call.index + call[0].length);
|
|
12622
14114
|
if (parsed === null) continue;
|
|
12623
14115
|
API_CALL.lastIndex = parsed.end;
|
|
12624
|
-
const path = parsed.args.length > 0 ?
|
|
14116
|
+
const path = parsed.args.length > 0 ? literalString(parsed.args[0]) : null;
|
|
12625
14117
|
if (path === null) continue;
|
|
12626
14118
|
if (api === "setAll" || parsed.args.length >= 3) addPrefixes(path, false);
|
|
12627
14119
|
}
|
|
12628
14120
|
}
|
|
12629
14121
|
return roots;
|
|
12630
14122
|
}
|
|
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
14123
|
var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
|
|
12676
14124
|
function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
|
|
12677
14125
|
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
@@ -13139,6 +14587,8 @@ function validateDocument(text, options = {}) {
|
|
|
13139
14587
|
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
13140
14588
|
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
13141
14589
|
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
14590
|
+
out.push(...validateScanDeclarations(text, stateTagName, locale3));
|
|
14591
|
+
out.push(...validateRecursion(text, stateTagName, locale3));
|
|
13142
14592
|
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
13143
14593
|
out.push(...validateMountAttributes(text, stateTagName, locale3));
|
|
13144
14594
|
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|