@wcstack/lint 2.3.0 → 2.5.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 +521 -19
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -147,6 +147,14 @@ 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",
|
|
150
158
|
// --- <wcs-state> script: $recursion declaration / `**` paths ---
|
|
151
159
|
// ランタイムと同じ code 語彙(@wcstack/state src/recursion/ が正本。
|
|
152
160
|
// docs/state-recursive-path-impl-plan.md §7)。静的に出すのは**パス文字列と宣言だけで
|
|
@@ -873,6 +881,7 @@ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
|
873
881
|
var STATE_ON_NAME = "$on";
|
|
874
882
|
var STATE_STREAMS_NAME = "$streams";
|
|
875
883
|
var STATE_WATCH_NAME = "$watch";
|
|
884
|
+
var STATE_SCAN_NAME = "$scan";
|
|
876
885
|
var STATE_RECURSION_NAME = "$recursion";
|
|
877
886
|
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
878
887
|
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
@@ -935,6 +944,7 @@ function getWcsManifest() {
|
|
|
935
944
|
STATE_ON_NAME,
|
|
936
945
|
STATE_STREAMS_NAME,
|
|
937
946
|
STATE_WATCH_NAME,
|
|
947
|
+
STATE_SCAN_NAME,
|
|
938
948
|
STATE_LIST_KEYS_NAME,
|
|
939
949
|
STATE_RECURSION_NAME,
|
|
940
950
|
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
@@ -1124,6 +1134,7 @@ var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
|
1124
1134
|
var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
|
|
1125
1135
|
var RESERVED_LIST_KEYS_KEY = "$listKeys";
|
|
1126
1136
|
var RESERVED_WATCH_KEY = "$watch";
|
|
1137
|
+
var RESERVED_SCAN_KEY = "$scan";
|
|
1127
1138
|
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
1128
1139
|
function analyzeStatePaths(scriptContent) {
|
|
1129
1140
|
const objectContent = extractDefaultExportObject(scriptContent);
|
|
@@ -1133,25 +1144,36 @@ function analyzeStatePaths(scriptContent) {
|
|
|
1133
1144
|
const pendingStreamValues = [];
|
|
1134
1145
|
const pendingListKeys = [];
|
|
1135
1146
|
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
1147
|
+
const effectiveDescriptors = effectiveTopLevelDescriptors(topLevelProps);
|
|
1136
1148
|
for (const prop of topLevelProps) {
|
|
1137
1149
|
if (prop.name.startsWith("$")) {
|
|
1138
1150
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
1139
1151
|
continue;
|
|
1140
1152
|
}
|
|
1153
|
+
const effective = effectiveDescriptors.get(prop.name);
|
|
1154
|
+
if (prop.kind === "getter" !== (effective !== "data")) {
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1141
1157
|
if (prop.kind === "method") {
|
|
1142
1158
|
paths.push({ path: prop.name, kind: "method" });
|
|
1143
1159
|
continue;
|
|
1144
1160
|
}
|
|
1145
1161
|
if (prop.kind === "getter") {
|
|
1146
|
-
|
|
1147
|
-
|
|
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;
|
|
1148
1170
|
}
|
|
1149
1171
|
continue;
|
|
1150
1172
|
}
|
|
1151
1173
|
pushDataPropertyPaths(prop, paths);
|
|
1152
1174
|
}
|
|
1153
1175
|
for (const streamValue of pendingStreamValues) {
|
|
1154
|
-
if (paths.some((p) => p.path === streamValue.name)) continue;
|
|
1176
|
+
if (paths.some((p) => p.path === streamValue.name && p.kind !== "eventToken")) continue;
|
|
1155
1177
|
pushDataPropertyPaths(streamValue, paths);
|
|
1156
1178
|
}
|
|
1157
1179
|
for (const listKeyEntry of pendingListKeys) {
|
|
@@ -1230,6 +1252,146 @@ function analyzeWatchEntries(scriptContent) {
|
|
|
1230
1252
|
function analyzeListKeyEntries(scriptContent) {
|
|
1231
1253
|
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1232
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
|
+
}
|
|
1233
1395
|
function hasDefaultExportObject(scriptContent) {
|
|
1234
1396
|
return locateDefaultExportObject(scriptContent) !== null;
|
|
1235
1397
|
}
|
|
@@ -1246,7 +1408,7 @@ function hasTopLevelSpread(scriptContent) {
|
|
|
1246
1408
|
}
|
|
1247
1409
|
return false;
|
|
1248
1410
|
}
|
|
1249
|
-
function analyzeObjectEntries(scriptContent, key) {
|
|
1411
|
+
function analyzeObjectEntries(scriptContent, key, allowEmptyKeys = false) {
|
|
1250
1412
|
const root = locateDefaultExportObject(scriptContent);
|
|
1251
1413
|
if (!root) return [];
|
|
1252
1414
|
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
@@ -1256,14 +1418,15 @@ function analyzeObjectEntries(scriptContent, key) {
|
|
|
1256
1418
|
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1257
1419
|
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1258
1420
|
const entries = [];
|
|
1259
|
-
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
|
|
1421
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value), allowEmptyKeys)) {
|
|
1260
1422
|
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1261
1423
|
entries.push({
|
|
1262
1424
|
key: entry.name,
|
|
1263
1425
|
start: innerStart + entry.nameStart,
|
|
1264
1426
|
end: innerStart + entry.nameEnd,
|
|
1265
1427
|
kind: entry.kind,
|
|
1266
|
-
value: entry.value
|
|
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)
|
|
1267
1430
|
});
|
|
1268
1431
|
}
|
|
1269
1432
|
return entries;
|
|
@@ -1311,20 +1474,27 @@ function isNonFunctionLiteral(value) {
|
|
|
1311
1474
|
return /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false|null|undefined)\b/.test(trimmed) || trimmed.startsWith("[") || trimmed.startsWith("{");
|
|
1312
1475
|
}
|
|
1313
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) {
|
|
1314
1483
|
const root = locateDefaultExportObject(scriptContent);
|
|
1315
1484
|
if (!root) return null;
|
|
1316
|
-
const
|
|
1317
|
-
if (!
|
|
1485
|
+
const declarationProp = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1486
|
+
if (!declarationProp || declarationProp.nameStart === void 0 || declarationProp.nameEnd === void 0) {
|
|
1318
1487
|
return null;
|
|
1319
1488
|
}
|
|
1320
|
-
const span = { start: root.start +
|
|
1321
|
-
if (
|
|
1489
|
+
const span = { start: root.start + declarationProp.nameStart, end: root.start + declarationProp.nameEnd };
|
|
1490
|
+
if (declarationProp.kind === "method") {
|
|
1322
1491
|
return span;
|
|
1323
1492
|
}
|
|
1324
|
-
if (
|
|
1325
|
-
const trimmed =
|
|
1493
|
+
if (declarationProp.kind !== "data" || !declarationProp.value) return null;
|
|
1494
|
+
const trimmed = declarationProp.value.trim();
|
|
1326
1495
|
if (trimmed.startsWith("{")) return null;
|
|
1327
1496
|
const scan = maskCommentsAndStrings(trimmed).trim();
|
|
1497
|
+
if (rejectArray && scan.startsWith("[") && isWholeBracketLiteral(scan)) return span;
|
|
1328
1498
|
const isArrowFunction = /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1329
1499
|
const isWholeLiteral = /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan);
|
|
1330
1500
|
if (!isArrowFunction && !isWholeLiteral) return null;
|
|
@@ -1347,6 +1517,19 @@ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKe
|
|
|
1347
1517
|
}
|
|
1348
1518
|
return;
|
|
1349
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
|
+
}
|
|
1350
1533
|
if (prop.name === RESERVED_COMMAND_TOKENS_KEY && prop.value) {
|
|
1351
1534
|
for (const name of extractStringArrayItems(prop.value)) {
|
|
1352
1535
|
paths.push({ path: `$command.${name}`, kind: "command" });
|
|
@@ -1520,6 +1703,9 @@ function findStreamInitialProperty(entryValue) {
|
|
|
1520
1703
|
const defProps = parseTopLevelProperties(extractObjectContent(entryValue));
|
|
1521
1704
|
return defProps.find((p) => p.kind === "data" && p.name === "initial");
|
|
1522
1705
|
}
|
|
1706
|
+
function isFlatScanOutputName(name) {
|
|
1707
|
+
return name.length > 0 && !name.startsWith("$") && !name.includes(".") && !name.includes("*");
|
|
1708
|
+
}
|
|
1523
1709
|
function extractStringArrayItems(value) {
|
|
1524
1710
|
if (!isArrayLiteral(value)) return [];
|
|
1525
1711
|
const items = [];
|
|
@@ -1531,6 +1717,19 @@ function extractStringArrayItems(value) {
|
|
|
1531
1717
|
return items;
|
|
1532
1718
|
}
|
|
1533
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
|
+
}
|
|
1534
1733
|
function pushDataPropertyPaths(prop, paths) {
|
|
1535
1734
|
pushDataPropertyPathsAt(prop.name, prop, paths, 0);
|
|
1536
1735
|
}
|
|
@@ -1609,10 +1808,10 @@ function locateDefaultExportObject(script) {
|
|
|
1609
1808
|
function extractDefaultExportObject(script) {
|
|
1610
1809
|
return locateDefaultExportObject(script)?.content ?? null;
|
|
1611
1810
|
}
|
|
1612
|
-
function parseTopLevelProperties(objectContent) {
|
|
1811
|
+
function parseTopLevelProperties(objectContent, allowEmptyDataKeys = false) {
|
|
1613
1812
|
const props = [];
|
|
1614
1813
|
const scan = maskCommentsAndStrings(objectContent);
|
|
1615
|
-
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;
|
|
1616
1815
|
let match;
|
|
1617
1816
|
while ((match = regex.exec(scan)) !== null) {
|
|
1618
1817
|
const indices = match.indices;
|
|
@@ -1658,7 +1857,7 @@ function parseTopLevelProperties(objectContent) {
|
|
|
1658
1857
|
continue;
|
|
1659
1858
|
}
|
|
1660
1859
|
const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
|
|
1661
|
-
if (propName) {
|
|
1860
|
+
if (propName !== void 0) {
|
|
1662
1861
|
const valueStartIndex = match.index + match[0].length;
|
|
1663
1862
|
const value = extractFullValue(objectContent, scan, valueStartIndex);
|
|
1664
1863
|
const jsdocType = extractJsDocType(objectContent, match.index);
|
|
@@ -2019,6 +2218,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
2019
2218
|
continue;
|
|
2020
2219
|
}
|
|
2021
2220
|
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
2221
|
+
const bindComponent = parseAttributeNames(wcsMatch.tagContent).has("bind-component");
|
|
2022
2222
|
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
2023
2223
|
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
2024
2224
|
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
@@ -2060,7 +2260,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
2060
2260
|
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
2061
2261
|
if (pos === 0) break;
|
|
2062
2262
|
}
|
|
2063
|
-
elements.push({ mountPath, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2263
|
+
elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2064
2264
|
pos = wcsEnd;
|
|
2065
2265
|
if (wcsCloseIdx !== -1) {
|
|
2066
2266
|
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
@@ -2150,6 +2350,15 @@ function findCloseTag(html, startPos, tagName) {
|
|
|
2150
2350
|
}
|
|
2151
2351
|
return -1;
|
|
2152
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
|
+
}
|
|
2153
2362
|
function extractAttribute(tagContent, attrName) {
|
|
2154
2363
|
const regex = new RegExp(
|
|
2155
2364
|
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
@@ -2389,6 +2598,33 @@ var ja = {
|
|
|
2389
2598
|
watchKeyEmptySegment: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`,
|
|
2390
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`,
|
|
2391
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`,
|
|
2392
2628
|
typeAnnotationIncompatible: (vt, rt) => `\u578B "${vt}" \u306F @type {${rt}} \u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093`,
|
|
2393
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`,
|
|
2394
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`,
|
|
@@ -2434,6 +2670,8 @@ var ja = {
|
|
|
2434
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`;
|
|
2435
2671
|
case "listKeys":
|
|
2436
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`;
|
|
2437
2675
|
default:
|
|
2438
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`;
|
|
2439
2677
|
}
|
|
@@ -2548,6 +2786,33 @@ var en = {
|
|
|
2548
2786
|
watchKeyEmptySegment: (k) => `$watch key "${k}" has an empty path segment`,
|
|
2549
2787
|
watchHandlerNotFunction: (k) => `The value of $watch entry "${k}" must be a function`,
|
|
2550
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"})`,
|
|
2551
2816
|
typeAnnotationIncompatible: (vt, rt) => `Type "${vt}" is not compatible with @type {${rt}}`,
|
|
2552
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}).`,
|
|
2553
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.`,
|
|
@@ -2593,6 +2858,8 @@ var en = {
|
|
|
2593
2858
|
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
2594
2859
|
case "listKeys":
|
|
2595
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 "*")`;
|
|
2596
2863
|
default:
|
|
2597
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`;
|
|
2598
2865
|
}
|
|
@@ -5630,6 +5897,240 @@ function validateEntry(entry, pathSet, paths, msgs) {
|
|
|
5630
5897
|
return null;
|
|
5631
5898
|
}
|
|
5632
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
|
+
|
|
5633
6134
|
// src/service/scriptCallArgs.ts
|
|
5634
6135
|
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
5635
6136
|
var TEMPLATE_NO_SUBST = /^\s*`((?:\\.|[^\\`$]|\$(?!\{))*)`\s*$/;
|
|
@@ -5760,7 +6261,7 @@ function validateRecursion(html, stateTagName = "wcs-state", locale3) {
|
|
|
5760
6261
|
const msgs = getMessages(locale3);
|
|
5761
6262
|
const out = [];
|
|
5762
6263
|
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
5763
|
-
const mounted =
|
|
6264
|
+
const mounted = element.bindComponent;
|
|
5764
6265
|
for (const block of element.scriptBlocks) {
|
|
5765
6266
|
if (!hasRecursionWildcard(block.content) && block.content.indexOf("$recursion") === -1) continue;
|
|
5766
6267
|
const declaration = analyzeRecursionDeclaration(block.content);
|
|
@@ -6068,7 +6569,7 @@ function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msg
|
|
|
6068
6569
|
const indexes = literalArrayLength(indexesArg);
|
|
6069
6570
|
if (indexes !== null && indexes > 0) {
|
|
6070
6571
|
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "prefix"));
|
|
6071
|
-
} else if (indexes === null &&
|
|
6572
|
+
} else if (indexes === null && isDefiniteNonArrayLiteral2(indexesArg)) {
|
|
6072
6573
|
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "notArray"));
|
|
6073
6574
|
}
|
|
6074
6575
|
}
|
|
@@ -6156,7 +6657,7 @@ function validateAssignments(script, offset2, spec, getterSuffixes, msgs, out) {
|
|
|
6156
6657
|
}
|
|
6157
6658
|
}
|
|
6158
6659
|
}
|
|
6159
|
-
function
|
|
6660
|
+
function isDefiniteNonArrayLiteral2(arg) {
|
|
6160
6661
|
const trimmed = arg.trim();
|
|
6161
6662
|
return trimmed === "null" || /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false)$/.test(trimmed) || trimmed.startsWith("{");
|
|
6162
6663
|
}
|
|
@@ -14086,6 +14587,7 @@ function validateDocument(text, options = {}) {
|
|
|
14086
14587
|
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
14087
14588
|
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
14088
14589
|
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
14590
|
+
out.push(...validateScanDeclarations(text, stateTagName, locale3));
|
|
14089
14591
|
out.push(...validateRecursion(text, stateTagName, locale3));
|
|
14090
14592
|
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
14091
14593
|
out.push(...validateMountAttributes(text, stateTagName, locale3));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcstack/lint",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Static-contract validator CLI (wcs-validate) for wcstack data-wcs bindings and wcstack.manifest.json sidecars. Thin npm wrapper around the wcstack-intellisense validator core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|