@wcstack/typescript 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +1 -1
- package/dist/schema-core.cjs +1087 -139
- package/dist/tsc-core.cjs +13 -1
- package/dist/wcs-schema.mjs +1 -1
- package/dist/wcs-tsc.mjs +1 -1
- package/package.json +1 -1
package/dist/schema-core.cjs
CHANGED
|
@@ -93,6 +93,36 @@ var WcsDiagnosticCode = {
|
|
|
93
93
|
// `$watch` のキーが状態定義に存在しない。バインディング側と違い黙って発火しない
|
|
94
94
|
// だけなので気づけない。severity は binding-path-missing に揃える(warning)。
|
|
95
95
|
WatchPathMissing: "wcs/watch-path-missing",
|
|
96
|
+
// --- <wcs-state> script: $recursion declaration / `**` paths ---
|
|
97
|
+
// ランタイムと同じ code 語彙(@wcstack/state src/recursion/ が正本。
|
|
98
|
+
// docs/state-recursive-path-impl-plan.md §7)。静的に出すのは**パス文字列と宣言だけで
|
|
99
|
+
// 決まる**ものに限る。データを見ないと決まらない wcs/recursion-shared-list /
|
|
100
|
+
// wcs/recursion-cycle / wcs/recursion-depth-exceeded、および評価時の呼び出し文脈に
|
|
101
|
+
// 依存する wcs/recursion-context は runtime 専用(静的側は出さない)。
|
|
102
|
+
//
|
|
103
|
+
// `**` を解釈しない場所へ `**` が渡った(data-wcs / mustache / $watch キー / $listKeys
|
|
104
|
+
// キー / $resolve / $postUpdate / $trackDependency / 代入)、または `$recursion` 宣言が
|
|
105
|
+
// 無いのに `**` を使った。runtime は PathInfo の不変条件として raiseError するか
|
|
106
|
+
//(API 経由)、getter を黙って無視する(宣言なしの `**` getter)。
|
|
107
|
+
RecursionUnsupported: "wcs/recursion-unsupported",
|
|
108
|
+
// 宣言済みアンカーと合致しない `**`(綴り違い・2 つ目の `**`)、または `**` の後ろが
|
|
109
|
+
// 整形されていない(空セグメント・`**` 直後の素の `*`)。
|
|
110
|
+
RecursionAnchor: "wcs/recursion-anchor",
|
|
111
|
+
// `$getAll` の添字の形が `**` に対して定義できない(非空の接頭辞 / 配列でない値)。
|
|
112
|
+
RecursionGetAllForm: "wcs/recursion-getall-form",
|
|
113
|
+
// `$setAll` の添字・値の形が `**` に対して定義できない
|
|
114
|
+
//(非空の接頭辞 / 添字省略 / mapper / spread)。
|
|
115
|
+
RecursionSetAllForm: "wcs/recursion-setall-form",
|
|
116
|
+
// ノード自身・子リスト・子ノード・子リストの length・多段の反復サブパスなら子リストへ
|
|
117
|
+
// 至る途中のオブジェクトへの一括書き込み(確定済みの子アドレスを壊す)。
|
|
118
|
+
RecursionStructuralWrite: "wcs/recursion-structural-write",
|
|
119
|
+
// 再帰 getter(およびその派生値の中)への書き込み。setter は初版では持てない。
|
|
120
|
+
RecursionReadonly: "wcs/recursion-readonly",
|
|
121
|
+
// `$recursion` 宣言そのもの、または `**` getter の宣言の形が不正(アンカー / 反復
|
|
122
|
+
// サブパスの形・複数宣言・setter・getter でない・ノード自身・構造を名指す接尾辞・
|
|
123
|
+
// 展開形と同名の具体 getter・ボリューム / マウント下での宣言)。
|
|
124
|
+
//(ランタイムは初期化時に raiseError)。wcs/watch-declaration-invalid の再帰版。
|
|
125
|
+
RecursionDeclarationInvalid: "wcs/recursion-declaration-invalid",
|
|
96
126
|
TypeAnnotation: "wcs/type-annotation",
|
|
97
127
|
TemplateSyntax: "wcs/template-syntax",
|
|
98
128
|
// --- <wcs-state> script: array reactivity hazards ---
|
|
@@ -1270,6 +1300,7 @@ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
|
1270
1300
|
var STATE_ON_NAME = "$on";
|
|
1271
1301
|
var STATE_STREAMS_NAME = "$streams";
|
|
1272
1302
|
var STATE_WATCH_NAME = "$watch";
|
|
1303
|
+
var STATE_RECURSION_NAME = "$recursion";
|
|
1273
1304
|
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
1274
1305
|
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
1275
1306
|
var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
@@ -1332,6 +1363,7 @@ function getWcsManifest() {
|
|
|
1332
1363
|
STATE_STREAMS_NAME,
|
|
1333
1364
|
STATE_WATCH_NAME,
|
|
1334
1365
|
STATE_LIST_KEYS_NAME,
|
|
1366
|
+
STATE_RECURSION_NAME,
|
|
1335
1367
|
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
1336
1368
|
STATE_STREAM_ERROR_NAMESPACE_NAME
|
|
1337
1369
|
]
|
|
@@ -1353,12 +1385,173 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
|
|
|
1353
1385
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
1354
1386
|
}));
|
|
1355
1387
|
|
|
1388
|
+
// src/service/recursionPaths.ts
|
|
1389
|
+
var RECURSION_WILDCARD = "**";
|
|
1390
|
+
var RECURSION_KEY = "$recursion";
|
|
1391
|
+
function hasRecursionWildcard(path) {
|
|
1392
|
+
return path.indexOf(RECURSION_WILDCARD) !== -1;
|
|
1393
|
+
}
|
|
1394
|
+
function checkNodePath(path) {
|
|
1395
|
+
if (typeof path !== "string" || path.length === 0) return "empty";
|
|
1396
|
+
const segments = path.split(".");
|
|
1397
|
+
if (segments.some((segment) => segment.length === 0)) return "emptySegment";
|
|
1398
|
+
if (segments.length < 2 || segments[segments.length - 1] !== "*") return "notElement";
|
|
1399
|
+
if (segments[0].startsWith("$")) return "reservedRoot";
|
|
1400
|
+
if (path.indexOf("#") !== -1) return "reservedMount";
|
|
1401
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1402
|
+
if (segments[i] === "*") return "midWildcard";
|
|
1403
|
+
if (segments[i] === RECURSION_WILDCARD) return "nestedRecursion";
|
|
1404
|
+
if (!isNaN(Number(segments[i]))) return "indexSegment";
|
|
1405
|
+
}
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
function makeRecursionSpec(anchor, repeat) {
|
|
1409
|
+
return Object.freeze({
|
|
1410
|
+
anchor,
|
|
1411
|
+
repeat,
|
|
1412
|
+
recursiveAnchor: anchor.slice(0, anchor.lastIndexOf(".")) + "." + RECURSION_WILDCARD,
|
|
1413
|
+
anchorList: anchor.slice(0, anchor.lastIndexOf(".")),
|
|
1414
|
+
repeatList: repeat.slice(0, repeat.lastIndexOf("."))
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
function splitRecursivePath(spec, path) {
|
|
1418
|
+
if (path === spec.recursiveAnchor) return "";
|
|
1419
|
+
if (!path.startsWith(spec.recursiveAnchor + ".")) return null;
|
|
1420
|
+
const suffix = path.slice(spec.recursiveAnchor.length);
|
|
1421
|
+
if (hasRecursionWildcard(suffix)) return null;
|
|
1422
|
+
const segments = suffix.slice(1).split(".");
|
|
1423
|
+
if (segments[0] === "*" || segments.some((segment) => segment.length === 0)) return null;
|
|
1424
|
+
return suffix;
|
|
1425
|
+
}
|
|
1426
|
+
function foldSuffixIndexes(suffix) {
|
|
1427
|
+
return suffix.length === 0 ? suffix : "." + indexSegmentsToWildcard(suffix.slice(1));
|
|
1428
|
+
}
|
|
1429
|
+
function foldRecursion(spec, path) {
|
|
1430
|
+
if (!path.startsWith(spec.anchor)) return null;
|
|
1431
|
+
const unit3 = "." + spec.repeat;
|
|
1432
|
+
let cursor = spec.anchor.length;
|
|
1433
|
+
let depth = 0;
|
|
1434
|
+
while (path.startsWith(unit3, cursor)) {
|
|
1435
|
+
cursor += unit3.length;
|
|
1436
|
+
depth++;
|
|
1437
|
+
}
|
|
1438
|
+
if (cursor !== path.length && path.charCodeAt(cursor) !== 46) return null;
|
|
1439
|
+
return { depth, rest: path.slice(cursor) };
|
|
1440
|
+
}
|
|
1441
|
+
function matchesRecursion(specs, path, has) {
|
|
1442
|
+
for (const spec of specs) {
|
|
1443
|
+
const folded = foldRecursion(spec, path);
|
|
1444
|
+
if (folded === null) continue;
|
|
1445
|
+
const unit3 = "." + spec.repeat;
|
|
1446
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1447
|
+
const rest = unit3.repeat(folded.depth - depth) + folded.rest;
|
|
1448
|
+
if (has(spec.anchor + rest)) return true;
|
|
1449
|
+
if (has(spec.recursiveAnchor + rest)) return true;
|
|
1450
|
+
for (let dot = rest.lastIndexOf("."); dot > 0; dot = rest.lastIndexOf(".", dot - 1)) {
|
|
1451
|
+
if (has(spec.recursiveAnchor + rest.slice(0, dot))) return true;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
function owningGetterSuffix(spec, getterSuffixes, path) {
|
|
1458
|
+
const pattern = indexSegmentsToWildcard(path);
|
|
1459
|
+
const folded = foldRecursion(spec, pattern);
|
|
1460
|
+
if (folded === null) return null;
|
|
1461
|
+
const unit3 = "." + spec.repeat;
|
|
1462
|
+
for (const suffix of getterSuffixes) {
|
|
1463
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1464
|
+
const expansion = spec.anchor + unit3.repeat(depth) + suffix;
|
|
1465
|
+
if (pattern === expansion || pattern.startsWith(expansion + ".")) return suffix;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return null;
|
|
1469
|
+
}
|
|
1470
|
+
function indexSegmentsToWildcard(path) {
|
|
1471
|
+
return path.split(".").map((segment) => segment !== "*" && !Number.isNaN(Number(segment)) ? "*" : segment).join(".");
|
|
1472
|
+
}
|
|
1473
|
+
function concreteExpansionSuffix(spec, getterSuffixes, key) {
|
|
1474
|
+
const folded = foldRecursion(spec, key);
|
|
1475
|
+
if (folded === null) return null;
|
|
1476
|
+
const unit3 = "." + spec.repeat;
|
|
1477
|
+
for (const suffix of getterSuffixes) {
|
|
1478
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1479
|
+
if (key === spec.anchor + unit3.repeat(depth) + suffix) return suffix;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
function collectRecursionSpecs(candidates) {
|
|
1485
|
+
const out = [];
|
|
1486
|
+
for (const candidate of candidates) {
|
|
1487
|
+
if (candidate.kind !== "recursionAnchor" || typeof candidate.repeat !== "string") continue;
|
|
1488
|
+
if (!candidate.path.endsWith("." + RECURSION_WILDCARD)) continue;
|
|
1489
|
+
const anchor = candidate.path.slice(0, candidate.path.length - RECURSION_WILDCARD.length) + "*";
|
|
1490
|
+
if (out.some((spec) => spec.anchor === anchor && spec.repeat === candidate.repeat)) continue;
|
|
1491
|
+
out.push(makeRecursionSpec(anchor, candidate.repeat));
|
|
1492
|
+
}
|
|
1493
|
+
return out;
|
|
1494
|
+
}
|
|
1495
|
+
function impliedStructurePaths(spec) {
|
|
1496
|
+
const out = [
|
|
1497
|
+
{ path: spec.anchorList, kind: "data", typeHint: "array" },
|
|
1498
|
+
{ path: spec.anchor, kind: "list" },
|
|
1499
|
+
{ path: `${spec.anchorList}.length`, kind: "data", typeHint: "number" }
|
|
1500
|
+
];
|
|
1501
|
+
const repeatSegments = spec.repeatList.split(".");
|
|
1502
|
+
for (let i = 1; i < repeatSegments.length; i++) {
|
|
1503
|
+
out.push({ path: `${spec.anchor}.${repeatSegments.slice(0, i).join(".")}`, kind: "data" });
|
|
1504
|
+
}
|
|
1505
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}`, kind: "data", typeHint: "array" });
|
|
1506
|
+
out.push({ path: `${spec.anchor}.${spec.repeat}`, kind: "list" });
|
|
1507
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}.length`, kind: "data", typeHint: "number" });
|
|
1508
|
+
return out;
|
|
1509
|
+
}
|
|
1510
|
+
function structuralWriteTarget(spec, suffix) {
|
|
1511
|
+
const unit3 = "." + spec.repeat;
|
|
1512
|
+
let rest = suffix;
|
|
1513
|
+
while (rest.startsWith(unit3)) rest = rest.slice(unit3.length);
|
|
1514
|
+
if (rest.length === 0) return "node";
|
|
1515
|
+
if (rest === "." + spec.repeatList + ".length") return "length";
|
|
1516
|
+
const segments = spec.repeatList.split(".");
|
|
1517
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
1518
|
+
if (rest === "." + segments.slice(0, i).join(".")) return i === segments.length ? "list" : "branch";
|
|
1519
|
+
}
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
function sameFamily(spec, a, b) {
|
|
1523
|
+
const unit3 = "." + spec.repeat;
|
|
1524
|
+
const shorter = a.length <= b.length ? a : b;
|
|
1525
|
+
const longer = a.length <= b.length ? b : a;
|
|
1526
|
+
if (!longer.endsWith(shorter)) return false;
|
|
1527
|
+
const gap = longer.slice(0, longer.length - shorter.length);
|
|
1528
|
+
if (gap.length === 0) return true;
|
|
1529
|
+
if (gap.length % unit3.length !== 0) return false;
|
|
1530
|
+
for (let cursor = 0; cursor < gap.length; cursor += unit3.length) {
|
|
1531
|
+
if (!gap.startsWith(unit3, cursor)) return false;
|
|
1532
|
+
}
|
|
1533
|
+
return true;
|
|
1534
|
+
}
|
|
1535
|
+
function conflictingGetterSuffix(spec, getterSuffixes, suffix) {
|
|
1536
|
+
for (const declared of getterSuffixes) {
|
|
1537
|
+
if (coversSuffix(spec, declared, suffix)) return declared;
|
|
1538
|
+
}
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
function coversSuffix(spec, familySuffix, suffix) {
|
|
1542
|
+
for (let end = suffix.length; end > 0; end = suffix.lastIndexOf(".", end - 1)) {
|
|
1543
|
+
if (sameFamily(spec, familySuffix, suffix.slice(0, end))) return true;
|
|
1544
|
+
}
|
|
1545
|
+
return false;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1356
1548
|
// src/service/stateAnalyzer.ts
|
|
1357
1549
|
var RESERVED_STREAMS_KEY = "$streams";
|
|
1358
1550
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
1359
1551
|
var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
|
|
1360
1552
|
var RESERVED_LIST_KEYS_KEY = "$listKeys";
|
|
1361
1553
|
var RESERVED_WATCH_KEY = "$watch";
|
|
1554
|
+
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
1362
1555
|
function analyzeStatePaths(scriptContent) {
|
|
1363
1556
|
const objectContent = extractDefaultExportObject(scriptContent);
|
|
1364
1557
|
if (!objectContent) return [];
|
|
@@ -1366,6 +1559,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
1366
1559
|
const topLevelProps = parseTopLevelProperties(objectContent);
|
|
1367
1560
|
const pendingStreamValues = [];
|
|
1368
1561
|
const pendingListKeys = [];
|
|
1562
|
+
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
1369
1563
|
for (const prop of topLevelProps) {
|
|
1370
1564
|
if (prop.name.startsWith("$")) {
|
|
1371
1565
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
@@ -1377,7 +1571,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
1377
1571
|
}
|
|
1378
1572
|
if (prop.kind === "getter") {
|
|
1379
1573
|
if (!paths.some((p) => p.path === prop.name)) {
|
|
1380
|
-
paths.push({ path: prop.name, kind: "computed" });
|
|
1574
|
+
paths.push({ path: prop.name, kind: hasRecursionWildcard(prop.name) ? "recursive" : "computed" });
|
|
1381
1575
|
}
|
|
1382
1576
|
continue;
|
|
1383
1577
|
}
|
|
@@ -1390,27 +1584,113 @@ function analyzeStatePaths(scriptContent) {
|
|
|
1390
1584
|
for (const listKeyEntry of pendingListKeys) {
|
|
1391
1585
|
pushListKeyPaths(listKeyEntry, paths);
|
|
1392
1586
|
}
|
|
1587
|
+
if (recursionSpec !== null) {
|
|
1588
|
+
if (!paths.some((p) => p.path === recursionSpec.recursiveAnchor && p.kind === "recursionAnchor")) {
|
|
1589
|
+
paths.push({ path: recursionSpec.recursiveAnchor, kind: "recursionAnchor", repeat: recursionSpec.repeat });
|
|
1590
|
+
}
|
|
1591
|
+
for (const implied of impliedStructurePaths(recursionSpec)) {
|
|
1592
|
+
if (paths.some((p) => p.path === implied.path)) continue;
|
|
1593
|
+
paths.push({ path: implied.path, kind: implied.kind, typeHint: implied.typeHint });
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1393
1596
|
collectRowShapesFromAssignments(scriptContent, paths);
|
|
1394
1597
|
return paths;
|
|
1395
1598
|
}
|
|
1599
|
+
function specFromRecursionValue(prop) {
|
|
1600
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value)) return null;
|
|
1601
|
+
const entries = parseTopLevelProperties(extractObjectContent(prop.value)).filter((e) => e.kind === "data");
|
|
1602
|
+
if (entries.length !== 1) return null;
|
|
1603
|
+
const anchor = entries[0].name;
|
|
1604
|
+
const repeat = extractStringLiteralValue(entries[0].value);
|
|
1605
|
+
if (repeat === null) return null;
|
|
1606
|
+
if (checkNodePath(anchor) !== null || checkNodePath(repeat) !== null) return null;
|
|
1607
|
+
return makeRecursionSpec(anchor, repeat);
|
|
1608
|
+
}
|
|
1609
|
+
function analyzeRecursionDeclaration(scriptContent) {
|
|
1610
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1611
|
+
if (!root) return null;
|
|
1612
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_RECURSION_KEY);
|
|
1613
|
+
if (!prop || prop.nameStart === void 0 || prop.nameEnd === void 0) return null;
|
|
1614
|
+
const span = { start: root.start + prop.nameStart, end: root.start + prop.nameEnd };
|
|
1615
|
+
if (prop.kind === "method") {
|
|
1616
|
+
return { ...span, notObject: true, objectLiteral: false, entries: [], spec: null };
|
|
1617
|
+
}
|
|
1618
|
+
if (prop.kind !== "data" || !prop.value || prop.valueStart === void 0) {
|
|
1619
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1620
|
+
}
|
|
1621
|
+
if (!isObjectLiteral(prop.value)) {
|
|
1622
|
+
const scan = maskCommentsAndStrings(prop.value).trim();
|
|
1623
|
+
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);
|
|
1624
|
+
return { ...span, notObject: definite, objectLiteral: false, entries: [], spec: null };
|
|
1625
|
+
}
|
|
1626
|
+
const objectContent = extractObjectContent(prop.value);
|
|
1627
|
+
if (hasUndecidableEntries(objectContent)) {
|
|
1628
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1629
|
+
}
|
|
1630
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1631
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1632
|
+
const entries = [];
|
|
1633
|
+
for (const entry of parseTopLevelProperties(objectContent)) {
|
|
1634
|
+
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1635
|
+
const valueStart = entry.valueStart === void 0 ? innerStart + entry.nameEnd : innerStart + entry.valueStart + (entry.value ? entry.value.length - entry.value.trimStart().length : 0);
|
|
1636
|
+
entries.push({
|
|
1637
|
+
anchor: entry.name,
|
|
1638
|
+
repeat: entry.kind === "data" ? extractStringLiteralValue(entry.value) : null,
|
|
1639
|
+
repeatDefinitelyNotString: entry.kind !== "data" || isDefiniteNonStringLiteral(entry.value),
|
|
1640
|
+
start: innerStart + entry.nameStart,
|
|
1641
|
+
end: innerStart + entry.nameEnd,
|
|
1642
|
+
valueStart,
|
|
1643
|
+
valueEnd: valueStart + (entry.value?.trim().length ?? 0)
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
return { ...span, notObject: false, objectLiteral: true, entries, spec: specFromRecursionValue(prop) };
|
|
1647
|
+
}
|
|
1396
1648
|
function analyzeWatchEntries(scriptContent) {
|
|
1649
|
+
return analyzeObjectEntries(scriptContent, RESERVED_WATCH_KEY).map((entry) => ({
|
|
1650
|
+
key: entry.key,
|
|
1651
|
+
start: entry.start,
|
|
1652
|
+
end: entry.end,
|
|
1653
|
+
// メソッド短縮記法は関数。data は値リテラルの形で判定し、識別子参照は疑わない。
|
|
1654
|
+
definitelyNotFunction: entry.kind === "data" && isNonFunctionLiteral(entry.value)
|
|
1655
|
+
}));
|
|
1656
|
+
}
|
|
1657
|
+
function analyzeListKeyEntries(scriptContent) {
|
|
1658
|
+
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1659
|
+
}
|
|
1660
|
+
function hasDefaultExportObject(scriptContent) {
|
|
1661
|
+
return locateDefaultExportObject(scriptContent) !== null;
|
|
1662
|
+
}
|
|
1663
|
+
function hasTopLevelSpread(scriptContent) {
|
|
1664
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1665
|
+
if (!root) return false;
|
|
1666
|
+
const scan = maskCommentsAndStrings(root.content);
|
|
1667
|
+
let depth = 0;
|
|
1668
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1669
|
+
const ch = scan[i];
|
|
1670
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
1671
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
1672
|
+
else if (depth === 0 && ch === "." && scan.startsWith("...", i)) return true;
|
|
1673
|
+
}
|
|
1674
|
+
return false;
|
|
1675
|
+
}
|
|
1676
|
+
function analyzeObjectEntries(scriptContent, key) {
|
|
1397
1677
|
const root = locateDefaultExportObject(scriptContent);
|
|
1398
1678
|
if (!root) return [];
|
|
1399
|
-
const
|
|
1400
|
-
if (!
|
|
1679
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1680
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
|
|
1401
1681
|
return [];
|
|
1402
1682
|
}
|
|
1403
|
-
const leading =
|
|
1404
|
-
const innerStart = root.start +
|
|
1683
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1684
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1405
1685
|
const entries = [];
|
|
1406
|
-
for (const entry of parseTopLevelProperties(extractObjectContent(
|
|
1686
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
|
|
1407
1687
|
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1408
1688
|
entries.push({
|
|
1409
1689
|
key: entry.name,
|
|
1410
1690
|
start: innerStart + entry.nameStart,
|
|
1411
1691
|
end: innerStart + entry.nameEnd,
|
|
1412
|
-
|
|
1413
|
-
|
|
1692
|
+
kind: entry.kind,
|
|
1693
|
+
value: entry.value
|
|
1414
1694
|
});
|
|
1415
1695
|
}
|
|
1416
1696
|
return entries;
|
|
@@ -1520,6 +1800,9 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1520
1800
|
if (listPath.length === 0 || segments.some((s) => s.length === 0) || segments[segments.length - 1] === "*") {
|
|
1521
1801
|
return;
|
|
1522
1802
|
}
|
|
1803
|
+
if (hasRecursionWildcard(listPath)) {
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1523
1806
|
const has = (path) => paths.some((p) => p.path === path);
|
|
1524
1807
|
if (!has(listPath)) paths.push({ path: listPath, kind: "data", typeHint: "array" });
|
|
1525
1808
|
if (!has(`${listPath}.*`)) paths.push({ path: `${listPath}.*`, kind: "list" });
|
|
@@ -1534,8 +1817,42 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1534
1817
|
}
|
|
1535
1818
|
function extractStringLiteralValue(value) {
|
|
1536
1819
|
if (!value) return null;
|
|
1537
|
-
const match = value.trim().match(/^["']([^"'\\]*)["']$/);
|
|
1538
|
-
|
|
1820
|
+
const match = value.trim().match(/^(?:["']([^"'\\]*)["']|`([^`\\$]*)`)$/);
|
|
1821
|
+
const literal2 = match ? match[1] ?? match[2] : null;
|
|
1822
|
+
return literal2 !== null && literal2 !== void 0 && literal2.length > 0 ? literal2 : null;
|
|
1823
|
+
}
|
|
1824
|
+
function hasUndecidableEntries(objectContent) {
|
|
1825
|
+
const scan = maskCommentsAndStrings(objectContent);
|
|
1826
|
+
let depth = 0;
|
|
1827
|
+
let atKey = true;
|
|
1828
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1829
|
+
const ch = scan[i];
|
|
1830
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
1831
|
+
if (depth === 0 && atKey && (ch === "[" || scan.startsWith("...", i))) return true;
|
|
1832
|
+
depth++;
|
|
1833
|
+
atKey = false;
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
1837
|
+
depth--;
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
if (depth !== 0) continue;
|
|
1841
|
+
if (ch === ",") {
|
|
1842
|
+
atKey = true;
|
|
1843
|
+
continue;
|
|
1844
|
+
}
|
|
1845
|
+
if (/\s/.test(ch)) continue;
|
|
1846
|
+
if (atKey && scan.startsWith("...", i)) return true;
|
|
1847
|
+
atKey = false;
|
|
1848
|
+
}
|
|
1849
|
+
return false;
|
|
1850
|
+
}
|
|
1851
|
+
function isDefiniteNonStringLiteral(value) {
|
|
1852
|
+
if (!value) return false;
|
|
1853
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1854
|
+
if (scan.length === 0) return false;
|
|
1855
|
+
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);
|
|
1539
1856
|
}
|
|
1540
1857
|
var ROW_ASSIGN = new RegExp(
|
|
1541
1858
|
String.raw`\bthis\s*(?:\.\s*([$\w]+)|\[\s*["']([^"']+)["']\s*\])\s*=(?![=>])\s*(?:(\[)|(?:[^;={}]|=>)*?\.\s*(?:concat|toSpliced|with)\s*(\())`,
|
|
@@ -2527,7 +2844,94 @@ var ja = {
|
|
|
2527
2844
|
default:
|
|
2528
2845
|
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`;
|
|
2529
2846
|
}
|
|
2530
|
-
}
|
|
2847
|
+
},
|
|
2848
|
+
recursionUnsupported: (p, where) => {
|
|
2849
|
+
switch (where) {
|
|
2850
|
+
case "binding":
|
|
2851
|
+
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`;
|
|
2852
|
+
case "watch":
|
|
2853
|
+
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`;
|
|
2854
|
+
case "resolve":
|
|
2855
|
+
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`;
|
|
2856
|
+
case "assignment":
|
|
2857
|
+
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`;
|
|
2858
|
+
case "postUpdate":
|
|
2859
|
+
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`;
|
|
2860
|
+
case "trackDependency":
|
|
2861
|
+
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`;
|
|
2862
|
+
case "listKeys":
|
|
2863
|
+
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`;
|
|
2864
|
+
default:
|
|
2865
|
+
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`;
|
|
2866
|
+
}
|
|
2867
|
+
},
|
|
2868
|
+
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`,
|
|
2869
|
+
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`,
|
|
2870
|
+
recursionSetAllForm: (p, problem) => {
|
|
2871
|
+
switch (problem) {
|
|
2872
|
+
case "prefix":
|
|
2873
|
+
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`;
|
|
2874
|
+
case "noIndexes":
|
|
2875
|
+
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`;
|
|
2876
|
+
case "mapper":
|
|
2877
|
+
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`;
|
|
2878
|
+
default:
|
|
2879
|
+
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`;
|
|
2880
|
+
}
|
|
2881
|
+
},
|
|
2882
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
2883
|
+
switch (target) {
|
|
2884
|
+
case "node":
|
|
2885
|
+
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`;
|
|
2886
|
+
case "branch":
|
|
2887
|
+
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`;
|
|
2888
|
+
case "length":
|
|
2889
|
+
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`;
|
|
2890
|
+
default:
|
|
2891
|
+
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`;
|
|
2892
|
+
}
|
|
2893
|
+
},
|
|
2894
|
+
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`,
|
|
2895
|
+
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`,
|
|
2896
|
+
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`,
|
|
2897
|
+
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`,
|
|
2898
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
2899
|
+
const subject = kind === "anchor" ? "$recursion \u306E\u30A2\u30F3\u30AB\u30FC" : "$recursion \u306E\u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9";
|
|
2900
|
+
switch (problem) {
|
|
2901
|
+
case "empty":
|
|
2902
|
+
return `${subject}\u306F\u7A7A\u3067\u306A\u3044\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`;
|
|
2903
|
+
case "emptySegment":
|
|
2904
|
+
return `${subject} "${path}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`;
|
|
2905
|
+
case "notElement":
|
|
2906
|
+
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`;
|
|
2907
|
+
case "reservedRoot":
|
|
2908
|
+
return `${subject} "${path}" \u306F "$" \u3067\u59CB\u3081\u3089\u308C\u307E\u305B\u3093\uFF08\u4E88\u7D04\u540D\u524D\u7A7A\u9593\uFF09`;
|
|
2909
|
+
case "reservedMount":
|
|
2910
|
+
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`;
|
|
2911
|
+
case "midWildcard":
|
|
2912
|
+
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`;
|
|
2913
|
+
case "indexSegment":
|
|
2914
|
+
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`;
|
|
2915
|
+
default:
|
|
2916
|
+
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`;
|
|
2917
|
+
}
|
|
2918
|
+
},
|
|
2919
|
+
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`,
|
|
2920
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
2921
|
+
switch (problem) {
|
|
2922
|
+
case "setter":
|
|
2923
|
+
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`;
|
|
2924
|
+
case "notGetter":
|
|
2925
|
+
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`;
|
|
2926
|
+
case "structural":
|
|
2927
|
+
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`;
|
|
2928
|
+
default:
|
|
2929
|
+
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`;
|
|
2930
|
+
}
|
|
2931
|
+
},
|
|
2932
|
+
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`,
|
|
2933
|
+
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`,
|
|
2934
|
+
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`
|
|
2531
2935
|
};
|
|
2532
2936
|
var EN_EXPECTED_LABEL = {
|
|
2533
2937
|
array: "an array-typed path",
|
|
@@ -2599,7 +3003,94 @@ var en = {
|
|
|
2599
3003
|
default:
|
|
2600
3004
|
return `"mount" path "${mountPath}" must not use reserved characters ($, #, @).`;
|
|
2601
3005
|
}
|
|
2602
|
-
}
|
|
3006
|
+
},
|
|
3007
|
+
recursionUnsupported: (p, where) => {
|
|
3008
|
+
switch (where) {
|
|
3009
|
+
case "binding":
|
|
3010
|
+
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`;
|
|
3011
|
+
case "watch":
|
|
3012
|
+
return `$watch key "${p}" cannot contain "**" \u2014 watching is defined against a concrete path (a fixed number of "*")`;
|
|
3013
|
+
case "resolve":
|
|
3014
|
+
return `$resolve("${p}") cannot take "**" \u2014 it accepts only an expanded concrete path with an exactly matching index tuple`;
|
|
3015
|
+
case "assignment":
|
|
3016
|
+
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`;
|
|
3017
|
+
case "postUpdate":
|
|
3018
|
+
return `$postUpdate("${p}") cannot take "**" \u2014 a notification is defined against a concrete path (a fixed number of "*")`;
|
|
3019
|
+
case "trackDependency":
|
|
3020
|
+
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
3021
|
+
case "listKeys":
|
|
3022
|
+
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")`;
|
|
3023
|
+
default:
|
|
3024
|
+
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`;
|
|
3025
|
+
}
|
|
3026
|
+
},
|
|
3027
|
+
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 "**")`,
|
|
3028
|
+
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`,
|
|
3029
|
+
recursionSetAllForm: (p, problem) => {
|
|
3030
|
+
switch (problem) {
|
|
3031
|
+
case "prefix":
|
|
3032
|
+
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`;
|
|
3033
|
+
case "noIndexes":
|
|
3034
|
+
return `$setAll("${p}", \u2026) with "**" requires an explicit empty indexes array ([]) \u2014 the write API takes no context`;
|
|
3035
|
+
case "mapper":
|
|
3036
|
+
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`;
|
|
3037
|
+
default:
|
|
3038
|
+
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`;
|
|
3039
|
+
}
|
|
3040
|
+
},
|
|
3041
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
3042
|
+
switch (target) {
|
|
3043
|
+
case "node":
|
|
3044
|
+
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`;
|
|
3045
|
+
case "branch":
|
|
3046
|
+
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`;
|
|
3047
|
+
case "length":
|
|
3048
|
+
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`;
|
|
3049
|
+
default:
|
|
3050
|
+
return `$setAll("${p}") writes the recursion structure itself (the "${repeatList}" list). This version broadcasts to leaf properties only`;
|
|
3051
|
+
}
|
|
3052
|
+
},
|
|
3053
|
+
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`,
|
|
3054
|
+
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`,
|
|
3055
|
+
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)`,
|
|
3056
|
+
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`,
|
|
3057
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
3058
|
+
const subject = kind === "anchor" ? "$recursion anchor" : "$recursion repeating sub-path";
|
|
3059
|
+
switch (problem) {
|
|
3060
|
+
case "empty":
|
|
3061
|
+
return `${subject} must be a non-empty string`;
|
|
3062
|
+
case "emptySegment":
|
|
3063
|
+
return `${subject} "${path}" must not contain empty path segments`;
|
|
3064
|
+
case "notElement":
|
|
3065
|
+
return `${subject} "${path}" must name a list element: a property path ending with ".*" (for example "nodes.*")`;
|
|
3066
|
+
case "reservedRoot":
|
|
3067
|
+
return `${subject} "${path}" must not start with "$" \u2014 that namespace is reserved`;
|
|
3068
|
+
case "reservedMount":
|
|
3069
|
+
return `${subject} "${path}" must not contain "#" \u2014 that segment is reserved for mounts`;
|
|
3070
|
+
case "midWildcard":
|
|
3071
|
+
return `${subject} "${path}" must have exactly one "*", at the end (wildcards in the middle are not supported in this version)`;
|
|
3072
|
+
case "indexSegment":
|
|
3073
|
+
return `${subject} "${path}" must not contain an index segment \u2014 the recursion is declared over the shape of the tree, not over one row`;
|
|
3074
|
+
default:
|
|
3075
|
+
return `${subject} "${path}" must not contain "**" \u2014 the declaration is what gives "**" its meaning`;
|
|
3076
|
+
}
|
|
3077
|
+
},
|
|
3078
|
+
recursionRepeatNotString: (anchor) => `$recursion entry "${anchor}" must map to the repeating sub-path as a string (for example "children.*")`,
|
|
3079
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
3080
|
+
switch (problem) {
|
|
3081
|
+
case "setter":
|
|
3082
|
+
return `Recursive setters are not supported in this version: "${key}". Declare a plain path setter, or write through the concrete path`;
|
|
3083
|
+
case "notGetter":
|
|
3084
|
+
return `"${key}" contains "**" but is not a getter. The recursion wildcard only names a family of computed paths`;
|
|
3085
|
+
case "structural":
|
|
3086
|
+
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")`;
|
|
3087
|
+
default:
|
|
3088
|
+
return `"${key}" names the recursive node itself. "**" names a computed path under a node (for example "${anchor}.total"), not the node`;
|
|
3089
|
+
}
|
|
3090
|
+
},
|
|
3091
|
+
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`,
|
|
3092
|
+
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`,
|
|
3093
|
+
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`
|
|
2603
3094
|
};
|
|
2604
3095
|
var CATALOGS = { ja, en };
|
|
2605
3096
|
function getMessages(locale3) {
|
|
@@ -2729,7 +3220,11 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2729
3220
|
}
|
|
2730
3221
|
if (checkPath) {
|
|
2731
3222
|
const schema = applicationSchema;
|
|
2732
|
-
const verdict =
|
|
3223
|
+
const verdict = hasRecursionWildcard(checkPath) ? {
|
|
3224
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
3225
|
+
message: msgs.recursionUnsupported(checkPath, "binding"),
|
|
3226
|
+
severity: "error"
|
|
3227
|
+
} : schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2733
3228
|
if (verdict) {
|
|
2734
3229
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2735
3230
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -2748,7 +3243,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2748
3243
|
const pathTrimmed = parsed.path.trim();
|
|
2749
3244
|
const prop = parsed.property.replace(/#.*$/, "");
|
|
2750
3245
|
const insideFor = isInsideForTemplate(html, attr.valueStart, attrName);
|
|
2751
|
-
if (pathTrimmed && !prop.startsWith("on")) {
|
|
3246
|
+
if (pathTrimmed && !prop.startsWith("on") && !hasRecursionWildcard(pathTrimmed)) {
|
|
2752
3247
|
if (!insideFor && pathTrimmed.includes("*")) {
|
|
2753
3248
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2754
3249
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -3028,11 +3523,16 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
|
|
|
3028
3523
|
}
|
|
3029
3524
|
return null;
|
|
3030
3525
|
}
|
|
3031
|
-
if (!scopedPathSet.has(checkPath)) {
|
|
3526
|
+
if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
|
|
3032
3527
|
return msgs.pathMissing(displayPath);
|
|
3033
3528
|
}
|
|
3034
3529
|
return null;
|
|
3035
3530
|
}
|
|
3531
|
+
function matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet) {
|
|
3532
|
+
const specs = collectRecursionSpecs(scopedPaths);
|
|
3533
|
+
if (specs.length === 0) return false;
|
|
3534
|
+
return matchesRecursion(specs, checkPath, (candidate) => scopedPathSet.has(candidate));
|
|
3535
|
+
}
|
|
3036
3536
|
function toMissingVerdict(message) {
|
|
3037
3537
|
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
3038
3538
|
}
|
|
@@ -3041,6 +3541,7 @@ function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scoped
|
|
|
3041
3541
|
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
3042
3542
|
}
|
|
3043
3543
|
if (scopedPathSet.has(checkPath)) return null;
|
|
3544
|
+
if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
|
|
3044
3545
|
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
3045
3546
|
if (resolution.kind === "nonexistent") {
|
|
3046
3547
|
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
@@ -3454,6 +3955,13 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
3454
3955
|
const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
|
|
3455
3956
|
const defaultSchema = applicationSchema;
|
|
3456
3957
|
const missingVerdict = (path, displayPath, pathSet2, scoped) => {
|
|
3958
|
+
if (hasRecursionWildcard(path)) {
|
|
3959
|
+
return {
|
|
3960
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
3961
|
+
severity: "error",
|
|
3962
|
+
message: msgs.recursionUnsupported(path, "binding")
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3457
3965
|
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
3458
3966
|
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
3459
3967
|
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
@@ -3586,7 +4094,7 @@ function isValidTemplatePath(path, pathSet, scopedPaths) {
|
|
|
3586
4094
|
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
3587
4095
|
return !hasNamespace || pathSet.has(path);
|
|
3588
4096
|
}
|
|
3589
|
-
return pathSet.has(path);
|
|
4097
|
+
return pathSet.has(path) || matchesRecursionCandidates(scopedPaths, path, pathSet);
|
|
3590
4098
|
}
|
|
3591
4099
|
|
|
3592
4100
|
// src/service/generated/builtinTags.generated.ts
|
|
@@ -5312,7 +5820,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
|
5312
5820
|
const paths = analyzeStatePaths(block.content);
|
|
5313
5821
|
const pathSet = new Set(paths.map((p) => p.path));
|
|
5314
5822
|
for (const entry of entries) {
|
|
5315
|
-
const diagnostic = validateEntry(entry, pathSet, msgs);
|
|
5823
|
+
const diagnostic = validateEntry(entry, pathSet, paths, msgs);
|
|
5316
5824
|
if (diagnostic === null) continue;
|
|
5317
5825
|
out.push({
|
|
5318
5826
|
code: diagnostic.code,
|
|
@@ -5325,7 +5833,7 @@ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
|
5325
5833
|
}
|
|
5326
5834
|
return out;
|
|
5327
5835
|
}
|
|
5328
|
-
function validateEntry(entry, pathSet, msgs) {
|
|
5836
|
+
function validateEntry(entry, pathSet, paths, msgs) {
|
|
5329
5837
|
const { key } = entry;
|
|
5330
5838
|
const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
|
|
5331
5839
|
if (key.includes(STATE_NAME_SEPARATOR)) {
|
|
@@ -5337,10 +5845,17 @@ function validateEntry(entry, pathSet, msgs) {
|
|
|
5337
5845
|
if (key.split(".").some((segment) => segment.length === 0)) {
|
|
5338
5846
|
return invalid(msgs.watchKeyEmptySegment(key));
|
|
5339
5847
|
}
|
|
5848
|
+
if (hasRecursionWildcard(key)) {
|
|
5849
|
+
return {
|
|
5850
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
5851
|
+
message: msgs.recursionUnsupported(key, "watch"),
|
|
5852
|
+
severity: "error"
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5340
5855
|
if (entry.definitelyNotFunction) {
|
|
5341
5856
|
return invalid(msgs.watchHandlerNotFunction(key));
|
|
5342
5857
|
}
|
|
5343
|
-
if (pathSet.size > 0 && !pathSet.has(key)) {
|
|
5858
|
+
if (pathSet.size > 0 && !pathSet.has(key) && !matchesRecursion(collectRecursionSpecs(paths), key, (p) => pathSet.has(p))) {
|
|
5344
5859
|
return {
|
|
5345
5860
|
code: WcsDiagnosticCode.WatchPathMissing,
|
|
5346
5861
|
message: msgs.watchPathMissing(key),
|
|
@@ -5350,6 +5865,542 @@ function validateEntry(entry, pathSet, msgs) {
|
|
|
5350
5865
|
return null;
|
|
5351
5866
|
}
|
|
5352
5867
|
|
|
5868
|
+
// src/service/scriptCallArgs.ts
|
|
5869
|
+
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
5870
|
+
var TEMPLATE_NO_SUBST = /^\s*`((?:\\.|[^\\`$]|\$(?!\{))*)`\s*$/;
|
|
5871
|
+
function splitCallArgs(source, open) {
|
|
5872
|
+
const args = [];
|
|
5873
|
+
const starts = [];
|
|
5874
|
+
let depth = 0;
|
|
5875
|
+
let argStart = open;
|
|
5876
|
+
let i = open;
|
|
5877
|
+
while (i < source.length) {
|
|
5878
|
+
const ch = source[i];
|
|
5879
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
5880
|
+
const quote = ch;
|
|
5881
|
+
i++;
|
|
5882
|
+
while (i < source.length) {
|
|
5883
|
+
if (source[i] === "\\") {
|
|
5884
|
+
i += 2;
|
|
5885
|
+
continue;
|
|
5886
|
+
}
|
|
5887
|
+
if (source[i] === quote) {
|
|
5888
|
+
i++;
|
|
5889
|
+
break;
|
|
5890
|
+
}
|
|
5891
|
+
i++;
|
|
5892
|
+
}
|
|
5893
|
+
continue;
|
|
5894
|
+
}
|
|
5895
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
5896
|
+
depth++;
|
|
5897
|
+
i++;
|
|
5898
|
+
continue;
|
|
5899
|
+
}
|
|
5900
|
+
if (ch === ")" && depth === 0) {
|
|
5901
|
+
args.push(source.slice(argStart, i));
|
|
5902
|
+
starts.push(argStart);
|
|
5903
|
+
return { args, starts, end: i + 1 };
|
|
5904
|
+
}
|
|
5905
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
5906
|
+
depth--;
|
|
5907
|
+
i++;
|
|
5908
|
+
continue;
|
|
5909
|
+
}
|
|
5910
|
+
if (ch === "," && depth === 0) {
|
|
5911
|
+
args.push(source.slice(argStart, i));
|
|
5912
|
+
starts.push(argStart);
|
|
5913
|
+
argStart = i + 1;
|
|
5914
|
+
i++;
|
|
5915
|
+
continue;
|
|
5916
|
+
}
|
|
5917
|
+
i++;
|
|
5918
|
+
}
|
|
5919
|
+
return null;
|
|
5920
|
+
}
|
|
5921
|
+
function literalString(arg) {
|
|
5922
|
+
const match = STRING_LITERAL.exec(arg);
|
|
5923
|
+
if (match !== null) return match[2];
|
|
5924
|
+
const template = TEMPLATE_NO_SUBST.exec(arg);
|
|
5925
|
+
return template === null ? null : template[1];
|
|
5926
|
+
}
|
|
5927
|
+
function literalArrayLength(arg) {
|
|
5928
|
+
const trimmed = arg.trim();
|
|
5929
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
5930
|
+
const inner = trimmed.slice(1, -1);
|
|
5931
|
+
if (inner.trim().length === 0) return 0;
|
|
5932
|
+
if (/(^|[^.])\.\.\./.test(inner)) return null;
|
|
5933
|
+
const parts = splitCallArgs(`${inner})`, 0);
|
|
5934
|
+
if (parts === null) return null;
|
|
5935
|
+
return parts.args.filter((part) => part.trim().length > 0).length;
|
|
5936
|
+
}
|
|
5937
|
+
function blankComments(source) {
|
|
5938
|
+
const out = source.split("");
|
|
5939
|
+
let i = 0;
|
|
5940
|
+
while (i < source.length) {
|
|
5941
|
+
const ch = source[i];
|
|
5942
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
5943
|
+
const quote = ch;
|
|
5944
|
+
i++;
|
|
5945
|
+
while (i < source.length) {
|
|
5946
|
+
if (source[i] === "\\") {
|
|
5947
|
+
i += 2;
|
|
5948
|
+
continue;
|
|
5949
|
+
}
|
|
5950
|
+
if (source[i] === quote) {
|
|
5951
|
+
i++;
|
|
5952
|
+
break;
|
|
5953
|
+
}
|
|
5954
|
+
i++;
|
|
5955
|
+
}
|
|
5956
|
+
continue;
|
|
5957
|
+
}
|
|
5958
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
5959
|
+
while (i < source.length && source[i] !== "\n") {
|
|
5960
|
+
out[i] = " ";
|
|
5961
|
+
i++;
|
|
5962
|
+
}
|
|
5963
|
+
continue;
|
|
5964
|
+
}
|
|
5965
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
5966
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
5967
|
+
out[i] = " ";
|
|
5968
|
+
i++;
|
|
5969
|
+
}
|
|
5970
|
+
if (i < source.length) {
|
|
5971
|
+
out[i] = " ";
|
|
5972
|
+
out[i + 1] = " ";
|
|
5973
|
+
i += 2;
|
|
5974
|
+
}
|
|
5975
|
+
continue;
|
|
5976
|
+
}
|
|
5977
|
+
i++;
|
|
5978
|
+
}
|
|
5979
|
+
return out.join("");
|
|
5980
|
+
}
|
|
5981
|
+
function createApiCallRegex(apis) {
|
|
5982
|
+
return new RegExp(`\\.\\s*\\$(${apis.join("|")})\\s*\\(`, "g");
|
|
5983
|
+
}
|
|
5984
|
+
|
|
5985
|
+
// src/service/recursionValidator.ts
|
|
5986
|
+
var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "trackDependency"];
|
|
5987
|
+
var UNSUPPORTED_API_SITE = {
|
|
5988
|
+
$resolve: "resolve",
|
|
5989
|
+
$postUpdate: "postUpdate",
|
|
5990
|
+
$trackDependency: "trackDependency"
|
|
5991
|
+
};
|
|
5992
|
+
var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
5993
|
+
var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
5994
|
+
function validateRecursion(html, stateTagName = "wcs-state", locale3) {
|
|
5995
|
+
const msgs = getMessages(locale3);
|
|
5996
|
+
const out = [];
|
|
5997
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
5998
|
+
const mounted = /\sbind-component\b/i.test(html.slice(element.tagStart, element.tagEnd));
|
|
5999
|
+
for (const block of element.scriptBlocks) {
|
|
6000
|
+
if (!hasRecursionWildcard(block.content) && block.content.indexOf("$recursion") === -1) continue;
|
|
6001
|
+
const declaration = analyzeRecursionDeclaration(block.content);
|
|
6002
|
+
let spec = null;
|
|
6003
|
+
let undeclared = false;
|
|
6004
|
+
let getterSuffixes = [];
|
|
6005
|
+
if (block.mountPath !== null) {
|
|
6006
|
+
validateVolumeBlock(block.content, block.contentStart, block.mountPath, declaration, msgs, out);
|
|
6007
|
+
} else if (mounted) {
|
|
6008
|
+
validateMountedComponentBlock(block.content, block.contentStart, declaration, msgs, out);
|
|
6009
|
+
} else {
|
|
6010
|
+
spec = validateDeclaration(declaration, block.contentStart, msgs, out);
|
|
6011
|
+
undeclared = declaration === null && hasDefaultExportObject(block.content) && !hasTopLevelSpread(block.content);
|
|
6012
|
+
getterSuffixes = validateRecursiveGetters(block.content, block.contentStart, spec, undeclared, msgs, out);
|
|
6013
|
+
}
|
|
6014
|
+
validateListKeys(block.content, block.contentStart, msgs, out);
|
|
6015
|
+
validateApiCalls(block.content, block.contentStart, spec, getterSuffixes, undeclared, msgs, out);
|
|
6016
|
+
validateAssignments(block.content, block.contentStart, spec, getterSuffixes, msgs, out);
|
|
6017
|
+
}
|
|
6018
|
+
}
|
|
6019
|
+
return out;
|
|
6020
|
+
}
|
|
6021
|
+
function push(out, code, start, end, message, severity = "error") {
|
|
6022
|
+
out.push({ code, start, end, message, severity });
|
|
6023
|
+
}
|
|
6024
|
+
function validateVolumeBlock(script, offset2, mountPath, declaration, msgs, out) {
|
|
6025
|
+
if (declaration !== null) {
|
|
6026
|
+
push(
|
|
6027
|
+
out,
|
|
6028
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6029
|
+
offset2 + declaration.start,
|
|
6030
|
+
offset2 + declaration.end,
|
|
6031
|
+
msgs.recursionInVolume("$recursion", mountPath)
|
|
6032
|
+
);
|
|
6033
|
+
}
|
|
6034
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6035
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6036
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
6037
|
+
seen.add(span.name);
|
|
6038
|
+
push(
|
|
6039
|
+
out,
|
|
6040
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6041
|
+
offset2 + span.start,
|
|
6042
|
+
offset2 + span.end,
|
|
6043
|
+
msgs.recursionInVolume(`"${span.name}"`, mountPath)
|
|
6044
|
+
);
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
function validateDeclaration(declaration, offset2, msgs, out) {
|
|
6048
|
+
if (declaration === null) return null;
|
|
6049
|
+
const code = WcsDiagnosticCode.RecursionDeclarationInvalid;
|
|
6050
|
+
if (declaration.notObject) {
|
|
6051
|
+
push(out, code, offset2 + declaration.start, offset2 + declaration.end, msgs.recursionNotObject());
|
|
6052
|
+
return null;
|
|
6053
|
+
}
|
|
6054
|
+
if (declaration.entries.length !== 1) {
|
|
6055
|
+
if (!declaration.objectLiteral) return null;
|
|
6056
|
+
push(
|
|
6057
|
+
out,
|
|
6058
|
+
code,
|
|
6059
|
+
offset2 + declaration.start,
|
|
6060
|
+
offset2 + declaration.end,
|
|
6061
|
+
msgs.recursionAnchorCount(declaration.entries.length)
|
|
6062
|
+
);
|
|
6063
|
+
return null;
|
|
6064
|
+
}
|
|
6065
|
+
const entry = declaration.entries[0];
|
|
6066
|
+
const anchorProblem = checkNodePath(entry.anchor);
|
|
6067
|
+
if (anchorProblem !== null) {
|
|
6068
|
+
push(
|
|
6069
|
+
out,
|
|
6070
|
+
code,
|
|
6071
|
+
offset2 + entry.start,
|
|
6072
|
+
offset2 + entry.end,
|
|
6073
|
+
msgs.recursionNodePathInvalid("anchor", entry.anchor, anchorProblem)
|
|
6074
|
+
);
|
|
6075
|
+
return null;
|
|
6076
|
+
}
|
|
6077
|
+
if (entry.repeat === null) {
|
|
6078
|
+
if (entry.repeatDefinitelyNotString) {
|
|
6079
|
+
push(
|
|
6080
|
+
out,
|
|
6081
|
+
code,
|
|
6082
|
+
offset2 + entry.valueStart,
|
|
6083
|
+
offset2 + entry.valueEnd,
|
|
6084
|
+
msgs.recursionRepeatNotString(entry.anchor)
|
|
6085
|
+
);
|
|
6086
|
+
}
|
|
6087
|
+
return null;
|
|
6088
|
+
}
|
|
6089
|
+
const repeatProblem = checkNodePath(entry.repeat);
|
|
6090
|
+
if (repeatProblem !== null) {
|
|
6091
|
+
push(
|
|
6092
|
+
out,
|
|
6093
|
+
code,
|
|
6094
|
+
offset2 + entry.valueStart,
|
|
6095
|
+
offset2 + entry.valueEnd,
|
|
6096
|
+
msgs.recursionNodePathInvalid("repeat", entry.repeat, repeatProblem)
|
|
6097
|
+
);
|
|
6098
|
+
return null;
|
|
6099
|
+
}
|
|
6100
|
+
return makeRecursionSpec(entry.anchor, entry.repeat);
|
|
6101
|
+
}
|
|
6102
|
+
function validateRecursiveGetters(script, offset2, spec, undeclared, msgs, out) {
|
|
6103
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6104
|
+
const spans = analyzeDeclarationSpans(script).filter((s) => hasRecursionWildcard(s.name)).filter((s) => seen.has(s.name) ? false : (seen.add(s.name), true));
|
|
6105
|
+
if (spans.length === 0) return [];
|
|
6106
|
+
const setterNames = new Set(
|
|
6107
|
+
analyzeCallableBodies(script).filter((c) => c.accessor === "set").map((c) => c.name)
|
|
6108
|
+
);
|
|
6109
|
+
const suffixes = [];
|
|
6110
|
+
const accepted = [];
|
|
6111
|
+
for (const span of spans) {
|
|
6112
|
+
const start = offset2 + span.start;
|
|
6113
|
+
const end = offset2 + span.end;
|
|
6114
|
+
if (spec === null) {
|
|
6115
|
+
if (undeclared) {
|
|
6116
|
+
push(
|
|
6117
|
+
out,
|
|
6118
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6119
|
+
start,
|
|
6120
|
+
end,
|
|
6121
|
+
msgs.recursionUnsupported(span.name, "undeclared"),
|
|
6122
|
+
"warning"
|
|
6123
|
+
);
|
|
6124
|
+
}
|
|
6125
|
+
continue;
|
|
6126
|
+
}
|
|
6127
|
+
if (span.kind !== "getter") {
|
|
6128
|
+
push(
|
|
6129
|
+
out,
|
|
6130
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6131
|
+
start,
|
|
6132
|
+
end,
|
|
6133
|
+
msgs.recursionGetterInvalid(span.name, "notGetter", spec.recursiveAnchor)
|
|
6134
|
+
);
|
|
6135
|
+
continue;
|
|
6136
|
+
}
|
|
6137
|
+
if (setterNames.has(span.name)) {
|
|
6138
|
+
push(
|
|
6139
|
+
out,
|
|
6140
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6141
|
+
start,
|
|
6142
|
+
end,
|
|
6143
|
+
msgs.recursionGetterInvalid(span.name, "setter", spec.recursiveAnchor)
|
|
6144
|
+
);
|
|
6145
|
+
continue;
|
|
6146
|
+
}
|
|
6147
|
+
const suffix = splitRecursivePath(spec, span.name);
|
|
6148
|
+
if (suffix === null) {
|
|
6149
|
+
push(
|
|
6150
|
+
out,
|
|
6151
|
+
WcsDiagnosticCode.RecursionAnchor,
|
|
6152
|
+
start,
|
|
6153
|
+
end,
|
|
6154
|
+
msgs.recursionAnchorMismatch(span.name, spec.recursiveAnchor)
|
|
6155
|
+
);
|
|
6156
|
+
continue;
|
|
6157
|
+
}
|
|
6158
|
+
if (suffix.length === 0) {
|
|
6159
|
+
push(
|
|
6160
|
+
out,
|
|
6161
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6162
|
+
start,
|
|
6163
|
+
end,
|
|
6164
|
+
msgs.recursionGetterInvalid(span.name, "nodeItself", spec.recursiveAnchor)
|
|
6165
|
+
);
|
|
6166
|
+
continue;
|
|
6167
|
+
}
|
|
6168
|
+
if (structuralWriteTarget(spec, foldSuffixIndexes(suffix)) !== null) {
|
|
6169
|
+
push(
|
|
6170
|
+
out,
|
|
6171
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6172
|
+
start,
|
|
6173
|
+
end,
|
|
6174
|
+
msgs.recursionGetterInvalid(span.name, "structural", spec.recursiveAnchor)
|
|
6175
|
+
);
|
|
6176
|
+
continue;
|
|
6177
|
+
}
|
|
6178
|
+
const collision = accepted.find((other) => sameFamily(spec, other.suffix, suffix));
|
|
6179
|
+
if (collision !== void 0) {
|
|
6180
|
+
push(
|
|
6181
|
+
out,
|
|
6182
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6183
|
+
start,
|
|
6184
|
+
end,
|
|
6185
|
+
msgs.recursionGetterCollision(collision.name, span.name, spec.repeat)
|
|
6186
|
+
);
|
|
6187
|
+
continue;
|
|
6188
|
+
}
|
|
6189
|
+
accepted.push({ name: span.name, suffix, start, end });
|
|
6190
|
+
suffixes.push(suffix);
|
|
6191
|
+
}
|
|
6192
|
+
if (spec !== null && suffixes.length > 0) {
|
|
6193
|
+
const reported = /* @__PURE__ */ new Set();
|
|
6194
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6195
|
+
if (hasRecursionWildcard(span.name) || reported.has(span.name)) continue;
|
|
6196
|
+
const suffix = concreteExpansionSuffix(spec, suffixes, span.name);
|
|
6197
|
+
if (suffix === null) continue;
|
|
6198
|
+
reported.add(span.name);
|
|
6199
|
+
push(
|
|
6200
|
+
out,
|
|
6201
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6202
|
+
offset2 + span.start,
|
|
6203
|
+
offset2 + span.end,
|
|
6204
|
+
msgs.recursionConcreteCollision(span.name, spec.recursiveAnchor + suffix)
|
|
6205
|
+
);
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
return suffixes;
|
|
6209
|
+
}
|
|
6210
|
+
function validateMountedComponentBlock(script, offset2, declaration, msgs, out) {
|
|
6211
|
+
if (declaration !== null) {
|
|
6212
|
+
push(
|
|
6213
|
+
out,
|
|
6214
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6215
|
+
offset2 + declaration.start,
|
|
6216
|
+
offset2 + declaration.end,
|
|
6217
|
+
msgs.recursionInMountedComponent("$recursion"),
|
|
6218
|
+
"warning"
|
|
6219
|
+
);
|
|
6220
|
+
}
|
|
6221
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6222
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6223
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
6224
|
+
seen.add(span.name);
|
|
6225
|
+
push(
|
|
6226
|
+
out,
|
|
6227
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6228
|
+
offset2 + span.start,
|
|
6229
|
+
offset2 + span.end,
|
|
6230
|
+
msgs.recursionInMountedComponent(`"${span.name}"`),
|
|
6231
|
+
"warning"
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
function validateListKeys(script, offset2, msgs, out) {
|
|
6236
|
+
for (const entry of analyzeListKeyEntries(script)) {
|
|
6237
|
+
if (!hasRecursionWildcard(entry.key)) continue;
|
|
6238
|
+
push(
|
|
6239
|
+
out,
|
|
6240
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6241
|
+
offset2 + entry.start,
|
|
6242
|
+
offset2 + entry.end,
|
|
6243
|
+
msgs.recursionUnsupported(entry.key, "listKeys")
|
|
6244
|
+
);
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msgs, out) {
|
|
6248
|
+
const scan = blankComments(script);
|
|
6249
|
+
const regex = createApiCallRegex(RECURSION_APIS);
|
|
6250
|
+
let match;
|
|
6251
|
+
while ((match = regex.exec(scan)) !== null) {
|
|
6252
|
+
const api = `$${match[1]}`;
|
|
6253
|
+
const parsed = splitCallArgs(scan, match.index + match[0].length);
|
|
6254
|
+
if (parsed === null) continue;
|
|
6255
|
+
regex.lastIndex = parsed.end;
|
|
6256
|
+
if (parsed.args.length === 0) continue;
|
|
6257
|
+
const pathArg = parsed.args[0];
|
|
6258
|
+
const path = literalString(pathArg);
|
|
6259
|
+
if (path === null) continue;
|
|
6260
|
+
const leading = pathArg.length - pathArg.trimStart().length;
|
|
6261
|
+
const start = offset2 + parsed.starts[0] + leading;
|
|
6262
|
+
const end = offset2 + parsed.starts[0] + pathArg.trimEnd().length;
|
|
6263
|
+
if (!hasRecursionWildcard(path)) {
|
|
6264
|
+
const writes = api === "$setAll" || api === "$resolve" && parsed.args.length >= 3;
|
|
6265
|
+
if (writes && spec !== null) {
|
|
6266
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6267
|
+
if (owning !== null) {
|
|
6268
|
+
push(
|
|
6269
|
+
out,
|
|
6270
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6271
|
+
start,
|
|
6272
|
+
end,
|
|
6273
|
+
msgs.recursionReadonly(`${api}("${path}")`, spec.recursiveAnchor + owning)
|
|
6274
|
+
);
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
continue;
|
|
6278
|
+
}
|
|
6279
|
+
if (api in UNSUPPORTED_API_SITE) {
|
|
6280
|
+
push(
|
|
6281
|
+
out,
|
|
6282
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6283
|
+
start,
|
|
6284
|
+
end,
|
|
6285
|
+
msgs.recursionUnsupported(path, UNSUPPORTED_API_SITE[api])
|
|
6286
|
+
);
|
|
6287
|
+
continue;
|
|
6288
|
+
}
|
|
6289
|
+
if (spec === null) {
|
|
6290
|
+
if (undeclared) {
|
|
6291
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "undeclared"));
|
|
6292
|
+
}
|
|
6293
|
+
continue;
|
|
6294
|
+
}
|
|
6295
|
+
const suffix = splitRecursivePath(spec, path);
|
|
6296
|
+
if (suffix === null) {
|
|
6297
|
+
push(out, WcsDiagnosticCode.RecursionAnchor, start, end, msgs.recursionAnchorMismatch(path, spec.recursiveAnchor));
|
|
6298
|
+
continue;
|
|
6299
|
+
}
|
|
6300
|
+
if (api === "$getAll") {
|
|
6301
|
+
if (parsed.args.length > 1) {
|
|
6302
|
+
const indexesArg = parsed.args[1];
|
|
6303
|
+
const indexes = literalArrayLength(indexesArg);
|
|
6304
|
+
if (indexes !== null && indexes > 0) {
|
|
6305
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "prefix"));
|
|
6306
|
+
} else if (indexes === null && isDefiniteNonArrayLiteral(indexesArg)) {
|
|
6307
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "notArray"));
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
continue;
|
|
6311
|
+
}
|
|
6312
|
+
validateSetAllForm(path, suffix, parsed.args, spec, getterSuffixes, start, end, msgs, out);
|
|
6313
|
+
}
|
|
6314
|
+
}
|
|
6315
|
+
function validateSetAllForm(path, suffix, args, spec, getterSuffixes, start, end, msgs, out) {
|
|
6316
|
+
const formCode = WcsDiagnosticCode.RecursionSetAllForm;
|
|
6317
|
+
const indexesArg = args.length > 1 ? args[1].trim() : "";
|
|
6318
|
+
if (args.length < 2 || indexesArg === "undefined" || indexesArg === "null") {
|
|
6319
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "noIndexes"));
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
6322
|
+
const indexes = literalArrayLength(args[1]);
|
|
6323
|
+
if (indexes !== null && indexes > 0) {
|
|
6324
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "prefix"));
|
|
6325
|
+
return;
|
|
6326
|
+
}
|
|
6327
|
+
if (args.length > 2 && isFunctionLiteral(args[2])) {
|
|
6328
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "mapper"));
|
|
6329
|
+
return;
|
|
6330
|
+
}
|
|
6331
|
+
if (args.length > 3 && /\bspread\s*:\s*true\b/.test(args[3])) {
|
|
6332
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "spread"));
|
|
6333
|
+
return;
|
|
6334
|
+
}
|
|
6335
|
+
const checkedSuffix = foldSuffixIndexes(suffix);
|
|
6336
|
+
const structural = structuralWriteTarget(spec, checkedSuffix);
|
|
6337
|
+
if (structural !== null) {
|
|
6338
|
+
push(
|
|
6339
|
+
out,
|
|
6340
|
+
WcsDiagnosticCode.RecursionStructuralWrite,
|
|
6341
|
+
start,
|
|
6342
|
+
end,
|
|
6343
|
+
msgs.recursionStructuralWrite(path, structural, spec.repeatList)
|
|
6344
|
+
);
|
|
6345
|
+
return;
|
|
6346
|
+
}
|
|
6347
|
+
const conflicting = conflictingGetterSuffix(spec, getterSuffixes, checkedSuffix);
|
|
6348
|
+
if (conflicting !== null) {
|
|
6349
|
+
push(
|
|
6350
|
+
out,
|
|
6351
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6352
|
+
start,
|
|
6353
|
+
end,
|
|
6354
|
+
msgs.recursionReadonly(`$setAll("${path}")`, spec.recursiveAnchor + conflicting)
|
|
6355
|
+
);
|
|
6356
|
+
}
|
|
6357
|
+
}
|
|
6358
|
+
function validateAssignments(script, offset2, spec, getterSuffixes, msgs, out) {
|
|
6359
|
+
const masked = maskCommentsAndStrings(script);
|
|
6360
|
+
const found = [];
|
|
6361
|
+
for (const source of [BRACKET_ASSIGNMENT, PRE_BRACKET_INCDEC]) {
|
|
6362
|
+
const regex = new RegExp(source.source, "g");
|
|
6363
|
+
let match;
|
|
6364
|
+
while ((match = regex.exec(masked)) !== null) {
|
|
6365
|
+
const pathStart = match.index + match[0].search(/["']/) + 1;
|
|
6366
|
+
found.push({ pathStart, length: match[1].length });
|
|
6367
|
+
}
|
|
6368
|
+
}
|
|
6369
|
+
found.sort((a, b) => a.pathStart - b.pathStart);
|
|
6370
|
+
let last = -1;
|
|
6371
|
+
for (const { pathStart, length } of found) {
|
|
6372
|
+
if (pathStart === last) continue;
|
|
6373
|
+
last = pathStart;
|
|
6374
|
+
const path = script.slice(pathStart, pathStart + length);
|
|
6375
|
+
const start = offset2 + pathStart;
|
|
6376
|
+
const end = start + path.length;
|
|
6377
|
+
if (hasRecursionWildcard(path)) {
|
|
6378
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "assignment"));
|
|
6379
|
+
continue;
|
|
6380
|
+
}
|
|
6381
|
+
if (spec === null) continue;
|
|
6382
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6383
|
+
if (owning !== null) {
|
|
6384
|
+
push(
|
|
6385
|
+
out,
|
|
6386
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6387
|
+
start,
|
|
6388
|
+
end,
|
|
6389
|
+
msgs.recursionReadonly(`this["${path}"] = \u2026`, spec.recursiveAnchor + owning)
|
|
6390
|
+
);
|
|
6391
|
+
}
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
function isDefiniteNonArrayLiteral(arg) {
|
|
6395
|
+
const trimmed = arg.trim();
|
|
6396
|
+
return trimmed === "null" || /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false)$/.test(trimmed) || trimmed.startsWith("{");
|
|
6397
|
+
}
|
|
6398
|
+
function isFunctionLiteral(arg) {
|
|
6399
|
+
const trimmed = arg.trim();
|
|
6400
|
+
if (trimmed.length === 0) return false;
|
|
6401
|
+
return /^(?:async\s+)?function\b/.test(trimmed) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(trimmed) || /^(?:async\s+)?[$\w]+\s*=>/.test(trimmed);
|
|
6402
|
+
}
|
|
6403
|
+
|
|
5353
6404
|
// src/service/namedStateValidator.ts
|
|
5354
6405
|
function findStateSelector(expr, embedded = false) {
|
|
5355
6406
|
const colon = embedded ? -1 : expr.indexOf(":");
|
|
@@ -11274,7 +12325,7 @@ function enterFunction(fn, outer, thisIsState) {
|
|
|
11274
12325
|
function isThisRoot(node, scope) {
|
|
11275
12326
|
return node.type === "ThisExpression" && scope.thisIsState || node.type === "Identifier" && scope.aliases.has(node.name);
|
|
11276
12327
|
}
|
|
11277
|
-
function
|
|
12328
|
+
function literalString2(node) {
|
|
11278
12329
|
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
11279
12330
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
11280
12331
|
return node.quasis[0].value.cooked ?? null;
|
|
@@ -11289,7 +12340,7 @@ function segmentOf(member) {
|
|
|
11289
12340
|
if (property.type === "Literal" && typeof property.value === "number") {
|
|
11290
12341
|
return { text: String(property.value), dynamic: null };
|
|
11291
12342
|
}
|
|
11292
|
-
const text =
|
|
12343
|
+
const text = literalString2(property);
|
|
11293
12344
|
if (text !== null) return { text, dynamic: null };
|
|
11294
12345
|
return property.type === "PrivateIdentifier" ? { text: null, dynamic: null } : { text: null, dynamic: property };
|
|
11295
12346
|
}
|
|
@@ -11385,7 +12436,7 @@ function visitCall(node, scope, out) {
|
|
|
11385
12436
|
if (api === UNTRACK_API) return;
|
|
11386
12437
|
if (PATH_ARG_APIS.has(api)) {
|
|
11387
12438
|
const first = node.arguments[0];
|
|
11388
|
-
const path = first !== void 0 && first.type !== "SpreadElement" ?
|
|
12439
|
+
const path = first !== void 0 && first.type !== "SpreadElement" ? literalString2(first) : null;
|
|
11389
12440
|
if (path !== null && path.length > 0 && !path.startsWith("$")) {
|
|
11390
12441
|
out.push({
|
|
11391
12442
|
path,
|
|
@@ -11451,7 +12502,7 @@ function visitDestructure(pattern, prefix, scope, out) {
|
|
|
11451
12502
|
if (property.type === "RestElement") continue;
|
|
11452
12503
|
let key = null;
|
|
11453
12504
|
if (!property.computed && property.key.type === "Identifier") key = property.key.name;
|
|
11454
|
-
else key =
|
|
12505
|
+
else key = literalString2(property.key);
|
|
11455
12506
|
let value = property.value;
|
|
11456
12507
|
if (value.type === "AssignmentPattern") {
|
|
11457
12508
|
visit(value.right, scope, out);
|
|
@@ -11484,6 +12535,10 @@ for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
|
|
|
11484
12535
|
tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
|
|
11485
12536
|
}
|
|
11486
12537
|
Object.freeze(tmpIndexByIndexName2);
|
|
12538
|
+
var RECURSION_WILDCARD2 = "**";
|
|
12539
|
+
function raiseError2(message) {
|
|
12540
|
+
throw new Error(`[@wcstack/state] ${message}`);
|
|
12541
|
+
}
|
|
11487
12542
|
var _cache = /* @__PURE__ */ new Map();
|
|
11488
12543
|
function clearPathInfoCacheForTooling() {
|
|
11489
12544
|
_cache.clear();
|
|
@@ -11494,6 +12549,9 @@ function getPathInfo(path) {
|
|
|
11494
12549
|
if (typeof pathInfo !== "undefined") {
|
|
11495
12550
|
return pathInfo;
|
|
11496
12551
|
}
|
|
12552
|
+
if (path.indexOf(RECURSION_WILDCARD2) !== -1) {
|
|
12553
|
+
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.`);
|
|
12554
|
+
}
|
|
11497
12555
|
pathInfo = Object.freeze(new PathInfo(path));
|
|
11498
12556
|
_cache.set(path, pathInfo);
|
|
11499
12557
|
return pathInfo;
|
|
@@ -11617,9 +12675,6 @@ function didYouMean(input, candidates) {
|
|
|
11617
12675
|
return best !== null ? ` Did you mean "${best}"?` : "";
|
|
11618
12676
|
}
|
|
11619
12677
|
var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
11620
|
-
function raiseError2(message) {
|
|
11621
|
-
throw new Error(`[@wcstack/state] ${message}`);
|
|
11622
|
-
}
|
|
11623
12678
|
var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
|
|
11624
12679
|
"if",
|
|
11625
12680
|
"elseif",
|
|
@@ -12623,71 +13678,6 @@ function buildReferenceIndex(html, options = {}) {
|
|
|
12623
13678
|
// src/service/semanticValidator.ts
|
|
12624
13679
|
var STATE_UPDATED_CALLBACK = "$updatedCallback";
|
|
12625
13680
|
var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
|
|
12626
|
-
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
12627
|
-
function splitCallArgs(source, open) {
|
|
12628
|
-
const args = [];
|
|
12629
|
-
const starts = [];
|
|
12630
|
-
let depth = 0;
|
|
12631
|
-
let argStart = open;
|
|
12632
|
-
let i = open;
|
|
12633
|
-
while (i < source.length) {
|
|
12634
|
-
const ch = source[i];
|
|
12635
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
12636
|
-
const quote = ch;
|
|
12637
|
-
i++;
|
|
12638
|
-
while (i < source.length) {
|
|
12639
|
-
if (source[i] === "\\") {
|
|
12640
|
-
i += 2;
|
|
12641
|
-
continue;
|
|
12642
|
-
}
|
|
12643
|
-
if (source[i] === quote) {
|
|
12644
|
-
i++;
|
|
12645
|
-
break;
|
|
12646
|
-
}
|
|
12647
|
-
i++;
|
|
12648
|
-
}
|
|
12649
|
-
continue;
|
|
12650
|
-
}
|
|
12651
|
-
if (ch === "(" || ch === "[" || ch === "{") {
|
|
12652
|
-
depth++;
|
|
12653
|
-
i++;
|
|
12654
|
-
continue;
|
|
12655
|
-
}
|
|
12656
|
-
if (ch === ")" && depth === 0) {
|
|
12657
|
-
args.push(source.slice(argStart, i));
|
|
12658
|
-
starts.push(argStart);
|
|
12659
|
-
return { args, starts, end: i + 1 };
|
|
12660
|
-
}
|
|
12661
|
-
if (ch === ")" || ch === "]" || ch === "}") {
|
|
12662
|
-
depth--;
|
|
12663
|
-
i++;
|
|
12664
|
-
continue;
|
|
12665
|
-
}
|
|
12666
|
-
if (ch === "," && depth === 0) {
|
|
12667
|
-
args.push(source.slice(argStart, i));
|
|
12668
|
-
starts.push(argStart);
|
|
12669
|
-
argStart = i + 1;
|
|
12670
|
-
i++;
|
|
12671
|
-
continue;
|
|
12672
|
-
}
|
|
12673
|
-
i++;
|
|
12674
|
-
}
|
|
12675
|
-
return null;
|
|
12676
|
-
}
|
|
12677
|
-
function literalString2(arg) {
|
|
12678
|
-
const match = STRING_LITERAL.exec(arg);
|
|
12679
|
-
return match === null ? null : match[2];
|
|
12680
|
-
}
|
|
12681
|
-
function literalArrayLength(arg) {
|
|
12682
|
-
const trimmed = arg.trim();
|
|
12683
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
12684
|
-
const inner = trimmed.slice(1, -1);
|
|
12685
|
-
if (inner.trim().length === 0) return 0;
|
|
12686
|
-
if (/(^|[^.])\.\.\./.test(inner)) return null;
|
|
12687
|
-
const parts = splitCallArgs(`${inner})`, 0);
|
|
12688
|
-
if (parts === null) return null;
|
|
12689
|
-
return parts.args.filter((part) => part.trim().length > 0).length;
|
|
12690
|
-
}
|
|
12691
13681
|
function validateIndexArity(script, scriptStart, locale3) {
|
|
12692
13682
|
const msgs = getMessages(locale3);
|
|
12693
13683
|
const out = [];
|
|
@@ -12699,8 +13689,9 @@ function validateIndexArity(script, scriptStart, locale3) {
|
|
|
12699
13689
|
if (parsed === null) continue;
|
|
12700
13690
|
API_CALL.lastIndex = parsed.end;
|
|
12701
13691
|
if (parsed.args.length < 2) continue;
|
|
12702
|
-
const path =
|
|
13692
|
+
const path = literalString(parsed.args[0]);
|
|
12703
13693
|
if (path === null) continue;
|
|
13694
|
+
if (hasRecursionWildcard(path)) continue;
|
|
12704
13695
|
const actual = literalArrayLength(parsed.args[1]);
|
|
12705
13696
|
if (actual === null) continue;
|
|
12706
13697
|
const wildcardCount = countWildcardSegments(path);
|
|
@@ -12807,7 +13798,7 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
|
|
|
12807
13798
|
}
|
|
12808
13799
|
var TWO_WAY_PROPS = /* @__PURE__ */ new Set(["value", "checked"]);
|
|
12809
13800
|
var BRACKET_WRITE = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
12810
|
-
var
|
|
13801
|
+
var PRE_BRACKET_INCDEC2 = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
12811
13802
|
function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
12812
13803
|
const roots = /* @__PURE__ */ new Set();
|
|
12813
13804
|
const addPrefixes = (path, inclusive) => {
|
|
@@ -12843,7 +13834,7 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12843
13834
|
for (const block of blocks) {
|
|
12844
13835
|
if (block.mountPath !== null) addPrefixes(block.mountPath, true);
|
|
12845
13836
|
const scan = blankComments(block.content);
|
|
12846
|
-
for (const regex of [BRACKET_WRITE,
|
|
13837
|
+
for (const regex of [BRACKET_WRITE, PRE_BRACKET_INCDEC2]) {
|
|
12847
13838
|
regex.lastIndex = 0;
|
|
12848
13839
|
let match;
|
|
12849
13840
|
while ((match = regex.exec(scan)) !== null) addPrefixes(match[1], false);
|
|
@@ -12856,57 +13847,13 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12856
13847
|
const parsed = splitCallArgs(scan, call.index + call[0].length);
|
|
12857
13848
|
if (parsed === null) continue;
|
|
12858
13849
|
API_CALL.lastIndex = parsed.end;
|
|
12859
|
-
const path = parsed.args.length > 0 ?
|
|
13850
|
+
const path = parsed.args.length > 0 ? literalString(parsed.args[0]) : null;
|
|
12860
13851
|
if (path === null) continue;
|
|
12861
13852
|
if (api === "setAll" || parsed.args.length >= 3) addPrefixes(path, false);
|
|
12862
13853
|
}
|
|
12863
13854
|
}
|
|
12864
13855
|
return roots;
|
|
12865
13856
|
}
|
|
12866
|
-
function blankComments(source) {
|
|
12867
|
-
const out = source.split("");
|
|
12868
|
-
let i = 0;
|
|
12869
|
-
while (i < source.length) {
|
|
12870
|
-
const ch = source[i];
|
|
12871
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
12872
|
-
const quote = ch;
|
|
12873
|
-
i++;
|
|
12874
|
-
while (i < source.length) {
|
|
12875
|
-
if (source[i] === "\\") {
|
|
12876
|
-
i += 2;
|
|
12877
|
-
continue;
|
|
12878
|
-
}
|
|
12879
|
-
if (source[i] === quote) {
|
|
12880
|
-
i++;
|
|
12881
|
-
break;
|
|
12882
|
-
}
|
|
12883
|
-
i++;
|
|
12884
|
-
}
|
|
12885
|
-
continue;
|
|
12886
|
-
}
|
|
12887
|
-
if (ch === "/" && source[i + 1] === "/") {
|
|
12888
|
-
while (i < source.length && source[i] !== "\n") {
|
|
12889
|
-
out[i] = " ";
|
|
12890
|
-
i++;
|
|
12891
|
-
}
|
|
12892
|
-
continue;
|
|
12893
|
-
}
|
|
12894
|
-
if (ch === "/" && source[i + 1] === "*") {
|
|
12895
|
-
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
12896
|
-
out[i] = " ";
|
|
12897
|
-
i++;
|
|
12898
|
-
}
|
|
12899
|
-
if (i < source.length) {
|
|
12900
|
-
out[i] = " ";
|
|
12901
|
-
out[i + 1] = " ";
|
|
12902
|
-
i += 2;
|
|
12903
|
-
}
|
|
12904
|
-
continue;
|
|
12905
|
-
}
|
|
12906
|
-
i++;
|
|
12907
|
-
}
|
|
12908
|
-
return out.join("");
|
|
12909
|
-
}
|
|
12910
13857
|
var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
|
|
12911
13858
|
function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
|
|
12912
13859
|
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
@@ -13011,6 +13958,7 @@ function validateDocument(text, options = {}) {
|
|
|
13011
13958
|
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
13012
13959
|
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
13013
13960
|
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
13961
|
+
out.push(...validateRecursion(text, stateTagName, locale3));
|
|
13014
13962
|
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
13015
13963
|
out.push(...validateMountAttributes(text, stateTagName, locale3));
|
|
13016
13964
|
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|