@wcstack/typescript 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +1 -1
- package/dist/schema-core.cjs +1674 -224
- package/dist/tsc-core.cjs +32 -2
- 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,44 @@ var WcsDiagnosticCode = {
|
|
|
93
93
|
// `$watch` のキーが状態定義に存在しない。バインディング側と違い黙って発火しない
|
|
94
94
|
// だけなので気づけない。severity は binding-path-missing に揃える(warning)。
|
|
95
95
|
WatchPathMissing: "wcs/watch-path-missing",
|
|
96
|
+
// --- <wcs-state> script: $scan declaration ---
|
|
97
|
+
// ランタイム(scan/processScanDeclaration.ts)が raiseError で落とす宣言の形。出力名・source の本数・
|
|
98
|
+
// initial / fold の欠落・from / resetOn のパスの形・未宣言トークン・自出力の読み。
|
|
99
|
+
ScanDeclarationInvalid: "wcs/scan-declaration-invalid",
|
|
100
|
+
// from / resetOn が getter(またはその配下)。畳むと出来事ではなく再評価の回数を数える。ランタイムも raise。
|
|
101
|
+
ScanSourceComputed: "wcs/scan-source-computed",
|
|
102
|
+
// from / resetOn のパスが状態定義に存在しない。黙って一度も畳まれない。severity は watch-path-missing に揃える。
|
|
103
|
+
ScanPathMissing: "wcs/scan-path-missing",
|
|
104
|
+
// --- <wcs-state> script: $recursion declaration / `**` paths ---
|
|
105
|
+
// ランタイムと同じ code 語彙(@wcstack/state src/recursion/ が正本。
|
|
106
|
+
// docs/state-recursive-path-impl-plan.md §7)。静的に出すのは**パス文字列と宣言だけで
|
|
107
|
+
// 決まる**ものに限る。データを見ないと決まらない wcs/recursion-shared-list /
|
|
108
|
+
// wcs/recursion-cycle / wcs/recursion-depth-exceeded、および評価時の呼び出し文脈に
|
|
109
|
+
// 依存する wcs/recursion-context は runtime 専用(静的側は出さない)。
|
|
110
|
+
//
|
|
111
|
+
// `**` を解釈しない場所へ `**` が渡った(data-wcs / mustache / $watch キー / $listKeys
|
|
112
|
+
// キー / $resolve / $postUpdate / $trackDependency / 代入)、または `$recursion` 宣言が
|
|
113
|
+
// 無いのに `**` を使った。runtime は PathInfo の不変条件として raiseError するか
|
|
114
|
+
//(API 経由)、getter を黙って無視する(宣言なしの `**` getter)。
|
|
115
|
+
RecursionUnsupported: "wcs/recursion-unsupported",
|
|
116
|
+
// 宣言済みアンカーと合致しない `**`(綴り違い・2 つ目の `**`)、または `**` の後ろが
|
|
117
|
+
// 整形されていない(空セグメント・`**` 直後の素の `*`)。
|
|
118
|
+
RecursionAnchor: "wcs/recursion-anchor",
|
|
119
|
+
// `$getAll` の添字の形が `**` に対して定義できない(非空の接頭辞 / 配列でない値)。
|
|
120
|
+
RecursionGetAllForm: "wcs/recursion-getall-form",
|
|
121
|
+
// `$setAll` の添字・値の形が `**` に対して定義できない
|
|
122
|
+
//(非空の接頭辞 / 添字省略 / mapper / spread)。
|
|
123
|
+
RecursionSetAllForm: "wcs/recursion-setall-form",
|
|
124
|
+
// ノード自身・子リスト・子ノード・子リストの length・多段の反復サブパスなら子リストへ
|
|
125
|
+
// 至る途中のオブジェクトへの一括書き込み(確定済みの子アドレスを壊す)。
|
|
126
|
+
RecursionStructuralWrite: "wcs/recursion-structural-write",
|
|
127
|
+
// 再帰 getter(およびその派生値の中)への書き込み。setter は初版では持てない。
|
|
128
|
+
RecursionReadonly: "wcs/recursion-readonly",
|
|
129
|
+
// `$recursion` 宣言そのもの、または `**` getter の宣言の形が不正(アンカー / 反復
|
|
130
|
+
// サブパスの形・複数宣言・setter・getter でない・ノード自身・構造を名指す接尾辞・
|
|
131
|
+
// 展開形と同名の具体 getter・ボリューム / マウント下での宣言)。
|
|
132
|
+
//(ランタイムは初期化時に raiseError)。wcs/watch-declaration-invalid の再帰版。
|
|
133
|
+
RecursionDeclarationInvalid: "wcs/recursion-declaration-invalid",
|
|
96
134
|
TypeAnnotation: "wcs/type-annotation",
|
|
97
135
|
TemplateSyntax: "wcs/template-syntax",
|
|
98
136
|
// --- <wcs-state> script: array reactivity hazards ---
|
|
@@ -1270,6 +1308,8 @@ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
|
1270
1308
|
var STATE_ON_NAME = "$on";
|
|
1271
1309
|
var STATE_STREAMS_NAME = "$streams";
|
|
1272
1310
|
var STATE_WATCH_NAME = "$watch";
|
|
1311
|
+
var STATE_SCAN_NAME = "$scan";
|
|
1312
|
+
var STATE_RECURSION_NAME = "$recursion";
|
|
1273
1313
|
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
1274
1314
|
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
1275
1315
|
var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
@@ -1331,7 +1371,9 @@ function getWcsManifest() {
|
|
|
1331
1371
|
STATE_ON_NAME,
|
|
1332
1372
|
STATE_STREAMS_NAME,
|
|
1333
1373
|
STATE_WATCH_NAME,
|
|
1374
|
+
STATE_SCAN_NAME,
|
|
1334
1375
|
STATE_LIST_KEYS_NAME,
|
|
1376
|
+
STATE_RECURSION_NAME,
|
|
1335
1377
|
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
1336
1378
|
STATE_STREAM_ERROR_NAMESPACE_NAME
|
|
1337
1379
|
]
|
|
@@ -1353,12 +1395,174 @@ var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET].map((name) => ({
|
|
|
1353
1395
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
1354
1396
|
}));
|
|
1355
1397
|
|
|
1398
|
+
// src/service/recursionPaths.ts
|
|
1399
|
+
var RECURSION_WILDCARD = "**";
|
|
1400
|
+
var RECURSION_KEY = "$recursion";
|
|
1401
|
+
function hasRecursionWildcard(path) {
|
|
1402
|
+
return path.indexOf(RECURSION_WILDCARD) !== -1;
|
|
1403
|
+
}
|
|
1404
|
+
function checkNodePath(path) {
|
|
1405
|
+
if (typeof path !== "string" || path.length === 0) return "empty";
|
|
1406
|
+
const segments = path.split(".");
|
|
1407
|
+
if (segments.some((segment) => segment.length === 0)) return "emptySegment";
|
|
1408
|
+
if (segments.length < 2 || segments[segments.length - 1] !== "*") return "notElement";
|
|
1409
|
+
if (segments[0].startsWith("$")) return "reservedRoot";
|
|
1410
|
+
if (path.indexOf("#") !== -1) return "reservedMount";
|
|
1411
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1412
|
+
if (segments[i] === "*") return "midWildcard";
|
|
1413
|
+
if (segments[i] === RECURSION_WILDCARD) return "nestedRecursion";
|
|
1414
|
+
if (!isNaN(Number(segments[i]))) return "indexSegment";
|
|
1415
|
+
}
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
function makeRecursionSpec(anchor, repeat) {
|
|
1419
|
+
return Object.freeze({
|
|
1420
|
+
anchor,
|
|
1421
|
+
repeat,
|
|
1422
|
+
recursiveAnchor: anchor.slice(0, anchor.lastIndexOf(".")) + "." + RECURSION_WILDCARD,
|
|
1423
|
+
anchorList: anchor.slice(0, anchor.lastIndexOf(".")),
|
|
1424
|
+
repeatList: repeat.slice(0, repeat.lastIndexOf("."))
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
function splitRecursivePath(spec, path) {
|
|
1428
|
+
if (path === spec.recursiveAnchor) return "";
|
|
1429
|
+
if (!path.startsWith(spec.recursiveAnchor + ".")) return null;
|
|
1430
|
+
const suffix = path.slice(spec.recursiveAnchor.length);
|
|
1431
|
+
if (hasRecursionWildcard(suffix)) return null;
|
|
1432
|
+
const segments = suffix.slice(1).split(".");
|
|
1433
|
+
if (segments[0] === "*" || segments.some((segment) => segment.length === 0)) return null;
|
|
1434
|
+
return suffix;
|
|
1435
|
+
}
|
|
1436
|
+
function foldSuffixIndexes(suffix) {
|
|
1437
|
+
return suffix.length === 0 ? suffix : "." + indexSegmentsToWildcard(suffix.slice(1));
|
|
1438
|
+
}
|
|
1439
|
+
function foldRecursion(spec, path) {
|
|
1440
|
+
if (!path.startsWith(spec.anchor)) return null;
|
|
1441
|
+
const unit3 = "." + spec.repeat;
|
|
1442
|
+
let cursor = spec.anchor.length;
|
|
1443
|
+
let depth = 0;
|
|
1444
|
+
while (path.startsWith(unit3, cursor)) {
|
|
1445
|
+
cursor += unit3.length;
|
|
1446
|
+
depth++;
|
|
1447
|
+
}
|
|
1448
|
+
if (cursor !== path.length && path.charCodeAt(cursor) !== 46) return null;
|
|
1449
|
+
return { depth, rest: path.slice(cursor) };
|
|
1450
|
+
}
|
|
1451
|
+
function matchesRecursion(specs, path, has) {
|
|
1452
|
+
for (const spec of specs) {
|
|
1453
|
+
const folded = foldRecursion(spec, path);
|
|
1454
|
+
if (folded === null) continue;
|
|
1455
|
+
const unit3 = "." + spec.repeat;
|
|
1456
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1457
|
+
const rest = unit3.repeat(folded.depth - depth) + folded.rest;
|
|
1458
|
+
if (has(spec.anchor + rest)) return true;
|
|
1459
|
+
if (has(spec.recursiveAnchor + rest)) return true;
|
|
1460
|
+
for (let dot = rest.lastIndexOf("."); dot > 0; dot = rest.lastIndexOf(".", dot - 1)) {
|
|
1461
|
+
if (has(spec.recursiveAnchor + rest.slice(0, dot))) return true;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1467
|
+
function owningGetterSuffix(spec, getterSuffixes, path) {
|
|
1468
|
+
const pattern = indexSegmentsToWildcard(path);
|
|
1469
|
+
const folded = foldRecursion(spec, pattern);
|
|
1470
|
+
if (folded === null) return null;
|
|
1471
|
+
const unit3 = "." + spec.repeat;
|
|
1472
|
+
for (const suffix of getterSuffixes) {
|
|
1473
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1474
|
+
const expansion = spec.anchor + unit3.repeat(depth) + suffix;
|
|
1475
|
+
if (pattern === expansion || pattern.startsWith(expansion + ".")) return suffix;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return null;
|
|
1479
|
+
}
|
|
1480
|
+
function indexSegmentsToWildcard(path) {
|
|
1481
|
+
return path.split(".").map((segment) => segment !== "*" && !Number.isNaN(Number(segment)) ? "*" : segment).join(".");
|
|
1482
|
+
}
|
|
1483
|
+
function concreteExpansionSuffix(spec, getterSuffixes, key) {
|
|
1484
|
+
const folded = foldRecursion(spec, key);
|
|
1485
|
+
if (folded === null) return null;
|
|
1486
|
+
const unit3 = "." + spec.repeat;
|
|
1487
|
+
for (const suffix of getterSuffixes) {
|
|
1488
|
+
for (let depth = folded.depth; depth >= 0; depth--) {
|
|
1489
|
+
if (key === spec.anchor + unit3.repeat(depth) + suffix) return suffix;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
function collectRecursionSpecs(candidates) {
|
|
1495
|
+
const out = [];
|
|
1496
|
+
for (const candidate of candidates) {
|
|
1497
|
+
if (candidate.kind !== "recursionAnchor" || typeof candidate.repeat !== "string") continue;
|
|
1498
|
+
if (!candidate.path.endsWith("." + RECURSION_WILDCARD)) continue;
|
|
1499
|
+
const anchor = candidate.path.slice(0, candidate.path.length - RECURSION_WILDCARD.length) + "*";
|
|
1500
|
+
if (out.some((spec) => spec.anchor === anchor && spec.repeat === candidate.repeat)) continue;
|
|
1501
|
+
out.push(makeRecursionSpec(anchor, candidate.repeat));
|
|
1502
|
+
}
|
|
1503
|
+
return out;
|
|
1504
|
+
}
|
|
1505
|
+
function impliedStructurePaths(spec) {
|
|
1506
|
+
const out = [
|
|
1507
|
+
{ path: spec.anchorList, kind: "data", typeHint: "array" },
|
|
1508
|
+
{ path: spec.anchor, kind: "list" },
|
|
1509
|
+
{ path: `${spec.anchorList}.length`, kind: "data", typeHint: "number" }
|
|
1510
|
+
];
|
|
1511
|
+
const repeatSegments = spec.repeatList.split(".");
|
|
1512
|
+
for (let i = 1; i < repeatSegments.length; i++) {
|
|
1513
|
+
out.push({ path: `${spec.anchor}.${repeatSegments.slice(0, i).join(".")}`, kind: "data" });
|
|
1514
|
+
}
|
|
1515
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}`, kind: "data", typeHint: "array" });
|
|
1516
|
+
out.push({ path: `${spec.anchor}.${spec.repeat}`, kind: "list" });
|
|
1517
|
+
out.push({ path: `${spec.anchor}.${spec.repeatList}.length`, kind: "data", typeHint: "number" });
|
|
1518
|
+
return out;
|
|
1519
|
+
}
|
|
1520
|
+
function structuralWriteTarget(spec, suffix) {
|
|
1521
|
+
const unit3 = "." + spec.repeat;
|
|
1522
|
+
let rest = suffix;
|
|
1523
|
+
while (rest.startsWith(unit3)) rest = rest.slice(unit3.length);
|
|
1524
|
+
if (rest.length === 0) return "node";
|
|
1525
|
+
if (rest === "." + spec.repeatList + ".length") return "length";
|
|
1526
|
+
const segments = spec.repeatList.split(".");
|
|
1527
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
1528
|
+
if (rest === "." + segments.slice(0, i).join(".")) return i === segments.length ? "list" : "branch";
|
|
1529
|
+
}
|
|
1530
|
+
return null;
|
|
1531
|
+
}
|
|
1532
|
+
function sameFamily(spec, a, b) {
|
|
1533
|
+
const unit3 = "." + spec.repeat;
|
|
1534
|
+
const shorter = a.length <= b.length ? a : b;
|
|
1535
|
+
const longer = a.length <= b.length ? b : a;
|
|
1536
|
+
if (!longer.endsWith(shorter)) return false;
|
|
1537
|
+
const gap = longer.slice(0, longer.length - shorter.length);
|
|
1538
|
+
if (gap.length === 0) return true;
|
|
1539
|
+
if (gap.length % unit3.length !== 0) return false;
|
|
1540
|
+
for (let cursor = 0; cursor < gap.length; cursor += unit3.length) {
|
|
1541
|
+
if (!gap.startsWith(unit3, cursor)) return false;
|
|
1542
|
+
}
|
|
1543
|
+
return true;
|
|
1544
|
+
}
|
|
1545
|
+
function conflictingGetterSuffix(spec, getterSuffixes, suffix) {
|
|
1546
|
+
for (const declared of getterSuffixes) {
|
|
1547
|
+
if (coversSuffix(spec, declared, suffix)) return declared;
|
|
1548
|
+
}
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
function coversSuffix(spec, familySuffix, suffix) {
|
|
1552
|
+
for (let end = suffix.length; end > 0; end = suffix.lastIndexOf(".", end - 1)) {
|
|
1553
|
+
if (sameFamily(spec, familySuffix, suffix.slice(0, end))) return true;
|
|
1554
|
+
}
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1356
1558
|
// src/service/stateAnalyzer.ts
|
|
1357
1559
|
var RESERVED_STREAMS_KEY = "$streams";
|
|
1358
1560
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
1359
1561
|
var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
|
|
1360
1562
|
var RESERVED_LIST_KEYS_KEY = "$listKeys";
|
|
1361
1563
|
var RESERVED_WATCH_KEY = "$watch";
|
|
1564
|
+
var RESERVED_SCAN_KEY = "$scan";
|
|
1565
|
+
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
1362
1566
|
function analyzeStatePaths(scriptContent) {
|
|
1363
1567
|
const objectContent = extractDefaultExportObject(scriptContent);
|
|
1364
1568
|
if (!objectContent) return [];
|
|
@@ -1366,51 +1570,290 @@ function analyzeStatePaths(scriptContent) {
|
|
|
1366
1570
|
const topLevelProps = parseTopLevelProperties(objectContent);
|
|
1367
1571
|
const pendingStreamValues = [];
|
|
1368
1572
|
const pendingListKeys = [];
|
|
1573
|
+
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
1574
|
+
const effectiveDescriptors = effectiveTopLevelDescriptors(topLevelProps);
|
|
1369
1575
|
for (const prop of topLevelProps) {
|
|
1370
1576
|
if (prop.name.startsWith("$")) {
|
|
1371
1577
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
1372
1578
|
continue;
|
|
1373
1579
|
}
|
|
1580
|
+
const effective = effectiveDescriptors.get(prop.name);
|
|
1581
|
+
if (prop.kind === "getter" !== (effective !== "data")) {
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1374
1584
|
if (prop.kind === "method") {
|
|
1375
1585
|
paths.push({ path: prop.name, kind: "method" });
|
|
1376
1586
|
continue;
|
|
1377
1587
|
}
|
|
1378
1588
|
if (prop.kind === "getter") {
|
|
1379
|
-
|
|
1380
|
-
|
|
1589
|
+
const { get, set } = effective;
|
|
1590
|
+
const writeOnly = set && !get;
|
|
1591
|
+
const declared = paths.find((p) => p.path === prop.name);
|
|
1592
|
+
if (declared === void 0) {
|
|
1593
|
+
const kind = hasRecursionWildcard(prop.name) ? "recursive" : "computed";
|
|
1594
|
+
paths.push(writeOnly ? { path: prop.name, kind, writeOnly: true } : { path: prop.name, kind });
|
|
1595
|
+
} else if (writeOnly) {
|
|
1596
|
+
declared.writeOnly = true;
|
|
1381
1597
|
}
|
|
1382
1598
|
continue;
|
|
1383
1599
|
}
|
|
1384
1600
|
pushDataPropertyPaths(prop, paths);
|
|
1385
1601
|
}
|
|
1386
1602
|
for (const streamValue of pendingStreamValues) {
|
|
1387
|
-
if (paths.some((p) => p.path === streamValue.name)) continue;
|
|
1603
|
+
if (paths.some((p) => p.path === streamValue.name && p.kind !== "eventToken")) continue;
|
|
1388
1604
|
pushDataPropertyPaths(streamValue, paths);
|
|
1389
1605
|
}
|
|
1390
1606
|
for (const listKeyEntry of pendingListKeys) {
|
|
1391
1607
|
pushListKeyPaths(listKeyEntry, paths);
|
|
1392
1608
|
}
|
|
1609
|
+
if (recursionSpec !== null) {
|
|
1610
|
+
if (!paths.some((p) => p.path === recursionSpec.recursiveAnchor && p.kind === "recursionAnchor")) {
|
|
1611
|
+
paths.push({ path: recursionSpec.recursiveAnchor, kind: "recursionAnchor", repeat: recursionSpec.repeat });
|
|
1612
|
+
}
|
|
1613
|
+
for (const implied of impliedStructurePaths(recursionSpec)) {
|
|
1614
|
+
if (paths.some((p) => p.path === implied.path)) continue;
|
|
1615
|
+
paths.push({ path: implied.path, kind: implied.kind, typeHint: implied.typeHint });
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1393
1618
|
collectRowShapesFromAssignments(scriptContent, paths);
|
|
1394
1619
|
return paths;
|
|
1395
1620
|
}
|
|
1621
|
+
function specFromRecursionValue(prop) {
|
|
1622
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value)) return null;
|
|
1623
|
+
const entries = parseTopLevelProperties(extractObjectContent(prop.value)).filter((e) => e.kind === "data");
|
|
1624
|
+
if (entries.length !== 1) return null;
|
|
1625
|
+
const anchor = entries[0].name;
|
|
1626
|
+
const repeat = extractStringLiteralValue(entries[0].value);
|
|
1627
|
+
if (repeat === null) return null;
|
|
1628
|
+
if (checkNodePath(anchor) !== null || checkNodePath(repeat) !== null) return null;
|
|
1629
|
+
return makeRecursionSpec(anchor, repeat);
|
|
1630
|
+
}
|
|
1631
|
+
function analyzeRecursionDeclaration(scriptContent) {
|
|
1632
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1633
|
+
if (!root) return null;
|
|
1634
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_RECURSION_KEY);
|
|
1635
|
+
if (!prop || prop.nameStart === void 0 || prop.nameEnd === void 0) return null;
|
|
1636
|
+
const span = { start: root.start + prop.nameStart, end: root.start + prop.nameEnd };
|
|
1637
|
+
if (prop.kind === "method") {
|
|
1638
|
+
return { ...span, notObject: true, objectLiteral: false, entries: [], spec: null };
|
|
1639
|
+
}
|
|
1640
|
+
if (prop.kind !== "data" || !prop.value || prop.valueStart === void 0) {
|
|
1641
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1642
|
+
}
|
|
1643
|
+
if (!isObjectLiteral(prop.value)) {
|
|
1644
|
+
const scan = maskCommentsAndStrings(prop.value).trim();
|
|
1645
|
+
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);
|
|
1646
|
+
return { ...span, notObject: definite, objectLiteral: false, entries: [], spec: null };
|
|
1647
|
+
}
|
|
1648
|
+
const objectContent = extractObjectContent(prop.value);
|
|
1649
|
+
if (hasUndecidableEntries(objectContent)) {
|
|
1650
|
+
return { ...span, notObject: false, objectLiteral: false, entries: [], spec: null };
|
|
1651
|
+
}
|
|
1652
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1653
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1654
|
+
const entries = [];
|
|
1655
|
+
for (const entry of parseTopLevelProperties(objectContent)) {
|
|
1656
|
+
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1657
|
+
const valueStart = entry.valueStart === void 0 ? innerStart + entry.nameEnd : innerStart + entry.valueStart + (entry.value ? entry.value.length - entry.value.trimStart().length : 0);
|
|
1658
|
+
entries.push({
|
|
1659
|
+
anchor: entry.name,
|
|
1660
|
+
repeat: entry.kind === "data" ? extractStringLiteralValue(entry.value) : null,
|
|
1661
|
+
repeatDefinitelyNotString: entry.kind !== "data" || isDefiniteNonStringLiteral(entry.value),
|
|
1662
|
+
start: innerStart + entry.nameStart,
|
|
1663
|
+
end: innerStart + entry.nameEnd,
|
|
1664
|
+
valueStart,
|
|
1665
|
+
valueEnd: valueStart + (entry.value?.trim().length ?? 0)
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
return { ...span, notObject: false, objectLiteral: true, entries, spec: specFromRecursionValue(prop) };
|
|
1669
|
+
}
|
|
1396
1670
|
function analyzeWatchEntries(scriptContent) {
|
|
1671
|
+
return analyzeObjectEntries(scriptContent, RESERVED_WATCH_KEY).map((entry) => ({
|
|
1672
|
+
key: entry.key,
|
|
1673
|
+
start: entry.start,
|
|
1674
|
+
end: entry.end,
|
|
1675
|
+
// メソッド短縮記法は関数。data は値リテラルの形で判定し、識別子参照は疑わない。
|
|
1676
|
+
definitelyNotFunction: entry.kind === "data" && isNonFunctionLiteral(entry.value)
|
|
1677
|
+
}));
|
|
1678
|
+
}
|
|
1679
|
+
function analyzeListKeyEntries(scriptContent) {
|
|
1680
|
+
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1681
|
+
}
|
|
1682
|
+
function analyzeScanEntries(scriptContent) {
|
|
1683
|
+
return analyzeObjectEntries(scriptContent, RESERVED_SCAN_KEY, true).map(describeScanEntry);
|
|
1684
|
+
}
|
|
1685
|
+
function describeScanEntry(entry) {
|
|
1686
|
+
const unreadable = {
|
|
1687
|
+
name: entry.key,
|
|
1688
|
+
start: entry.start,
|
|
1689
|
+
end: entry.end,
|
|
1690
|
+
notObject: false,
|
|
1691
|
+
readable: false,
|
|
1692
|
+
hasFrom: false,
|
|
1693
|
+
hasOn: false,
|
|
1694
|
+
from: null,
|
|
1695
|
+
on: null,
|
|
1696
|
+
hasInitial: false,
|
|
1697
|
+
foldMissingOrNotFunction: false,
|
|
1698
|
+
resetOn: null,
|
|
1699
|
+
resetOnNotArray: false,
|
|
1700
|
+
fromNotString: false,
|
|
1701
|
+
onNotString: false,
|
|
1702
|
+
resetOnHasNonString: false
|
|
1703
|
+
};
|
|
1704
|
+
if (entry.kind === "method") {
|
|
1705
|
+
return { ...unreadable, notObject: true };
|
|
1706
|
+
}
|
|
1707
|
+
const value = entry.value;
|
|
1708
|
+
if (entry.kind !== "data" || value === void 0 || entry.valueStart === void 0) {
|
|
1709
|
+
return unreadable;
|
|
1710
|
+
}
|
|
1711
|
+
if (!isObjectLiteral(value)) {
|
|
1712
|
+
return { ...unreadable, notObject: isDefiniteNonObjectLiteral(value) };
|
|
1713
|
+
}
|
|
1714
|
+
const content = extractObjectContent(value);
|
|
1715
|
+
if (hasUndecidableEntries(content)) {
|
|
1716
|
+
return unreadable;
|
|
1717
|
+
}
|
|
1718
|
+
const contentStart = entry.valueStart + 1;
|
|
1719
|
+
const props = parseTopLevelProperties(content);
|
|
1720
|
+
const find = (name) => props.find((p) => p.name === name && !(p.kind === "data" && p.value?.trim() === "undefined"));
|
|
1721
|
+
const foldProp = find("fold");
|
|
1722
|
+
const resetProp = find("resetOn");
|
|
1723
|
+
return {
|
|
1724
|
+
...unreadable,
|
|
1725
|
+
readable: true,
|
|
1726
|
+
hasFrom: find("from") !== void 0,
|
|
1727
|
+
hasOn: find("on") !== void 0,
|
|
1728
|
+
from: scanStringField(find("from"), contentStart),
|
|
1729
|
+
on: scanStringField(find("on"), contentStart),
|
|
1730
|
+
hasInitial: props.some((p) => p.name === "initial"),
|
|
1731
|
+
foldMissingOrNotFunction: foldProp === void 0 || foldProp.kind === "data" && isNonFunctionLiteral(foldProp.value),
|
|
1732
|
+
resetOn: resetProp === void 0 ? null : scanStringArrayFields(resetProp, contentStart),
|
|
1733
|
+
resetOnNotArray: resetProp !== void 0 && resetProp.kind === "data" && isDefiniteNonArrayLiteral(resetProp.value),
|
|
1734
|
+
fromNotString: isDefiniteNonPathValue(find("from")),
|
|
1735
|
+
onNotString: isDefiniteNonPathValue(find("on")),
|
|
1736
|
+
resetOnHasNonString: resetProp !== void 0 && resetProp.kind === "data" && resetProp.value !== void 0 && hasDefiniteNonStringElement(resetProp.value)
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function isDefiniteNonPathValue(prop) {
|
|
1740
|
+
if (prop === void 0 || prop.kind === "getter") return false;
|
|
1741
|
+
if (prop.kind === "method") return true;
|
|
1742
|
+
return prop.value !== void 0 && isDefiniteNonPathLiteral(prop.value, true);
|
|
1743
|
+
}
|
|
1744
|
+
function isDefiniteNonPathLiteral(value, emptyIsInvalid) {
|
|
1745
|
+
const text = value.trim();
|
|
1746
|
+
if (/^(["'`])\1$/.test(text)) return emptyIsInvalid;
|
|
1747
|
+
const scan = maskCommentsAndStrings(text).trim();
|
|
1748
|
+
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);
|
|
1749
|
+
}
|
|
1750
|
+
function isWholeBracketLiteral(scan) {
|
|
1751
|
+
if (scan[0] !== "[" && scan[0] !== "{") return false;
|
|
1752
|
+
let depth = 0;
|
|
1753
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1754
|
+
const ch = scan[i];
|
|
1755
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
1756
|
+
depth++;
|
|
1757
|
+
} else if (ch === ")" || ch === "]" || ch === "}") {
|
|
1758
|
+
depth--;
|
|
1759
|
+
if (depth === 0) return i === scan.length - 1;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
return false;
|
|
1763
|
+
}
|
|
1764
|
+
function hasDefiniteNonStringElement(value) {
|
|
1765
|
+
const text = value.trim();
|
|
1766
|
+
const scan = maskCommentsAndStrings(text);
|
|
1767
|
+
if (scan[0] !== "[" || !isWholeBracketLiteral(scan)) return false;
|
|
1768
|
+
const elements = [];
|
|
1769
|
+
let depth = 0;
|
|
1770
|
+
let start = 1;
|
|
1771
|
+
for (let i = 1; i < scan.length - 1; i++) {
|
|
1772
|
+
const ch = scan[i];
|
|
1773
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
1774
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
1775
|
+
else if (ch === "," && depth === 0) {
|
|
1776
|
+
elements.push(text.slice(start, i));
|
|
1777
|
+
start = i + 1;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
elements.push(text.slice(start, scan.length - 1));
|
|
1781
|
+
return elements.some((element) => element.trim().length > 0 && isDefiniteNonPathLiteral(element, false));
|
|
1782
|
+
}
|
|
1783
|
+
function scanStringField(prop, contentStart) {
|
|
1784
|
+
if (!prop || prop.kind !== "data" || prop.value === void 0 || prop.valueStart === void 0) return null;
|
|
1785
|
+
const literal2 = extractStringLiteralValue(prop.value);
|
|
1786
|
+
if (literal2 === null) return null;
|
|
1787
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1788
|
+
const start = contentStart + prop.valueStart + leading + 1;
|
|
1789
|
+
return { value: literal2, start, end: start + literal2.length };
|
|
1790
|
+
}
|
|
1791
|
+
function scanStringArrayFields(prop, contentStart) {
|
|
1792
|
+
if (prop.kind !== "data" || prop.value === void 0 || prop.valueStart === void 0) return null;
|
|
1793
|
+
const text = prop.value.trim();
|
|
1794
|
+
if (!isArrayLiteral(text)) return null;
|
|
1795
|
+
const literal2 = /(["'])([^"'\\\n]*)\1/g;
|
|
1796
|
+
if (text.replace(literal2, "").replace(/[\s,]/g, "") !== "[]") return null;
|
|
1797
|
+
const base = contentStart + prop.valueStart + (prop.value.length - prop.value.trimStart().length);
|
|
1798
|
+
const fields = [];
|
|
1799
|
+
for (const match of text.matchAll(literal2)) {
|
|
1800
|
+
const start = base + match.index + 1;
|
|
1801
|
+
fields.push({ value: match[2], start, end: start + match[2].length });
|
|
1802
|
+
}
|
|
1803
|
+
return fields;
|
|
1804
|
+
}
|
|
1805
|
+
function isDefiniteNonObjectLiteral(value) {
|
|
1806
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1807
|
+
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);
|
|
1808
|
+
}
|
|
1809
|
+
function isDefiniteNonArrayLiteral(value) {
|
|
1810
|
+
if (value === void 0) return false;
|
|
1811
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
1812
|
+
return /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^\{/.test(scan);
|
|
1813
|
+
}
|
|
1814
|
+
function readEventTokenNames(scriptContent) {
|
|
1815
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1816
|
+
if (!root || hasTopLevelSpread(scriptContent)) return null;
|
|
1817
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_EVENT_TOKENS_KEY);
|
|
1818
|
+
if (!prop) return /* @__PURE__ */ new Set();
|
|
1819
|
+
if (prop.kind !== "data" || prop.value === void 0 || !isArrayLiteral(prop.value)) return null;
|
|
1820
|
+
return new Set(extractStringArrayItems(prop.value));
|
|
1821
|
+
}
|
|
1822
|
+
function hasDefaultExportObject(scriptContent) {
|
|
1823
|
+
return locateDefaultExportObject(scriptContent) !== null;
|
|
1824
|
+
}
|
|
1825
|
+
function hasTopLevelSpread(scriptContent) {
|
|
1826
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
1827
|
+
if (!root) return false;
|
|
1828
|
+
const scan = maskCommentsAndStrings(root.content);
|
|
1829
|
+
let depth = 0;
|
|
1830
|
+
for (let i = 0; i < scan.length; i++) {
|
|
1831
|
+
const ch = scan[i];
|
|
1832
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
1833
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
1834
|
+
else if (depth === 0 && ch === "." && scan.startsWith("...", i)) return true;
|
|
1835
|
+
}
|
|
1836
|
+
return false;
|
|
1837
|
+
}
|
|
1838
|
+
function analyzeObjectEntries(scriptContent, key, allowEmptyKeys = false) {
|
|
1397
1839
|
const root = locateDefaultExportObject(scriptContent);
|
|
1398
1840
|
if (!root) return [];
|
|
1399
|
-
const
|
|
1400
|
-
if (!
|
|
1841
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1842
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
|
|
1401
1843
|
return [];
|
|
1402
1844
|
}
|
|
1403
|
-
const leading =
|
|
1404
|
-
const innerStart = root.start +
|
|
1845
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1846
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1405
1847
|
const entries = [];
|
|
1406
|
-
for (const entry of parseTopLevelProperties(extractObjectContent(
|
|
1848
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value), allowEmptyKeys)) {
|
|
1407
1849
|
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1408
1850
|
entries.push({
|
|
1409
1851
|
key: entry.name,
|
|
1410
1852
|
start: innerStart + entry.nameStart,
|
|
1411
1853
|
end: innerStart + entry.nameEnd,
|
|
1412
|
-
|
|
1413
|
-
|
|
1854
|
+
kind: entry.kind,
|
|
1855
|
+
value: entry.value,
|
|
1856
|
+
valueStart: entry.valueStart === void 0 || entry.value === void 0 ? void 0 : innerStart + entry.valueStart + (entry.value.length - entry.value.trimStart().length)
|
|
1414
1857
|
});
|
|
1415
1858
|
}
|
|
1416
1859
|
return entries;
|
|
@@ -1458,20 +1901,27 @@ function isNonFunctionLiteral(value) {
|
|
|
1458
1901
|
return /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false|null|undefined)\b/.test(trimmed) || trimmed.startsWith("[") || trimmed.startsWith("{");
|
|
1459
1902
|
}
|
|
1460
1903
|
function findNonObjectWatch(scriptContent) {
|
|
1904
|
+
return findNonObjectDeclaration(scriptContent, RESERVED_WATCH_KEY);
|
|
1905
|
+
}
|
|
1906
|
+
function findNonObjectScan(scriptContent) {
|
|
1907
|
+
return findNonObjectDeclaration(scriptContent, RESERVED_SCAN_KEY, true);
|
|
1908
|
+
}
|
|
1909
|
+
function findNonObjectDeclaration(scriptContent, key, rejectArray = false) {
|
|
1461
1910
|
const root = locateDefaultExportObject(scriptContent);
|
|
1462
1911
|
if (!root) return null;
|
|
1463
|
-
const
|
|
1464
|
-
if (!
|
|
1912
|
+
const declarationProp = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1913
|
+
if (!declarationProp || declarationProp.nameStart === void 0 || declarationProp.nameEnd === void 0) {
|
|
1465
1914
|
return null;
|
|
1466
1915
|
}
|
|
1467
|
-
const span = { start: root.start +
|
|
1468
|
-
if (
|
|
1916
|
+
const span = { start: root.start + declarationProp.nameStart, end: root.start + declarationProp.nameEnd };
|
|
1917
|
+
if (declarationProp.kind === "method") {
|
|
1469
1918
|
return span;
|
|
1470
1919
|
}
|
|
1471
|
-
if (
|
|
1472
|
-
const trimmed =
|
|
1920
|
+
if (declarationProp.kind !== "data" || !declarationProp.value) return null;
|
|
1921
|
+
const trimmed = declarationProp.value.trim();
|
|
1473
1922
|
if (trimmed.startsWith("{")) return null;
|
|
1474
1923
|
const scan = maskCommentsAndStrings(trimmed).trim();
|
|
1924
|
+
if (rejectArray && scan.startsWith("[") && isWholeBracketLiteral(scan)) return span;
|
|
1475
1925
|
const isArrowFunction = /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1476
1926
|
const isWholeLiteral = /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan);
|
|
1477
1927
|
if (!isArrowFunction && !isWholeLiteral) return null;
|
|
@@ -1494,6 +1944,19 @@ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKe
|
|
|
1494
1944
|
}
|
|
1495
1945
|
return;
|
|
1496
1946
|
}
|
|
1947
|
+
if (prop.name === RESERVED_SCAN_KEY && prop.kind === "data" && prop.value && isObjectLiteral(prop.value)) {
|
|
1948
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value), true)) {
|
|
1949
|
+
if (entry.kind !== "data" || !isFlatScanOutputName(entry.name)) continue;
|
|
1950
|
+
const initial = entry.value && isObjectLiteral(entry.value) ? findStreamInitialProperty(entry.value) : void 0;
|
|
1951
|
+
pendingStreamValues.push({
|
|
1952
|
+
name: entry.name,
|
|
1953
|
+
kind: "data",
|
|
1954
|
+
value: initial?.value,
|
|
1955
|
+
typeHint: initial?.typeHint
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1497
1960
|
if (prop.name === RESERVED_COMMAND_TOKENS_KEY && prop.value) {
|
|
1498
1961
|
for (const name of extractStringArrayItems(prop.value)) {
|
|
1499
1962
|
paths.push({ path: `$command.${name}`, kind: "command" });
|
|
@@ -1520,6 +1983,9 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1520
1983
|
if (listPath.length === 0 || segments.some((s) => s.length === 0) || segments[segments.length - 1] === "*") {
|
|
1521
1984
|
return;
|
|
1522
1985
|
}
|
|
1986
|
+
if (hasRecursionWildcard(listPath)) {
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1523
1989
|
const has = (path) => paths.some((p) => p.path === path);
|
|
1524
1990
|
if (!has(listPath)) paths.push({ path: listPath, kind: "data", typeHint: "array" });
|
|
1525
1991
|
if (!has(`${listPath}.*`)) paths.push({ path: `${listPath}.*`, kind: "list" });
|
|
@@ -1534,8 +2000,42 @@ function pushListKeyPaths(entry, paths) {
|
|
|
1534
2000
|
}
|
|
1535
2001
|
function extractStringLiteralValue(value) {
|
|
1536
2002
|
if (!value) return null;
|
|
1537
|
-
const match = value.trim().match(/^["']([^"'\\]*)["']$/);
|
|
1538
|
-
|
|
2003
|
+
const match = value.trim().match(/^(?:["']([^"'\\]*)["']|`([^`\\$]*)`)$/);
|
|
2004
|
+
const literal2 = match ? match[1] ?? match[2] : null;
|
|
2005
|
+
return literal2 !== null && literal2 !== void 0 && literal2.length > 0 ? literal2 : null;
|
|
2006
|
+
}
|
|
2007
|
+
function hasUndecidableEntries(objectContent) {
|
|
2008
|
+
const scan = maskCommentsAndStrings(objectContent);
|
|
2009
|
+
let depth = 0;
|
|
2010
|
+
let atKey = true;
|
|
2011
|
+
for (let i = 0; i < scan.length; i++) {
|
|
2012
|
+
const ch = scan[i];
|
|
2013
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
2014
|
+
if (depth === 0 && atKey && (ch === "[" || scan.startsWith("...", i))) return true;
|
|
2015
|
+
depth++;
|
|
2016
|
+
atKey = false;
|
|
2017
|
+
continue;
|
|
2018
|
+
}
|
|
2019
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
2020
|
+
depth--;
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
if (depth !== 0) continue;
|
|
2024
|
+
if (ch === ",") {
|
|
2025
|
+
atKey = true;
|
|
2026
|
+
continue;
|
|
2027
|
+
}
|
|
2028
|
+
if (/\s/.test(ch)) continue;
|
|
2029
|
+
if (atKey && scan.startsWith("...", i)) return true;
|
|
2030
|
+
atKey = false;
|
|
2031
|
+
}
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
function isDefiniteNonStringLiteral(value) {
|
|
2035
|
+
if (!value) return false;
|
|
2036
|
+
const scan = maskCommentsAndStrings(value).trim();
|
|
2037
|
+
if (scan.length === 0) return false;
|
|
2038
|
+
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
2039
|
}
|
|
1540
2040
|
var ROW_ASSIGN = new RegExp(
|
|
1541
2041
|
String.raw`\bthis\s*(?:\.\s*([$\w]+)|\[\s*["']([^"']+)["']\s*\])\s*=(?![=>])\s*(?:(\[)|(?:[^;={}]|=>)*?\.\s*(?:concat|toSpliced|with)\s*(\())`,
|
|
@@ -1630,6 +2130,9 @@ function findStreamInitialProperty(entryValue) {
|
|
|
1630
2130
|
const defProps = parseTopLevelProperties(extractObjectContent(entryValue));
|
|
1631
2131
|
return defProps.find((p) => p.kind === "data" && p.name === "initial");
|
|
1632
2132
|
}
|
|
2133
|
+
function isFlatScanOutputName(name) {
|
|
2134
|
+
return name.length > 0 && !name.startsWith("$") && !name.includes(".") && !name.includes("*");
|
|
2135
|
+
}
|
|
1633
2136
|
function extractStringArrayItems(value) {
|
|
1634
2137
|
if (!isArrayLiteral(value)) return [];
|
|
1635
2138
|
const items = [];
|
|
@@ -1641,6 +2144,19 @@ function extractStringArrayItems(value) {
|
|
|
1641
2144
|
return items;
|
|
1642
2145
|
}
|
|
1643
2146
|
var MAX_OBJECT_NEST_DEPTH = 5;
|
|
2147
|
+
function effectiveTopLevelDescriptors(props) {
|
|
2148
|
+
const effective = /* @__PURE__ */ new Map();
|
|
2149
|
+
for (const prop of props) {
|
|
2150
|
+
if (prop.kind !== "getter") {
|
|
2151
|
+
effective.set(prop.name, "data");
|
|
2152
|
+
continue;
|
|
2153
|
+
}
|
|
2154
|
+
const current2 = effective.get(prop.name);
|
|
2155
|
+
const pair = current2 === void 0 || current2 === "data" ? { get: false, set: false } : current2;
|
|
2156
|
+
effective.set(prop.name, prop.accessor === "set" ? { ...pair, set: true } : { ...pair, get: true });
|
|
2157
|
+
}
|
|
2158
|
+
return effective;
|
|
2159
|
+
}
|
|
1644
2160
|
function pushDataPropertyPaths(prop, paths) {
|
|
1645
2161
|
pushDataPropertyPathsAt(prop.name, prop, paths, 0);
|
|
1646
2162
|
}
|
|
@@ -1719,10 +2235,10 @@ function locateDefaultExportObject(script) {
|
|
|
1719
2235
|
function extractDefaultExportObject(script) {
|
|
1720
2236
|
return locateDefaultExportObject(script)?.content ?? null;
|
|
1721
2237
|
}
|
|
1722
|
-
function parseTopLevelProperties(objectContent) {
|
|
2238
|
+
function parseTopLevelProperties(objectContent, allowEmptyDataKeys = false) {
|
|
1723
2239
|
const props = [];
|
|
1724
2240
|
const scan = maskCommentsAndStrings(objectContent);
|
|
1725
|
-
const regex = /(?:(?:get|set)\s+(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:async\s+)?(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*:\s*)/gd;
|
|
2241
|
+
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;
|
|
1726
2242
|
let match;
|
|
1727
2243
|
while ((match = regex.exec(scan)) !== null) {
|
|
1728
2244
|
const indices = match.indices;
|
|
@@ -1768,7 +2284,7 @@ function parseTopLevelProperties(objectContent) {
|
|
|
1768
2284
|
continue;
|
|
1769
2285
|
}
|
|
1770
2286
|
const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
|
|
1771
|
-
if (propName) {
|
|
2287
|
+
if (propName !== void 0) {
|
|
1772
2288
|
const valueStartIndex = match.index + match[0].length;
|
|
1773
2289
|
const value = extractFullValue(objectContent, scan, valueStartIndex);
|
|
1774
2290
|
const jsdocType = extractJsDocType(objectContent, match.index);
|
|
@@ -2129,6 +2645,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
2129
2645
|
continue;
|
|
2130
2646
|
}
|
|
2131
2647
|
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
2648
|
+
const bindComponent = parseAttributeNames(wcsMatch.tagContent).has("bind-component");
|
|
2132
2649
|
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
2133
2650
|
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
2134
2651
|
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
@@ -2170,7 +2687,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
2170
2687
|
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
2171
2688
|
if (pos === 0) break;
|
|
2172
2689
|
}
|
|
2173
|
-
elements.push({ mountPath, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2690
|
+
elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2174
2691
|
pos = wcsEnd;
|
|
2175
2692
|
if (wcsCloseIdx !== -1) {
|
|
2176
2693
|
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
@@ -2260,6 +2777,15 @@ function findCloseTag(html, startPos, tagName) {
|
|
|
2260
2777
|
}
|
|
2261
2778
|
return -1;
|
|
2262
2779
|
}
|
|
2780
|
+
function parseAttributeNames(tagContent) {
|
|
2781
|
+
const names = /* @__PURE__ */ new Set();
|
|
2782
|
+
const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>`=]+))?/g;
|
|
2783
|
+
let match;
|
|
2784
|
+
while ((match = attribute.exec(tagContent)) !== null) {
|
|
2785
|
+
names.add(match[1].toLowerCase());
|
|
2786
|
+
}
|
|
2787
|
+
return names;
|
|
2788
|
+
}
|
|
2263
2789
|
function extractAttribute(tagContent, attrName) {
|
|
2264
2790
|
const regex = new RegExp(
|
|
2265
2791
|
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
@@ -2499,6 +3025,33 @@ var ja = {
|
|
|
2499
3025
|
watchKeyEmptySegment: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`,
|
|
2500
3026
|
watchHandlerNotFunction: (k) => `$watch \u306E\u30A8\u30F3\u30C8\u30EA "${k}" \u306E\u5024\u306F\u95A2\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
2501
3027
|
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`,
|
|
3028
|
+
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`,
|
|
3029
|
+
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`,
|
|
3030
|
+
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`,
|
|
3031
|
+
scanOutputEmpty: () => `$scan \u306E\u51FA\u529B\u540D\u306F\u7A7A\u3067\u306A\u3044\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
3032
|
+
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`,
|
|
3033
|
+
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`,
|
|
3034
|
+
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`,
|
|
3035
|
+
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`,
|
|
3036
|
+
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`,
|
|
3037
|
+
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`,
|
|
3038
|
+
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`,
|
|
3039
|
+
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`,
|
|
3040
|
+
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`,
|
|
3041
|
+
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`,
|
|
3042
|
+
scanFoldNotFunction: (n) => `$scan \u306E\u30A8\u30F3\u30C8\u30EA "${n}" \u306E "fold" \u306F\u95A2\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
|
|
3043
|
+
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`,
|
|
3044
|
+
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`,
|
|
3045
|
+
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`,
|
|
3046
|
+
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`,
|
|
3047
|
+
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`,
|
|
3048
|
+
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`,
|
|
3049
|
+
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`,
|
|
3050
|
+
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`,
|
|
3051
|
+
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`,
|
|
3052
|
+
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`,
|
|
3053
|
+
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`,
|
|
3054
|
+
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`,
|
|
2502
3055
|
typeAnnotationIncompatible: (vt, rt) => `\u578B "${vt}" \u306F @type {${rt}} \u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093`,
|
|
2503
3056
|
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`,
|
|
2504
3057
|
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`,
|
|
@@ -2527,7 +3080,96 @@ var ja = {
|
|
|
2527
3080
|
default:
|
|
2528
3081
|
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
3082
|
}
|
|
2530
|
-
}
|
|
3083
|
+
},
|
|
3084
|
+
recursionUnsupported: (p, where) => {
|
|
3085
|
+
switch (where) {
|
|
3086
|
+
case "binding":
|
|
3087
|
+
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`;
|
|
3088
|
+
case "watch":
|
|
3089
|
+
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`;
|
|
3090
|
+
case "resolve":
|
|
3091
|
+
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`;
|
|
3092
|
+
case "assignment":
|
|
3093
|
+
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`;
|
|
3094
|
+
case "postUpdate":
|
|
3095
|
+
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`;
|
|
3096
|
+
case "trackDependency":
|
|
3097
|
+
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`;
|
|
3098
|
+
case "listKeys":
|
|
3099
|
+
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`;
|
|
3100
|
+
case "scan":
|
|
3101
|
+
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`;
|
|
3102
|
+
default:
|
|
3103
|
+
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`;
|
|
3104
|
+
}
|
|
3105
|
+
},
|
|
3106
|
+
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`,
|
|
3107
|
+
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`,
|
|
3108
|
+
recursionSetAllForm: (p, problem) => {
|
|
3109
|
+
switch (problem) {
|
|
3110
|
+
case "prefix":
|
|
3111
|
+
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`;
|
|
3112
|
+
case "noIndexes":
|
|
3113
|
+
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`;
|
|
3114
|
+
case "mapper":
|
|
3115
|
+
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`;
|
|
3116
|
+
default:
|
|
3117
|
+
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`;
|
|
3118
|
+
}
|
|
3119
|
+
},
|
|
3120
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
3121
|
+
switch (target) {
|
|
3122
|
+
case "node":
|
|
3123
|
+
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`;
|
|
3124
|
+
case "branch":
|
|
3125
|
+
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`;
|
|
3126
|
+
case "length":
|
|
3127
|
+
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`;
|
|
3128
|
+
default:
|
|
3129
|
+
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`;
|
|
3130
|
+
}
|
|
3131
|
+
},
|
|
3132
|
+
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`,
|
|
3133
|
+
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`,
|
|
3134
|
+
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`,
|
|
3135
|
+
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`,
|
|
3136
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
3137
|
+
const subject = kind === "anchor" ? "$recursion \u306E\u30A2\u30F3\u30AB\u30FC" : "$recursion \u306E\u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9";
|
|
3138
|
+
switch (problem) {
|
|
3139
|
+
case "empty":
|
|
3140
|
+
return `${subject}\u306F\u7A7A\u3067\u306A\u3044\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`;
|
|
3141
|
+
case "emptySegment":
|
|
3142
|
+
return `${subject} "${path}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`;
|
|
3143
|
+
case "notElement":
|
|
3144
|
+
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`;
|
|
3145
|
+
case "reservedRoot":
|
|
3146
|
+
return `${subject} "${path}" \u306F "$" \u3067\u59CB\u3081\u3089\u308C\u307E\u305B\u3093\uFF08\u4E88\u7D04\u540D\u524D\u7A7A\u9593\uFF09`;
|
|
3147
|
+
case "reservedMount":
|
|
3148
|
+
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`;
|
|
3149
|
+
case "midWildcard":
|
|
3150
|
+
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`;
|
|
3151
|
+
case "indexSegment":
|
|
3152
|
+
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`;
|
|
3153
|
+
default:
|
|
3154
|
+
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`;
|
|
3155
|
+
}
|
|
3156
|
+
},
|
|
3157
|
+
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`,
|
|
3158
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
3159
|
+
switch (problem) {
|
|
3160
|
+
case "setter":
|
|
3161
|
+
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`;
|
|
3162
|
+
case "notGetter":
|
|
3163
|
+
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`;
|
|
3164
|
+
case "structural":
|
|
3165
|
+
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`;
|
|
3166
|
+
default:
|
|
3167
|
+
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`;
|
|
3168
|
+
}
|
|
3169
|
+
},
|
|
3170
|
+
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`,
|
|
3171
|
+
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`,
|
|
3172
|
+
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
3173
|
};
|
|
2532
3174
|
var EN_EXPECTED_LABEL = {
|
|
2533
3175
|
array: "an array-typed path",
|
|
@@ -2571,6 +3213,33 @@ var en = {
|
|
|
2571
3213
|
watchKeyEmptySegment: (k) => `$watch key "${k}" has an empty path segment`,
|
|
2572
3214
|
watchHandlerNotFunction: (k) => `The value of $watch entry "${k}" must be a function`,
|
|
2573
3215
|
watchPathMissing: (k) => `$watch key "${k}" does not exist in the state definition (it will never fire)`,
|
|
3216
|
+
scanNotObject: () => `$scan must be an object mapping output names to { from | on, initial, fold, resetOn? } (the runtime throws on this shape at load time)`,
|
|
3217
|
+
scanOutputInvalid: (n) => `$scan output name "${n}" must be a flat property name ("." and "*" and a leading "$" are not allowed)`,
|
|
3218
|
+
scanOutputReserved: (n) => `$scan output name "${n}" is a property name inherited from Object.prototype (e.g. "constructor")`,
|
|
3219
|
+
scanOutputEmpty: () => `$scan output name must be a non-empty string`,
|
|
3220
|
+
scanInVolume: (mountPath) => `$scan cannot be declared in a volume (mount="${mountPath}"); the runtime throws before grafting. Declare the scan on the root state`,
|
|
3221
|
+
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`,
|
|
3222
|
+
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)`,
|
|
3223
|
+
scanEntryNotObject: (n) => `$scan entry "${n}" must be an object { from | on, initial, fold, resetOn? }`,
|
|
3224
|
+
scanSourceCount: (n) => `$scan entry "${n}" must declare exactly one of "from" (a state path) or "on" (an event-token name)`,
|
|
3225
|
+
scanFromNotString: (n) => `$scan entry "${n}" "from" must be a non-empty state path string`,
|
|
3226
|
+
scanOnNotString: (n) => `$scan entry "${n}" "on" must be a non-empty event-token name`,
|
|
3227
|
+
scanResetNotString: (n) => `$scan entry "${n}" "resetOn" must contain only state path strings`,
|
|
3228
|
+
scanOutputCycle: (chain) => `$scan entries ${chain.map((c) => `"${c}"`).join(" \u2192 ")} feed each other through "from" (each fold would re-trigger the next forever)`,
|
|
3229
|
+
scanInitialMissing: (n) => `$scan entry "${n}" requires "initial" (the seed of the accumulator and the value resetOn returns to)`,
|
|
3230
|
+
scanFoldNotFunction: (n) => `$scan entry "${n}" fold must be a function`,
|
|
3231
|
+
scanOnUndeclared: (n, t) => `$scan entry "${n}" on "${t}" is not declared in $eventTokens`,
|
|
3232
|
+
scanPathInvalid: (n, f, p) => `$scan entry "${n}" ${f} "${p}" is not a valid state path (a leading "$", "@" and empty segments are not allowed)`,
|
|
3233
|
+
scanPathReserved: (n, f, p) => `$scan entry "${n}" ${f} "${p}" is a property name inherited from Object.prototype (e.g. "constructor")`,
|
|
3234
|
+
scanFromSelf: (n, p) => `$scan entry "${n}" from "${p}" reads the entry's own output (it would fold its own writes forever)`,
|
|
3235
|
+
scanResetNotArray: (n) => `$scan entry "${n}" "resetOn" must be an array of state paths`,
|
|
3236
|
+
scanResetWildcard: (n, p) => `$scan entry "${n}" resetOn "${p}" must not contain "*" (a reset returns the whole output to initial)`,
|
|
3237
|
+
scanResetIsFrom: (n, p) => `$scan entry "${n}" resetOn "${p}" is the entry's own from (every change would reset instead of fold)`,
|
|
3238
|
+
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)`,
|
|
3239
|
+
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`,
|
|
3240
|
+
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`,
|
|
3241
|
+
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`,
|
|
3242
|
+
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"})`,
|
|
2574
3243
|
typeAnnotationIncompatible: (vt, rt) => `Type "${vt}" is not compatible with @type {${rt}}`,
|
|
2575
3244
|
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}).`,
|
|
2576
3245
|
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.`,
|
|
@@ -2599,7 +3268,96 @@ var en = {
|
|
|
2599
3268
|
default:
|
|
2600
3269
|
return `"mount" path "${mountPath}" must not use reserved characters ($, #, @).`;
|
|
2601
3270
|
}
|
|
2602
|
-
}
|
|
3271
|
+
},
|
|
3272
|
+
recursionUnsupported: (p, where) => {
|
|
3273
|
+
switch (where) {
|
|
3274
|
+
case "binding":
|
|
3275
|
+
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`;
|
|
3276
|
+
case "watch":
|
|
3277
|
+
return `$watch key "${p}" cannot contain "**" \u2014 watching is defined against a concrete path (a fixed number of "*")`;
|
|
3278
|
+
case "resolve":
|
|
3279
|
+
return `$resolve("${p}") cannot take "**" \u2014 it accepts only an expanded concrete path with an exactly matching index tuple`;
|
|
3280
|
+
case "assignment":
|
|
3281
|
+
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`;
|
|
3282
|
+
case "postUpdate":
|
|
3283
|
+
return `$postUpdate("${p}") cannot take "**" \u2014 a notification is defined against a concrete path (a fixed number of "*")`;
|
|
3284
|
+
case "trackDependency":
|
|
3285
|
+
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
3286
|
+
case "listKeys":
|
|
3287
|
+
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")`;
|
|
3288
|
+
case "scan":
|
|
3289
|
+
return `$scan path "${p}" cannot contain "**" \u2014 "from" and "resetOn" name a concrete path (a fixed number of "*")`;
|
|
3290
|
+
default:
|
|
3291
|
+
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`;
|
|
3292
|
+
}
|
|
3293
|
+
},
|
|
3294
|
+
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 "**")`,
|
|
3295
|
+
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`,
|
|
3296
|
+
recursionSetAllForm: (p, problem) => {
|
|
3297
|
+
switch (problem) {
|
|
3298
|
+
case "prefix":
|
|
3299
|
+
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`;
|
|
3300
|
+
case "noIndexes":
|
|
3301
|
+
return `$setAll("${p}", \u2026) with "**" requires an explicit empty indexes array ([]) \u2014 the write API takes no context`;
|
|
3302
|
+
case "mapper":
|
|
3303
|
+
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`;
|
|
3304
|
+
default:
|
|
3305
|
+
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`;
|
|
3306
|
+
}
|
|
3307
|
+
},
|
|
3308
|
+
recursionStructuralWrite: (p, target, repeatList) => {
|
|
3309
|
+
switch (target) {
|
|
3310
|
+
case "node":
|
|
3311
|
+
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`;
|
|
3312
|
+
case "branch":
|
|
3313
|
+
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`;
|
|
3314
|
+
case "length":
|
|
3315
|
+
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`;
|
|
3316
|
+
default:
|
|
3317
|
+
return `$setAll("${p}") writes the recursion structure itself (the "${repeatList}" list). This version broadcasts to leaf properties only`;
|
|
3318
|
+
}
|
|
3319
|
+
},
|
|
3320
|
+
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`,
|
|
3321
|
+
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`,
|
|
3322
|
+
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)`,
|
|
3323
|
+
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`,
|
|
3324
|
+
recursionNodePathInvalid: (kind, path, problem) => {
|
|
3325
|
+
const subject = kind === "anchor" ? "$recursion anchor" : "$recursion repeating sub-path";
|
|
3326
|
+
switch (problem) {
|
|
3327
|
+
case "empty":
|
|
3328
|
+
return `${subject} must be a non-empty string`;
|
|
3329
|
+
case "emptySegment":
|
|
3330
|
+
return `${subject} "${path}" must not contain empty path segments`;
|
|
3331
|
+
case "notElement":
|
|
3332
|
+
return `${subject} "${path}" must name a list element: a property path ending with ".*" (for example "nodes.*")`;
|
|
3333
|
+
case "reservedRoot":
|
|
3334
|
+
return `${subject} "${path}" must not start with "$" \u2014 that namespace is reserved`;
|
|
3335
|
+
case "reservedMount":
|
|
3336
|
+
return `${subject} "${path}" must not contain "#" \u2014 that segment is reserved for mounts`;
|
|
3337
|
+
case "midWildcard":
|
|
3338
|
+
return `${subject} "${path}" must have exactly one "*", at the end (wildcards in the middle are not supported in this version)`;
|
|
3339
|
+
case "indexSegment":
|
|
3340
|
+
return `${subject} "${path}" must not contain an index segment \u2014 the recursion is declared over the shape of the tree, not over one row`;
|
|
3341
|
+
default:
|
|
3342
|
+
return `${subject} "${path}" must not contain "**" \u2014 the declaration is what gives "**" its meaning`;
|
|
3343
|
+
}
|
|
3344
|
+
},
|
|
3345
|
+
recursionRepeatNotString: (anchor) => `$recursion entry "${anchor}" must map to the repeating sub-path as a string (for example "children.*")`,
|
|
3346
|
+
recursionGetterInvalid: (key, problem, anchor) => {
|
|
3347
|
+
switch (problem) {
|
|
3348
|
+
case "setter":
|
|
3349
|
+
return `Recursive setters are not supported in this version: "${key}". Declare a plain path setter, or write through the concrete path`;
|
|
3350
|
+
case "notGetter":
|
|
3351
|
+
return `"${key}" contains "**" but is not a getter. The recursion wildcard only names a family of computed paths`;
|
|
3352
|
+
case "structural":
|
|
3353
|
+
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")`;
|
|
3354
|
+
default:
|
|
3355
|
+
return `"${key}" names the recursive node itself. "**" names a computed path under a node (for example "${anchor}.total"), not the node`;
|
|
3356
|
+
}
|
|
3357
|
+
},
|
|
3358
|
+
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`,
|
|
3359
|
+
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`,
|
|
3360
|
+
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
3361
|
};
|
|
2604
3362
|
var CATALOGS = { ja, en };
|
|
2605
3363
|
function getMessages(locale3) {
|
|
@@ -2729,7 +3487,11 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2729
3487
|
}
|
|
2730
3488
|
if (checkPath) {
|
|
2731
3489
|
const schema = applicationSchema;
|
|
2732
|
-
const verdict =
|
|
3490
|
+
const verdict = hasRecursionWildcard(checkPath) ? {
|
|
3491
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
3492
|
+
message: msgs.recursionUnsupported(checkPath, "binding"),
|
|
3493
|
+
severity: "error"
|
|
3494
|
+
} : schema !== void 0 ? validateSchemaPathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, schema, msgs) : toMissingVerdict(validatePathExistence(checkPath, pathTrimmed, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
2733
3495
|
if (verdict) {
|
|
2734
3496
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2735
3497
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -2748,7 +3510,7 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale3, f
|
|
|
2748
3510
|
const pathTrimmed = parsed.path.trim();
|
|
2749
3511
|
const prop = parsed.property.replace(/#.*$/, "");
|
|
2750
3512
|
const insideFor = isInsideForTemplate(html, attr.valueStart, attrName);
|
|
2751
|
-
if (pathTrimmed && !prop.startsWith("on")) {
|
|
3513
|
+
if (pathTrimmed && !prop.startsWith("on") && !hasRecursionWildcard(pathTrimmed)) {
|
|
2752
3514
|
if (!insideFor && pathTrimmed.includes("*")) {
|
|
2753
3515
|
const pathOffset = binding.indexOf(parsed.path);
|
|
2754
3516
|
const pathStart = bindingStart + pathOffset;
|
|
@@ -3028,11 +3790,16 @@ function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSe
|
|
|
3028
3790
|
}
|
|
3029
3791
|
return null;
|
|
3030
3792
|
}
|
|
3031
|
-
if (!scopedPathSet.has(checkPath)) {
|
|
3793
|
+
if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
|
|
3032
3794
|
return msgs.pathMissing(displayPath);
|
|
3033
3795
|
}
|
|
3034
3796
|
return null;
|
|
3035
3797
|
}
|
|
3798
|
+
function matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet) {
|
|
3799
|
+
const specs = collectRecursionSpecs(scopedPaths);
|
|
3800
|
+
if (specs.length === 0) return false;
|
|
3801
|
+
return matchesRecursion(specs, checkPath, (candidate) => scopedPathSet.has(candidate));
|
|
3802
|
+
}
|
|
3036
3803
|
function toMissingVerdict(message) {
|
|
3037
3804
|
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
3038
3805
|
}
|
|
@@ -3041,6 +3808,7 @@ function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scoped
|
|
|
3041
3808
|
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
3042
3809
|
}
|
|
3043
3810
|
if (scopedPathSet.has(checkPath)) return null;
|
|
3811
|
+
if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
|
|
3044
3812
|
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
3045
3813
|
if (resolution.kind === "nonexistent") {
|
|
3046
3814
|
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
@@ -3454,6 +4222,13 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
3454
4222
|
const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
|
|
3455
4223
|
const defaultSchema = applicationSchema;
|
|
3456
4224
|
const missingVerdict = (path, displayPath, pathSet2, scoped) => {
|
|
4225
|
+
if (hasRecursionWildcard(path)) {
|
|
4226
|
+
return {
|
|
4227
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
4228
|
+
severity: "error",
|
|
4229
|
+
message: msgs.recursionUnsupported(path, "binding")
|
|
4230
|
+
};
|
|
4231
|
+
}
|
|
3457
4232
|
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
3458
4233
|
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
3459
4234
|
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
@@ -3586,7 +4361,7 @@ function isValidTemplatePath(path, pathSet, scopedPaths) {
|
|
|
3586
4361
|
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
3587
4362
|
return !hasNamespace || pathSet.has(path);
|
|
3588
4363
|
}
|
|
3589
|
-
return pathSet.has(path);
|
|
4364
|
+
return pathSet.has(path) || matchesRecursionCandidates(scopedPaths, path, pathSet);
|
|
3590
4365
|
}
|
|
3591
4366
|
|
|
3592
4367
|
// src/service/generated/builtinTags.generated.ts
|
|
@@ -5258,96 +6033,873 @@ function collectSignalsRefs(html) {
|
|
|
5258
6033
|
}
|
|
5259
6034
|
continue;
|
|
5260
6035
|
}
|
|
5261
|
-
if (!/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
|
|
5262
|
-
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
5263
|
-
const body = blankJsComments(match[2]);
|
|
5264
|
-
const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*@wcstack\/signals[^"']*)\1/g;
|
|
5265
|
-
let im;
|
|
5266
|
-
while ((im = importRegex.exec(body)) !== null) {
|
|
5267
|
-
const kind = classifySignalsSpecifier(im[2]);
|
|
5268
|
-
if (!kind) continue;
|
|
5269
|
-
const specStart = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
|
|
5270
|
-
refs.push({ kind, start: specStart, end: specStart + im[2].length });
|
|
6036
|
+
if (!/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
|
|
6037
|
+
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
6038
|
+
const body = blankJsComments(match[2]);
|
|
6039
|
+
const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*@wcstack\/signals[^"']*)\1/g;
|
|
6040
|
+
let im;
|
|
6041
|
+
while ((im = importRegex.exec(body)) !== null) {
|
|
6042
|
+
const kind = classifySignalsSpecifier(im[2]);
|
|
6043
|
+
if (!kind) continue;
|
|
6044
|
+
const specStart = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
|
|
6045
|
+
refs.push({ kind, start: specStart, end: specStart + im[2].length });
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
return refs;
|
|
6049
|
+
}
|
|
6050
|
+
function classifySignalsSpecifier(spec) {
|
|
6051
|
+
if (!spec.includes("@wcstack/signals")) return null;
|
|
6052
|
+
return /@wcstack\/signals\/dom\b/.test(spec) ? "dom" : "bare";
|
|
6053
|
+
}
|
|
6054
|
+
function extractSrc(openTag) {
|
|
6055
|
+
const srcMatch = /\bsrc\s*=\s*(["'])(.*?)\1/i.exec(openTag);
|
|
6056
|
+
if (!srcMatch) return null;
|
|
6057
|
+
return {
|
|
6058
|
+
value: srcMatch[2],
|
|
6059
|
+
offsetInTag: srcMatch.index + srcMatch[0].indexOf(srcMatch[1]) + 1
|
|
6060
|
+
};
|
|
6061
|
+
}
|
|
6062
|
+
function blankHtmlComments(html) {
|
|
6063
|
+
return html.replace(/<!--[\s\S]*?-->/g, (m) => " ".repeat(m.length));
|
|
6064
|
+
}
|
|
6065
|
+
function blankJsComments(code) {
|
|
6066
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/(^|[^:])\/\/[^\n]*/g, (m, pre) => pre + " ".repeat(m.length - pre.length));
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
// src/service/watchDeclarationValidator.ts
|
|
6070
|
+
var STATE_NAME_SEPARATOR = "@";
|
|
6071
|
+
function validateWatchDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
6072
|
+
const msgs = getMessages(locale3);
|
|
6073
|
+
const out = [];
|
|
6074
|
+
for (const block of parseWcsScriptBlocks(html, stateTagName)) {
|
|
6075
|
+
const nonObject = findNonObjectWatch(block.content);
|
|
6076
|
+
if (nonObject !== null) {
|
|
6077
|
+
out.push({
|
|
6078
|
+
code: WcsDiagnosticCode.WatchDeclarationInvalid,
|
|
6079
|
+
start: block.contentStart + nonObject.start,
|
|
6080
|
+
end: block.contentStart + nonObject.end,
|
|
6081
|
+
message: msgs.watchNotObject(),
|
|
6082
|
+
severity: "error"
|
|
6083
|
+
});
|
|
6084
|
+
}
|
|
6085
|
+
const entries = analyzeWatchEntries(block.content);
|
|
6086
|
+
if (entries.length === 0) continue;
|
|
6087
|
+
const paths = analyzeStatePaths(block.content);
|
|
6088
|
+
const pathSet = new Set(paths.map((p) => p.path));
|
|
6089
|
+
for (const entry of entries) {
|
|
6090
|
+
const diagnostic = validateEntry(entry, pathSet, paths, msgs);
|
|
6091
|
+
if (diagnostic === null) continue;
|
|
6092
|
+
out.push({
|
|
6093
|
+
code: diagnostic.code,
|
|
6094
|
+
start: block.contentStart + entry.start,
|
|
6095
|
+
end: block.contentStart + entry.end,
|
|
6096
|
+
message: diagnostic.message,
|
|
6097
|
+
severity: diagnostic.severity
|
|
6098
|
+
});
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
return out;
|
|
6102
|
+
}
|
|
6103
|
+
function validateEntry(entry, pathSet, paths, msgs) {
|
|
6104
|
+
const { key } = entry;
|
|
6105
|
+
const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
|
|
6106
|
+
if (key.includes(STATE_NAME_SEPARATOR)) {
|
|
6107
|
+
return invalid(msgs.watchKeyCrossState(key));
|
|
6108
|
+
}
|
|
6109
|
+
if (key.startsWith("$")) {
|
|
6110
|
+
return invalid(msgs.watchKeyReserved(key));
|
|
6111
|
+
}
|
|
6112
|
+
if (key.split(".").some((segment) => segment.length === 0)) {
|
|
6113
|
+
return invalid(msgs.watchKeyEmptySegment(key));
|
|
6114
|
+
}
|
|
6115
|
+
if (hasRecursionWildcard(key)) {
|
|
6116
|
+
return {
|
|
6117
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
6118
|
+
message: msgs.recursionUnsupported(key, "watch"),
|
|
6119
|
+
severity: "error"
|
|
6120
|
+
};
|
|
6121
|
+
}
|
|
6122
|
+
if (entry.definitelyNotFunction) {
|
|
6123
|
+
return invalid(msgs.watchHandlerNotFunction(key));
|
|
6124
|
+
}
|
|
6125
|
+
if (pathSet.size > 0 && !pathSet.has(key) && !matchesRecursion(collectRecursionSpecs(paths), key, (p) => pathSet.has(p))) {
|
|
6126
|
+
return {
|
|
6127
|
+
code: WcsDiagnosticCode.WatchPathMissing,
|
|
6128
|
+
message: msgs.watchPathMissing(key),
|
|
6129
|
+
severity: "warning"
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
return null;
|
|
6133
|
+
}
|
|
6134
|
+
|
|
6135
|
+
// src/service/scanDeclarationValidator.ts
|
|
6136
|
+
function validateScanDeclarations(html, stateTagName = "wcs-state", locale3) {
|
|
6137
|
+
const msgs = getMessages(locale3);
|
|
6138
|
+
const out = [];
|
|
6139
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
6140
|
+
const mounted = element.bindComponent;
|
|
6141
|
+
for (const block of element.scriptBlocks) {
|
|
6142
|
+
if (block.mountPath !== null || mounted) {
|
|
6143
|
+
const declaration = analyzeDeclarationSpans(block.content).find((span) => span.name === "$scan");
|
|
6144
|
+
if (declaration !== void 0) {
|
|
6145
|
+
out.push({
|
|
6146
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
6147
|
+
start: block.contentStart + declaration.start,
|
|
6148
|
+
end: block.contentStart + declaration.end,
|
|
6149
|
+
message: block.mountPath !== null ? msgs.scanInVolume(block.mountPath) : msgs.scanInMountedComponent(),
|
|
6150
|
+
severity: block.mountPath !== null ? "error" : "warning"
|
|
6151
|
+
});
|
|
6152
|
+
}
|
|
6153
|
+
continue;
|
|
6154
|
+
}
|
|
6155
|
+
const nonObject = findNonObjectScan(block.content);
|
|
6156
|
+
if (nonObject !== null) {
|
|
6157
|
+
out.push({
|
|
6158
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
6159
|
+
start: block.contentStart + nonObject.start,
|
|
6160
|
+
end: block.contentStart + nonObject.end,
|
|
6161
|
+
message: msgs.scanNotObject(),
|
|
6162
|
+
severity: "error"
|
|
6163
|
+
});
|
|
6164
|
+
}
|
|
6165
|
+
const entries = analyzeScanEntries(block.content);
|
|
6166
|
+
if (entries.length === 0) continue;
|
|
6167
|
+
const paths = analyzeStatePaths(block.content);
|
|
6168
|
+
const context = {
|
|
6169
|
+
paths,
|
|
6170
|
+
pathSet: new Set(paths.map((p) => p.path)),
|
|
6171
|
+
recursionSpecs: collectRecursionSpecs(paths),
|
|
6172
|
+
tokens: readEventTokenNames(block.content),
|
|
6173
|
+
outputs: new Set(entries.map((entry) => entry.name)),
|
|
6174
|
+
msgs
|
|
6175
|
+
};
|
|
6176
|
+
const diagnostics = [
|
|
6177
|
+
...entries.flatMap((entry) => validateEntry2(entry, context)),
|
|
6178
|
+
...findOutputCycles(entries, context)
|
|
6179
|
+
];
|
|
6180
|
+
for (const diagnostic of diagnostics) {
|
|
6181
|
+
out.push({
|
|
6182
|
+
code: diagnostic.code,
|
|
6183
|
+
start: block.contentStart + diagnostic.start,
|
|
6184
|
+
end: block.contentStart + diagnostic.end,
|
|
6185
|
+
message: diagnostic.message,
|
|
6186
|
+
severity: diagnostic.severity
|
|
6187
|
+
});
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
}
|
|
6191
|
+
return out;
|
|
6192
|
+
}
|
|
6193
|
+
function validateOutput(entry, context) {
|
|
6194
|
+
const { name } = entry;
|
|
6195
|
+
const invalid = (message) => ({ code: WcsDiagnosticCode.ScanDeclarationInvalid, message, severity: "error", start: entry.start, end: entry.end });
|
|
6196
|
+
if (name.length === 0) {
|
|
6197
|
+
return { ...invalid(context.msgs.scanOutputEmpty()), start: entry.start - 1, end: entry.end + 1 };
|
|
6198
|
+
}
|
|
6199
|
+
if (name.startsWith("$") || name.includes(".") || name.includes("*")) {
|
|
6200
|
+
return invalid(context.msgs.scanOutputInvalid(name));
|
|
6201
|
+
}
|
|
6202
|
+
if (name in Object.prototype) {
|
|
6203
|
+
return invalid(context.msgs.scanOutputReserved(name));
|
|
6204
|
+
}
|
|
6205
|
+
if (context.paths.some((p) => p.path === name && p.kind === "computed")) {
|
|
6206
|
+
return invalid(context.msgs.scanOutputConflict(name, "getter"));
|
|
6207
|
+
}
|
|
6208
|
+
if (context.paths.some((p) => p.path === `$streamStatus.${name}`)) {
|
|
6209
|
+
return invalid(context.msgs.scanOutputConflict(name, "stream"));
|
|
6210
|
+
}
|
|
6211
|
+
if (entry.notObject) {
|
|
6212
|
+
return invalid(context.msgs.scanEntryNotObject(name));
|
|
6213
|
+
}
|
|
6214
|
+
if (context.paths.some((p) => p.path === name && p.kind === "method")) {
|
|
6215
|
+
return invalid(context.msgs.scanOutputConflict(name, "method"));
|
|
6216
|
+
}
|
|
6217
|
+
return null;
|
|
6218
|
+
}
|
|
6219
|
+
function validateEntry2(entry, context) {
|
|
6220
|
+
const outputProblem = validateOutput(entry, context);
|
|
6221
|
+
if (outputProblem !== null) return [outputProblem];
|
|
6222
|
+
if (!entry.readable) return [];
|
|
6223
|
+
const { name } = entry;
|
|
6224
|
+
const { msgs } = context;
|
|
6225
|
+
const out = [];
|
|
6226
|
+
const atName = (message) => ({ code: WcsDiagnosticCode.ScanDeclarationInvalid, message, severity: "error", start: entry.start, end: entry.end });
|
|
6227
|
+
const atField = (field, message) => ({ code: WcsDiagnosticCode.ScanDeclarationInvalid, message, severity: "error", start: field.start, end: field.end });
|
|
6228
|
+
if (entry.hasFrom === entry.hasOn) {
|
|
6229
|
+
out.push(atName(msgs.scanSourceCount(name)));
|
|
6230
|
+
}
|
|
6231
|
+
if (entry.onNotString) {
|
|
6232
|
+
out.push(atName(msgs.scanOnNotString(name)));
|
|
6233
|
+
}
|
|
6234
|
+
if (entry.fromNotString) {
|
|
6235
|
+
out.push(atName(msgs.scanFromNotString(name)));
|
|
6236
|
+
}
|
|
6237
|
+
if (!entry.hasInitial) {
|
|
6238
|
+
out.push(atName(msgs.scanInitialMissing(name)));
|
|
6239
|
+
}
|
|
6240
|
+
if (entry.foldMissingOrNotFunction) {
|
|
6241
|
+
out.push(atName(msgs.scanFoldNotFunction(name)));
|
|
6242
|
+
}
|
|
6243
|
+
if (entry.on !== null && context.tokens !== null && !context.tokens.has(entry.on.value)) {
|
|
6244
|
+
out.push(atField(entry.on, msgs.scanOnUndeclared(name, entry.on.value)));
|
|
6245
|
+
}
|
|
6246
|
+
const from = entry.from;
|
|
6247
|
+
if (from !== null) {
|
|
6248
|
+
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);
|
|
6249
|
+
if (problem !== null) out.push(problem);
|
|
6250
|
+
}
|
|
6251
|
+
if (entry.resetOnNotArray) {
|
|
6252
|
+
out.push(atName(msgs.scanResetNotArray(name)));
|
|
6253
|
+
}
|
|
6254
|
+
if (entry.resetOnHasNonString) {
|
|
6255
|
+
out.push(atName(msgs.scanResetNotString(name)));
|
|
6256
|
+
}
|
|
6257
|
+
for (const reset2 of entry.resetOn ?? []) {
|
|
6258
|
+
const root = reset2.value.split(".")[0];
|
|
6259
|
+
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);
|
|
6260
|
+
if (problem !== null) out.push(problem);
|
|
6261
|
+
}
|
|
6262
|
+
return out;
|
|
6263
|
+
}
|
|
6264
|
+
function checkPathForm(name, field, target, msgs) {
|
|
6265
|
+
const path = target.value;
|
|
6266
|
+
const at2 = (code, message) => ({ code, message, severity: "error", start: target.start, end: target.end });
|
|
6267
|
+
if (path.length === 0 || path.startsWith("$") || path.includes("@")) {
|
|
6268
|
+
return at2(WcsDiagnosticCode.ScanDeclarationInvalid, msgs.scanPathInvalid(name, field, path));
|
|
6269
|
+
}
|
|
6270
|
+
if (path in Object.prototype) {
|
|
6271
|
+
return at2(WcsDiagnosticCode.ScanDeclarationInvalid, msgs.scanPathReserved(name, field, path));
|
|
6272
|
+
}
|
|
6273
|
+
if (hasRecursionWildcard(path)) {
|
|
6274
|
+
return at2(WcsDiagnosticCode.RecursionUnsupported, msgs.recursionUnsupported(path, "scan"));
|
|
6275
|
+
}
|
|
6276
|
+
if (path.split(".").some((segment) => segment.length === 0)) {
|
|
6277
|
+
return at2(WcsDiagnosticCode.ScanDeclarationInvalid, msgs.scanPathInvalid(name, field, path));
|
|
6278
|
+
}
|
|
6279
|
+
return null;
|
|
6280
|
+
}
|
|
6281
|
+
function checkPathComputed(name, field, target, context) {
|
|
6282
|
+
const segments = target.value.split(".");
|
|
6283
|
+
const computed = new Set(context.paths.filter((p) => p.kind === "computed" && p.writeOnly !== true).map((p) => p.path));
|
|
6284
|
+
const at2 = (getter) => ({
|
|
6285
|
+
code: WcsDiagnosticCode.ScanSourceComputed,
|
|
6286
|
+
message: context.msgs.scanSourceComputed(name, field, target.value, getter),
|
|
6287
|
+
severity: "error",
|
|
6288
|
+
start: target.start,
|
|
6289
|
+
end: target.end
|
|
6290
|
+
});
|
|
6291
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
6292
|
+
const prefix = segments.slice(0, i).join(".");
|
|
6293
|
+
if (computed.has(prefix)) {
|
|
6294
|
+
return at2(prefix);
|
|
6295
|
+
}
|
|
6296
|
+
}
|
|
6297
|
+
const recursive = new Set(context.paths.filter((p) => p.kind === "recursive").map((p) => p.path));
|
|
6298
|
+
const found = { getter: null };
|
|
6299
|
+
matchesRecursion(context.recursionSpecs, target.value, (candidate) => {
|
|
6300
|
+
if (!recursive.has(candidate)) return false;
|
|
6301
|
+
found.getter = candidate;
|
|
6302
|
+
return true;
|
|
6303
|
+
});
|
|
6304
|
+
return found.getter === null ? null : at2(found.getter);
|
|
6305
|
+
}
|
|
6306
|
+
function checkFromWriteOnly(name, target, context) {
|
|
6307
|
+
const writeOnly = new Set(context.paths.filter((p) => p.writeOnly === true).map((p) => p.path));
|
|
6308
|
+
const segments = target.value.split(".");
|
|
6309
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
6310
|
+
const prefix = segments.slice(0, i).join(".");
|
|
6311
|
+
if (writeOnly.has(prefix)) {
|
|
6312
|
+
return {
|
|
6313
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
6314
|
+
message: context.msgs.scanFromWriteOnly(name, target.value, prefix),
|
|
6315
|
+
severity: "error",
|
|
6316
|
+
start: target.start,
|
|
6317
|
+
end: target.end
|
|
6318
|
+
};
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
return null;
|
|
6322
|
+
}
|
|
6323
|
+
function checkPathMissing(name, field, target, context) {
|
|
6324
|
+
const { paths, pathSet, recursionSpecs } = context;
|
|
6325
|
+
if (paths.length > 0 && !pathSet.has(target.value) && // `$recursion` の展開形は、宣言済みの候補へ畳めれば存在する(`$watch` と同じ照合)
|
|
6326
|
+
!matchesRecursion(recursionSpecs, target.value, (candidate) => pathSet.has(candidate))) {
|
|
6327
|
+
return {
|
|
6328
|
+
code: WcsDiagnosticCode.ScanPathMissing,
|
|
6329
|
+
message: context.msgs.scanPathMissing(name, field, target.value),
|
|
6330
|
+
severity: "warning",
|
|
6331
|
+
start: target.start,
|
|
6332
|
+
end: target.end
|
|
6333
|
+
};
|
|
6334
|
+
}
|
|
6335
|
+
return null;
|
|
6336
|
+
}
|
|
6337
|
+
function findOutputCycles(entries, context) {
|
|
6338
|
+
const fromByName = /* @__PURE__ */ new Map();
|
|
6339
|
+
const next = /* @__PURE__ */ new Map();
|
|
6340
|
+
for (const entry of entries) {
|
|
6341
|
+
if (entry.from === null) continue;
|
|
6342
|
+
const root = entry.from.value.split(".")[0];
|
|
6343
|
+
if (root !== entry.name && context.outputs.has(root)) {
|
|
6344
|
+
fromByName.set(entry.name, entry.from);
|
|
6345
|
+
next.set(entry.name, root);
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
const out = [];
|
|
6349
|
+
for (const [start, from] of fromByName) {
|
|
6350
|
+
const chain = [start];
|
|
6351
|
+
let current2 = next.get(start);
|
|
6352
|
+
while (current2 !== void 0 && current2 !== start && chain.length <= next.size) {
|
|
6353
|
+
chain.push(current2);
|
|
6354
|
+
current2 = next.get(current2);
|
|
6355
|
+
}
|
|
6356
|
+
if (current2 === start) {
|
|
6357
|
+
out.push({
|
|
6358
|
+
code: WcsDiagnosticCode.ScanDeclarationInvalid,
|
|
6359
|
+
message: context.msgs.scanOutputCycle([...chain, start]),
|
|
6360
|
+
severity: "error",
|
|
6361
|
+
start: from.start,
|
|
6362
|
+
end: from.end
|
|
6363
|
+
});
|
|
6364
|
+
}
|
|
6365
|
+
}
|
|
6366
|
+
return out;
|
|
6367
|
+
}
|
|
6368
|
+
|
|
6369
|
+
// src/service/scriptCallArgs.ts
|
|
6370
|
+
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
6371
|
+
var TEMPLATE_NO_SUBST = /^\s*`((?:\\.|[^\\`$]|\$(?!\{))*)`\s*$/;
|
|
6372
|
+
function splitCallArgs(source, open) {
|
|
6373
|
+
const args = [];
|
|
6374
|
+
const starts = [];
|
|
6375
|
+
let depth = 0;
|
|
6376
|
+
let argStart = open;
|
|
6377
|
+
let i = open;
|
|
6378
|
+
while (i < source.length) {
|
|
6379
|
+
const ch = source[i];
|
|
6380
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
6381
|
+
const quote = ch;
|
|
6382
|
+
i++;
|
|
6383
|
+
while (i < source.length) {
|
|
6384
|
+
if (source[i] === "\\") {
|
|
6385
|
+
i += 2;
|
|
6386
|
+
continue;
|
|
6387
|
+
}
|
|
6388
|
+
if (source[i] === quote) {
|
|
6389
|
+
i++;
|
|
6390
|
+
break;
|
|
6391
|
+
}
|
|
6392
|
+
i++;
|
|
6393
|
+
}
|
|
6394
|
+
continue;
|
|
6395
|
+
}
|
|
6396
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
6397
|
+
depth++;
|
|
6398
|
+
i++;
|
|
6399
|
+
continue;
|
|
6400
|
+
}
|
|
6401
|
+
if (ch === ")" && depth === 0) {
|
|
6402
|
+
args.push(source.slice(argStart, i));
|
|
6403
|
+
starts.push(argStart);
|
|
6404
|
+
return { args, starts, end: i + 1 };
|
|
6405
|
+
}
|
|
6406
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
6407
|
+
depth--;
|
|
6408
|
+
i++;
|
|
6409
|
+
continue;
|
|
6410
|
+
}
|
|
6411
|
+
if (ch === "," && depth === 0) {
|
|
6412
|
+
args.push(source.slice(argStart, i));
|
|
6413
|
+
starts.push(argStart);
|
|
6414
|
+
argStart = i + 1;
|
|
6415
|
+
i++;
|
|
6416
|
+
continue;
|
|
6417
|
+
}
|
|
6418
|
+
i++;
|
|
6419
|
+
}
|
|
6420
|
+
return null;
|
|
6421
|
+
}
|
|
6422
|
+
function literalString(arg) {
|
|
6423
|
+
const match = STRING_LITERAL.exec(arg);
|
|
6424
|
+
if (match !== null) return match[2];
|
|
6425
|
+
const template = TEMPLATE_NO_SUBST.exec(arg);
|
|
6426
|
+
return template === null ? null : template[1];
|
|
6427
|
+
}
|
|
6428
|
+
function literalArrayLength(arg) {
|
|
6429
|
+
const trimmed = arg.trim();
|
|
6430
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
6431
|
+
const inner = trimmed.slice(1, -1);
|
|
6432
|
+
if (inner.trim().length === 0) return 0;
|
|
6433
|
+
if (/(^|[^.])\.\.\./.test(inner)) return null;
|
|
6434
|
+
const parts = splitCallArgs(`${inner})`, 0);
|
|
6435
|
+
if (parts === null) return null;
|
|
6436
|
+
return parts.args.filter((part) => part.trim().length > 0).length;
|
|
6437
|
+
}
|
|
6438
|
+
function blankComments(source) {
|
|
6439
|
+
const out = source.split("");
|
|
6440
|
+
let i = 0;
|
|
6441
|
+
while (i < source.length) {
|
|
6442
|
+
const ch = source[i];
|
|
6443
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
6444
|
+
const quote = ch;
|
|
6445
|
+
i++;
|
|
6446
|
+
while (i < source.length) {
|
|
6447
|
+
if (source[i] === "\\") {
|
|
6448
|
+
i += 2;
|
|
6449
|
+
continue;
|
|
6450
|
+
}
|
|
6451
|
+
if (source[i] === quote) {
|
|
6452
|
+
i++;
|
|
6453
|
+
break;
|
|
6454
|
+
}
|
|
6455
|
+
i++;
|
|
6456
|
+
}
|
|
6457
|
+
continue;
|
|
6458
|
+
}
|
|
6459
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
6460
|
+
while (i < source.length && source[i] !== "\n") {
|
|
6461
|
+
out[i] = " ";
|
|
6462
|
+
i++;
|
|
6463
|
+
}
|
|
6464
|
+
continue;
|
|
6465
|
+
}
|
|
6466
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
6467
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
6468
|
+
out[i] = " ";
|
|
6469
|
+
i++;
|
|
6470
|
+
}
|
|
6471
|
+
if (i < source.length) {
|
|
6472
|
+
out[i] = " ";
|
|
6473
|
+
out[i + 1] = " ";
|
|
6474
|
+
i += 2;
|
|
6475
|
+
}
|
|
6476
|
+
continue;
|
|
6477
|
+
}
|
|
6478
|
+
i++;
|
|
6479
|
+
}
|
|
6480
|
+
return out.join("");
|
|
6481
|
+
}
|
|
6482
|
+
function createApiCallRegex(apis) {
|
|
6483
|
+
return new RegExp(`\\.\\s*\\$(${apis.join("|")})\\s*\\(`, "g");
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
// src/service/recursionValidator.ts
|
|
6487
|
+
var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "trackDependency"];
|
|
6488
|
+
var UNSUPPORTED_API_SITE = {
|
|
6489
|
+
$resolve: "resolve",
|
|
6490
|
+
$postUpdate: "postUpdate",
|
|
6491
|
+
$trackDependency: "trackDependency"
|
|
6492
|
+
};
|
|
6493
|
+
var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
6494
|
+
var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
6495
|
+
function validateRecursion(html, stateTagName = "wcs-state", locale3) {
|
|
6496
|
+
const msgs = getMessages(locale3);
|
|
6497
|
+
const out = [];
|
|
6498
|
+
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
6499
|
+
const mounted = element.bindComponent;
|
|
6500
|
+
for (const block of element.scriptBlocks) {
|
|
6501
|
+
if (!hasRecursionWildcard(block.content) && block.content.indexOf("$recursion") === -1) continue;
|
|
6502
|
+
const declaration = analyzeRecursionDeclaration(block.content);
|
|
6503
|
+
let spec = null;
|
|
6504
|
+
let undeclared = false;
|
|
6505
|
+
let getterSuffixes = [];
|
|
6506
|
+
if (block.mountPath !== null) {
|
|
6507
|
+
validateVolumeBlock(block.content, block.contentStart, block.mountPath, declaration, msgs, out);
|
|
6508
|
+
} else if (mounted) {
|
|
6509
|
+
validateMountedComponentBlock(block.content, block.contentStart, declaration, msgs, out);
|
|
6510
|
+
} else {
|
|
6511
|
+
spec = validateDeclaration(declaration, block.contentStart, msgs, out);
|
|
6512
|
+
undeclared = declaration === null && hasDefaultExportObject(block.content) && !hasTopLevelSpread(block.content);
|
|
6513
|
+
getterSuffixes = validateRecursiveGetters(block.content, block.contentStart, spec, undeclared, msgs, out);
|
|
6514
|
+
}
|
|
6515
|
+
validateListKeys(block.content, block.contentStart, msgs, out);
|
|
6516
|
+
validateApiCalls(block.content, block.contentStart, spec, getterSuffixes, undeclared, msgs, out);
|
|
6517
|
+
validateAssignments(block.content, block.contentStart, spec, getterSuffixes, msgs, out);
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
return out;
|
|
6521
|
+
}
|
|
6522
|
+
function push(out, code, start, end, message, severity = "error") {
|
|
6523
|
+
out.push({ code, start, end, message, severity });
|
|
6524
|
+
}
|
|
6525
|
+
function validateVolumeBlock(script, offset2, mountPath, declaration, msgs, out) {
|
|
6526
|
+
if (declaration !== null) {
|
|
6527
|
+
push(
|
|
6528
|
+
out,
|
|
6529
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6530
|
+
offset2 + declaration.start,
|
|
6531
|
+
offset2 + declaration.end,
|
|
6532
|
+
msgs.recursionInVolume("$recursion", mountPath)
|
|
6533
|
+
);
|
|
6534
|
+
}
|
|
6535
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6536
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6537
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
6538
|
+
seen.add(span.name);
|
|
6539
|
+
push(
|
|
6540
|
+
out,
|
|
6541
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6542
|
+
offset2 + span.start,
|
|
6543
|
+
offset2 + span.end,
|
|
6544
|
+
msgs.recursionInVolume(`"${span.name}"`, mountPath)
|
|
6545
|
+
);
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
function validateDeclaration(declaration, offset2, msgs, out) {
|
|
6549
|
+
if (declaration === null) return null;
|
|
6550
|
+
const code = WcsDiagnosticCode.RecursionDeclarationInvalid;
|
|
6551
|
+
if (declaration.notObject) {
|
|
6552
|
+
push(out, code, offset2 + declaration.start, offset2 + declaration.end, msgs.recursionNotObject());
|
|
6553
|
+
return null;
|
|
6554
|
+
}
|
|
6555
|
+
if (declaration.entries.length !== 1) {
|
|
6556
|
+
if (!declaration.objectLiteral) return null;
|
|
6557
|
+
push(
|
|
6558
|
+
out,
|
|
6559
|
+
code,
|
|
6560
|
+
offset2 + declaration.start,
|
|
6561
|
+
offset2 + declaration.end,
|
|
6562
|
+
msgs.recursionAnchorCount(declaration.entries.length)
|
|
6563
|
+
);
|
|
6564
|
+
return null;
|
|
6565
|
+
}
|
|
6566
|
+
const entry = declaration.entries[0];
|
|
6567
|
+
const anchorProblem = checkNodePath(entry.anchor);
|
|
6568
|
+
if (anchorProblem !== null) {
|
|
6569
|
+
push(
|
|
6570
|
+
out,
|
|
6571
|
+
code,
|
|
6572
|
+
offset2 + entry.start,
|
|
6573
|
+
offset2 + entry.end,
|
|
6574
|
+
msgs.recursionNodePathInvalid("anchor", entry.anchor, anchorProblem)
|
|
6575
|
+
);
|
|
6576
|
+
return null;
|
|
6577
|
+
}
|
|
6578
|
+
if (entry.repeat === null) {
|
|
6579
|
+
if (entry.repeatDefinitelyNotString) {
|
|
6580
|
+
push(
|
|
6581
|
+
out,
|
|
6582
|
+
code,
|
|
6583
|
+
offset2 + entry.valueStart,
|
|
6584
|
+
offset2 + entry.valueEnd,
|
|
6585
|
+
msgs.recursionRepeatNotString(entry.anchor)
|
|
6586
|
+
);
|
|
6587
|
+
}
|
|
6588
|
+
return null;
|
|
6589
|
+
}
|
|
6590
|
+
const repeatProblem = checkNodePath(entry.repeat);
|
|
6591
|
+
if (repeatProblem !== null) {
|
|
6592
|
+
push(
|
|
6593
|
+
out,
|
|
6594
|
+
code,
|
|
6595
|
+
offset2 + entry.valueStart,
|
|
6596
|
+
offset2 + entry.valueEnd,
|
|
6597
|
+
msgs.recursionNodePathInvalid("repeat", entry.repeat, repeatProblem)
|
|
6598
|
+
);
|
|
6599
|
+
return null;
|
|
6600
|
+
}
|
|
6601
|
+
return makeRecursionSpec(entry.anchor, entry.repeat);
|
|
6602
|
+
}
|
|
6603
|
+
function validateRecursiveGetters(script, offset2, spec, undeclared, msgs, out) {
|
|
6604
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6605
|
+
const spans = analyzeDeclarationSpans(script).filter((s) => hasRecursionWildcard(s.name)).filter((s) => seen.has(s.name) ? false : (seen.add(s.name), true));
|
|
6606
|
+
if (spans.length === 0) return [];
|
|
6607
|
+
const setterNames = new Set(
|
|
6608
|
+
analyzeCallableBodies(script).filter((c) => c.accessor === "set").map((c) => c.name)
|
|
6609
|
+
);
|
|
6610
|
+
const suffixes = [];
|
|
6611
|
+
const accepted = [];
|
|
6612
|
+
for (const span of spans) {
|
|
6613
|
+
const start = offset2 + span.start;
|
|
6614
|
+
const end = offset2 + span.end;
|
|
6615
|
+
if (spec === null) {
|
|
6616
|
+
if (undeclared) {
|
|
6617
|
+
push(
|
|
6618
|
+
out,
|
|
6619
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6620
|
+
start,
|
|
6621
|
+
end,
|
|
6622
|
+
msgs.recursionUnsupported(span.name, "undeclared"),
|
|
6623
|
+
"warning"
|
|
6624
|
+
);
|
|
6625
|
+
}
|
|
6626
|
+
continue;
|
|
6627
|
+
}
|
|
6628
|
+
if (span.kind !== "getter") {
|
|
6629
|
+
push(
|
|
6630
|
+
out,
|
|
6631
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6632
|
+
start,
|
|
6633
|
+
end,
|
|
6634
|
+
msgs.recursionGetterInvalid(span.name, "notGetter", spec.recursiveAnchor)
|
|
6635
|
+
);
|
|
6636
|
+
continue;
|
|
6637
|
+
}
|
|
6638
|
+
if (setterNames.has(span.name)) {
|
|
6639
|
+
push(
|
|
6640
|
+
out,
|
|
6641
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6642
|
+
start,
|
|
6643
|
+
end,
|
|
6644
|
+
msgs.recursionGetterInvalid(span.name, "setter", spec.recursiveAnchor)
|
|
6645
|
+
);
|
|
6646
|
+
continue;
|
|
6647
|
+
}
|
|
6648
|
+
const suffix = splitRecursivePath(spec, span.name);
|
|
6649
|
+
if (suffix === null) {
|
|
6650
|
+
push(
|
|
6651
|
+
out,
|
|
6652
|
+
WcsDiagnosticCode.RecursionAnchor,
|
|
6653
|
+
start,
|
|
6654
|
+
end,
|
|
6655
|
+
msgs.recursionAnchorMismatch(span.name, spec.recursiveAnchor)
|
|
6656
|
+
);
|
|
6657
|
+
continue;
|
|
6658
|
+
}
|
|
6659
|
+
if (suffix.length === 0) {
|
|
6660
|
+
push(
|
|
6661
|
+
out,
|
|
6662
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6663
|
+
start,
|
|
6664
|
+
end,
|
|
6665
|
+
msgs.recursionGetterInvalid(span.name, "nodeItself", spec.recursiveAnchor)
|
|
6666
|
+
);
|
|
6667
|
+
continue;
|
|
6668
|
+
}
|
|
6669
|
+
if (structuralWriteTarget(spec, foldSuffixIndexes(suffix)) !== null) {
|
|
6670
|
+
push(
|
|
6671
|
+
out,
|
|
6672
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6673
|
+
start,
|
|
6674
|
+
end,
|
|
6675
|
+
msgs.recursionGetterInvalid(span.name, "structural", spec.recursiveAnchor)
|
|
6676
|
+
);
|
|
6677
|
+
continue;
|
|
6678
|
+
}
|
|
6679
|
+
const collision = accepted.find((other) => sameFamily(spec, other.suffix, suffix));
|
|
6680
|
+
if (collision !== void 0) {
|
|
6681
|
+
push(
|
|
6682
|
+
out,
|
|
6683
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6684
|
+
start,
|
|
6685
|
+
end,
|
|
6686
|
+
msgs.recursionGetterCollision(collision.name, span.name, spec.repeat)
|
|
6687
|
+
);
|
|
6688
|
+
continue;
|
|
5271
6689
|
}
|
|
6690
|
+
accepted.push({ name: span.name, suffix, start, end });
|
|
6691
|
+
suffixes.push(suffix);
|
|
5272
6692
|
}
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
6693
|
+
if (spec !== null && suffixes.length > 0) {
|
|
6694
|
+
const reported = /* @__PURE__ */ new Set();
|
|
6695
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6696
|
+
if (hasRecursionWildcard(span.name) || reported.has(span.name)) continue;
|
|
6697
|
+
const suffix = concreteExpansionSuffix(spec, suffixes, span.name);
|
|
6698
|
+
if (suffix === null) continue;
|
|
6699
|
+
reported.add(span.name);
|
|
6700
|
+
push(
|
|
6701
|
+
out,
|
|
6702
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6703
|
+
offset2 + span.start,
|
|
6704
|
+
offset2 + span.end,
|
|
6705
|
+
msgs.recursionConcreteCollision(span.name, spec.recursiveAnchor + suffix)
|
|
6706
|
+
);
|
|
6707
|
+
}
|
|
6708
|
+
}
|
|
6709
|
+
return suffixes;
|
|
5286
6710
|
}
|
|
5287
|
-
function
|
|
5288
|
-
|
|
6711
|
+
function validateMountedComponentBlock(script, offset2, declaration, msgs, out) {
|
|
6712
|
+
if (declaration !== null) {
|
|
6713
|
+
push(
|
|
6714
|
+
out,
|
|
6715
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6716
|
+
offset2 + declaration.start,
|
|
6717
|
+
offset2 + declaration.end,
|
|
6718
|
+
msgs.recursionInMountedComponent("$recursion"),
|
|
6719
|
+
"warning"
|
|
6720
|
+
);
|
|
6721
|
+
}
|
|
6722
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6723
|
+
for (const span of analyzeDeclarationSpans(script)) {
|
|
6724
|
+
if (!hasRecursionWildcard(span.name) || seen.has(span.name)) continue;
|
|
6725
|
+
seen.add(span.name);
|
|
6726
|
+
push(
|
|
6727
|
+
out,
|
|
6728
|
+
WcsDiagnosticCode.RecursionDeclarationInvalid,
|
|
6729
|
+
offset2 + span.start,
|
|
6730
|
+
offset2 + span.end,
|
|
6731
|
+
msgs.recursionInMountedComponent(`"${span.name}"`),
|
|
6732
|
+
"warning"
|
|
6733
|
+
);
|
|
6734
|
+
}
|
|
5289
6735
|
}
|
|
5290
|
-
function
|
|
5291
|
-
|
|
6736
|
+
function validateListKeys(script, offset2, msgs, out) {
|
|
6737
|
+
for (const entry of analyzeListKeyEntries(script)) {
|
|
6738
|
+
if (!hasRecursionWildcard(entry.key)) continue;
|
|
6739
|
+
push(
|
|
6740
|
+
out,
|
|
6741
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6742
|
+
offset2 + entry.start,
|
|
6743
|
+
offset2 + entry.end,
|
|
6744
|
+
msgs.recursionUnsupported(entry.key, "listKeys")
|
|
6745
|
+
);
|
|
6746
|
+
}
|
|
5292
6747
|
}
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
6748
|
+
function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msgs, out) {
|
|
6749
|
+
const scan = blankComments(script);
|
|
6750
|
+
const regex = createApiCallRegex(RECURSION_APIS);
|
|
6751
|
+
let match;
|
|
6752
|
+
while ((match = regex.exec(scan)) !== null) {
|
|
6753
|
+
const api = `$${match[1]}`;
|
|
6754
|
+
const parsed = splitCallArgs(scan, match.index + match[0].length);
|
|
6755
|
+
if (parsed === null) continue;
|
|
6756
|
+
regex.lastIndex = parsed.end;
|
|
6757
|
+
if (parsed.args.length === 0) continue;
|
|
6758
|
+
const pathArg = parsed.args[0];
|
|
6759
|
+
const path = literalString(pathArg);
|
|
6760
|
+
if (path === null) continue;
|
|
6761
|
+
const leading = pathArg.length - pathArg.trimStart().length;
|
|
6762
|
+
const start = offset2 + parsed.starts[0] + leading;
|
|
6763
|
+
const end = offset2 + parsed.starts[0] + pathArg.trimEnd().length;
|
|
6764
|
+
if (!hasRecursionWildcard(path)) {
|
|
6765
|
+
const writes = api === "$setAll" || api === "$resolve" && parsed.args.length >= 3;
|
|
6766
|
+
if (writes && spec !== null) {
|
|
6767
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6768
|
+
if (owning !== null) {
|
|
6769
|
+
push(
|
|
6770
|
+
out,
|
|
6771
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6772
|
+
start,
|
|
6773
|
+
end,
|
|
6774
|
+
msgs.recursionReadonly(`${api}("${path}")`, spec.recursiveAnchor + owning)
|
|
6775
|
+
);
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
continue;
|
|
5309
6779
|
}
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
}
|
|
6780
|
+
if (api in UNSUPPORTED_API_SITE) {
|
|
6781
|
+
push(
|
|
6782
|
+
out,
|
|
6783
|
+
WcsDiagnosticCode.RecursionUnsupported,
|
|
6784
|
+
start,
|
|
6785
|
+
end,
|
|
6786
|
+
msgs.recursionUnsupported(path, UNSUPPORTED_API_SITE[api])
|
|
6787
|
+
);
|
|
6788
|
+
continue;
|
|
6789
|
+
}
|
|
6790
|
+
if (spec === null) {
|
|
6791
|
+
if (undeclared) {
|
|
6792
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "undeclared"));
|
|
6793
|
+
}
|
|
6794
|
+
continue;
|
|
6795
|
+
}
|
|
6796
|
+
const suffix = splitRecursivePath(spec, path);
|
|
6797
|
+
if (suffix === null) {
|
|
6798
|
+
push(out, WcsDiagnosticCode.RecursionAnchor, start, end, msgs.recursionAnchorMismatch(path, spec.recursiveAnchor));
|
|
6799
|
+
continue;
|
|
6800
|
+
}
|
|
6801
|
+
if (api === "$getAll") {
|
|
6802
|
+
if (parsed.args.length > 1) {
|
|
6803
|
+
const indexesArg = parsed.args[1];
|
|
6804
|
+
const indexes = literalArrayLength(indexesArg);
|
|
6805
|
+
if (indexes !== null && indexes > 0) {
|
|
6806
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "prefix"));
|
|
6807
|
+
} else if (indexes === null && isDefiniteNonArrayLiteral2(indexesArg)) {
|
|
6808
|
+
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "notArray"));
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
continue;
|
|
5324
6812
|
}
|
|
6813
|
+
validateSetAllForm(path, suffix, parsed.args, spec, getterSuffixes, start, end, msgs, out);
|
|
5325
6814
|
}
|
|
5326
|
-
return out;
|
|
5327
6815
|
}
|
|
5328
|
-
function
|
|
5329
|
-
const
|
|
5330
|
-
const
|
|
5331
|
-
if (
|
|
5332
|
-
|
|
6816
|
+
function validateSetAllForm(path, suffix, args, spec, getterSuffixes, start, end, msgs, out) {
|
|
6817
|
+
const formCode = WcsDiagnosticCode.RecursionSetAllForm;
|
|
6818
|
+
const indexesArg = args.length > 1 ? args[1].trim() : "";
|
|
6819
|
+
if (args.length < 2 || indexesArg === "undefined" || indexesArg === "null") {
|
|
6820
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "noIndexes"));
|
|
6821
|
+
return;
|
|
5333
6822
|
}
|
|
5334
|
-
|
|
5335
|
-
|
|
6823
|
+
const indexes = literalArrayLength(args[1]);
|
|
6824
|
+
if (indexes !== null && indexes > 0) {
|
|
6825
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "prefix"));
|
|
6826
|
+
return;
|
|
5336
6827
|
}
|
|
5337
|
-
if (
|
|
5338
|
-
|
|
6828
|
+
if (args.length > 2 && isFunctionLiteral(args[2])) {
|
|
6829
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "mapper"));
|
|
6830
|
+
return;
|
|
5339
6831
|
}
|
|
5340
|
-
if (
|
|
5341
|
-
|
|
6832
|
+
if (args.length > 3 && /\bspread\s*:\s*true\b/.test(args[3])) {
|
|
6833
|
+
push(out, formCode, start, end, msgs.recursionSetAllForm(path, "spread"));
|
|
6834
|
+
return;
|
|
5342
6835
|
}
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
6836
|
+
const checkedSuffix = foldSuffixIndexes(suffix);
|
|
6837
|
+
const structural = structuralWriteTarget(spec, checkedSuffix);
|
|
6838
|
+
if (structural !== null) {
|
|
6839
|
+
push(
|
|
6840
|
+
out,
|
|
6841
|
+
WcsDiagnosticCode.RecursionStructuralWrite,
|
|
6842
|
+
start,
|
|
6843
|
+
end,
|
|
6844
|
+
msgs.recursionStructuralWrite(path, structural, spec.repeatList)
|
|
6845
|
+
);
|
|
6846
|
+
return;
|
|
6847
|
+
}
|
|
6848
|
+
const conflicting = conflictingGetterSuffix(spec, getterSuffixes, checkedSuffix);
|
|
6849
|
+
if (conflicting !== null) {
|
|
6850
|
+
push(
|
|
6851
|
+
out,
|
|
6852
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6853
|
+
start,
|
|
6854
|
+
end,
|
|
6855
|
+
msgs.recursionReadonly(`$setAll("${path}")`, spec.recursiveAnchor + conflicting)
|
|
6856
|
+
);
|
|
6857
|
+
}
|
|
6858
|
+
}
|
|
6859
|
+
function validateAssignments(script, offset2, spec, getterSuffixes, msgs, out) {
|
|
6860
|
+
const masked = maskCommentsAndStrings(script);
|
|
6861
|
+
const found = [];
|
|
6862
|
+
for (const source of [BRACKET_ASSIGNMENT, PRE_BRACKET_INCDEC]) {
|
|
6863
|
+
const regex = new RegExp(source.source, "g");
|
|
6864
|
+
let match;
|
|
6865
|
+
while ((match = regex.exec(masked)) !== null) {
|
|
6866
|
+
const pathStart = match.index + match[0].search(/["']/) + 1;
|
|
6867
|
+
found.push({ pathStart, length: match[1].length });
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
found.sort((a, b) => a.pathStart - b.pathStart);
|
|
6871
|
+
let last = -1;
|
|
6872
|
+
for (const { pathStart, length } of found) {
|
|
6873
|
+
if (pathStart === last) continue;
|
|
6874
|
+
last = pathStart;
|
|
6875
|
+
const path = script.slice(pathStart, pathStart + length);
|
|
6876
|
+
const start = offset2 + pathStart;
|
|
6877
|
+
const end = start + path.length;
|
|
6878
|
+
if (hasRecursionWildcard(path)) {
|
|
6879
|
+
push(out, WcsDiagnosticCode.RecursionUnsupported, start, end, msgs.recursionUnsupported(path, "assignment"));
|
|
6880
|
+
continue;
|
|
6881
|
+
}
|
|
6882
|
+
if (spec === null) continue;
|
|
6883
|
+
const owning = owningGetterSuffix(spec, getterSuffixes, path);
|
|
6884
|
+
if (owning !== null) {
|
|
6885
|
+
push(
|
|
6886
|
+
out,
|
|
6887
|
+
WcsDiagnosticCode.RecursionReadonly,
|
|
6888
|
+
start,
|
|
6889
|
+
end,
|
|
6890
|
+
msgs.recursionReadonly(`this["${path}"] = \u2026`, spec.recursiveAnchor + owning)
|
|
6891
|
+
);
|
|
6892
|
+
}
|
|
5349
6893
|
}
|
|
5350
|
-
|
|
6894
|
+
}
|
|
6895
|
+
function isDefiniteNonArrayLiteral2(arg) {
|
|
6896
|
+
const trimmed = arg.trim();
|
|
6897
|
+
return trimmed === "null" || /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false)$/.test(trimmed) || trimmed.startsWith("{");
|
|
6898
|
+
}
|
|
6899
|
+
function isFunctionLiteral(arg) {
|
|
6900
|
+
const trimmed = arg.trim();
|
|
6901
|
+
if (trimmed.length === 0) return false;
|
|
6902
|
+
return /^(?:async\s+)?function\b/.test(trimmed) || /^(?:async\s+)?\([^()]*\)\s*=>/.test(trimmed) || /^(?:async\s+)?[$\w]+\s*=>/.test(trimmed);
|
|
5351
6903
|
}
|
|
5352
6904
|
|
|
5353
6905
|
// src/service/namedStateValidator.ts
|
|
@@ -11274,7 +12826,7 @@ function enterFunction(fn, outer, thisIsState) {
|
|
|
11274
12826
|
function isThisRoot(node, scope) {
|
|
11275
12827
|
return node.type === "ThisExpression" && scope.thisIsState || node.type === "Identifier" && scope.aliases.has(node.name);
|
|
11276
12828
|
}
|
|
11277
|
-
function
|
|
12829
|
+
function literalString2(node) {
|
|
11278
12830
|
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
11279
12831
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
11280
12832
|
return node.quasis[0].value.cooked ?? null;
|
|
@@ -11289,7 +12841,7 @@ function segmentOf(member) {
|
|
|
11289
12841
|
if (property.type === "Literal" && typeof property.value === "number") {
|
|
11290
12842
|
return { text: String(property.value), dynamic: null };
|
|
11291
12843
|
}
|
|
11292
|
-
const text =
|
|
12844
|
+
const text = literalString2(property);
|
|
11293
12845
|
if (text !== null) return { text, dynamic: null };
|
|
11294
12846
|
return property.type === "PrivateIdentifier" ? { text: null, dynamic: null } : { text: null, dynamic: property };
|
|
11295
12847
|
}
|
|
@@ -11385,7 +12937,7 @@ function visitCall(node, scope, out) {
|
|
|
11385
12937
|
if (api === UNTRACK_API) return;
|
|
11386
12938
|
if (PATH_ARG_APIS.has(api)) {
|
|
11387
12939
|
const first = node.arguments[0];
|
|
11388
|
-
const path = first !== void 0 && first.type !== "SpreadElement" ?
|
|
12940
|
+
const path = first !== void 0 && first.type !== "SpreadElement" ? literalString2(first) : null;
|
|
11389
12941
|
if (path !== null && path.length > 0 && !path.startsWith("$")) {
|
|
11390
12942
|
out.push({
|
|
11391
12943
|
path,
|
|
@@ -11451,7 +13003,7 @@ function visitDestructure(pattern, prefix, scope, out) {
|
|
|
11451
13003
|
if (property.type === "RestElement") continue;
|
|
11452
13004
|
let key = null;
|
|
11453
13005
|
if (!property.computed && property.key.type === "Identifier") key = property.key.name;
|
|
11454
|
-
else key =
|
|
13006
|
+
else key = literalString2(property.key);
|
|
11455
13007
|
let value = property.value;
|
|
11456
13008
|
if (value.type === "AssignmentPattern") {
|
|
11457
13009
|
visit(value.right, scope, out);
|
|
@@ -11484,6 +13036,10 @@ for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
|
|
|
11484
13036
|
tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
|
|
11485
13037
|
}
|
|
11486
13038
|
Object.freeze(tmpIndexByIndexName2);
|
|
13039
|
+
var RECURSION_WILDCARD2 = "**";
|
|
13040
|
+
function raiseError2(message) {
|
|
13041
|
+
throw new Error(`[@wcstack/state] ${message}`);
|
|
13042
|
+
}
|
|
11487
13043
|
var _cache = /* @__PURE__ */ new Map();
|
|
11488
13044
|
function clearPathInfoCacheForTooling() {
|
|
11489
13045
|
_cache.clear();
|
|
@@ -11494,6 +13050,9 @@ function getPathInfo(path) {
|
|
|
11494
13050
|
if (typeof pathInfo !== "undefined") {
|
|
11495
13051
|
return pathInfo;
|
|
11496
13052
|
}
|
|
13053
|
+
if (path.indexOf(RECURSION_WILDCARD2) !== -1) {
|
|
13054
|
+
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.`);
|
|
13055
|
+
}
|
|
11497
13056
|
pathInfo = Object.freeze(new PathInfo(path));
|
|
11498
13057
|
_cache.set(path, pathInfo);
|
|
11499
13058
|
return pathInfo;
|
|
@@ -11617,9 +13176,6 @@ function didYouMean(input, candidates) {
|
|
|
11617
13176
|
return best !== null ? ` Did you mean "${best}"?` : "";
|
|
11618
13177
|
}
|
|
11619
13178
|
var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
11620
|
-
function raiseError2(message) {
|
|
11621
|
-
throw new Error(`[@wcstack/state] ${message}`);
|
|
11622
|
-
}
|
|
11623
13179
|
var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
|
|
11624
13180
|
"if",
|
|
11625
13181
|
"elseif",
|
|
@@ -12623,71 +14179,6 @@ function buildReferenceIndex(html, options = {}) {
|
|
|
12623
14179
|
// src/service/semanticValidator.ts
|
|
12624
14180
|
var STATE_UPDATED_CALLBACK = "$updatedCallback";
|
|
12625
14181
|
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
14182
|
function validateIndexArity(script, scriptStart, locale3) {
|
|
12692
14183
|
const msgs = getMessages(locale3);
|
|
12693
14184
|
const out = [];
|
|
@@ -12699,8 +14190,9 @@ function validateIndexArity(script, scriptStart, locale3) {
|
|
|
12699
14190
|
if (parsed === null) continue;
|
|
12700
14191
|
API_CALL.lastIndex = parsed.end;
|
|
12701
14192
|
if (parsed.args.length < 2) continue;
|
|
12702
|
-
const path =
|
|
14193
|
+
const path = literalString(parsed.args[0]);
|
|
12703
14194
|
if (path === null) continue;
|
|
14195
|
+
if (hasRecursionWildcard(path)) continue;
|
|
12704
14196
|
const actual = literalArrayLength(parsed.args[1]);
|
|
12705
14197
|
if (actual === null) continue;
|
|
12706
14198
|
const wildcardCount = countWildcardSegments(path);
|
|
@@ -12807,7 +14299,7 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
|
|
|
12807
14299
|
}
|
|
12808
14300
|
var TWO_WAY_PROPS = /* @__PURE__ */ new Set(["value", "checked"]);
|
|
12809
14301
|
var BRACKET_WRITE = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
12810
|
-
var
|
|
14302
|
+
var PRE_BRACKET_INCDEC2 = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
12811
14303
|
function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
12812
14304
|
const roots = /* @__PURE__ */ new Set();
|
|
12813
14305
|
const addPrefixes = (path, inclusive) => {
|
|
@@ -12843,7 +14335,7 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12843
14335
|
for (const block of blocks) {
|
|
12844
14336
|
if (block.mountPath !== null) addPrefixes(block.mountPath, true);
|
|
12845
14337
|
const scan = blankComments(block.content);
|
|
12846
|
-
for (const regex of [BRACKET_WRITE,
|
|
14338
|
+
for (const regex of [BRACKET_WRITE, PRE_BRACKET_INCDEC2]) {
|
|
12847
14339
|
regex.lastIndex = 0;
|
|
12848
14340
|
let match;
|
|
12849
14341
|
while ((match = regex.exec(scan)) !== null) addPrefixes(match[1], false);
|
|
@@ -12856,57 +14348,13 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
12856
14348
|
const parsed = splitCallArgs(scan, call.index + call[0].length);
|
|
12857
14349
|
if (parsed === null) continue;
|
|
12858
14350
|
API_CALL.lastIndex = parsed.end;
|
|
12859
|
-
const path = parsed.args.length > 0 ?
|
|
14351
|
+
const path = parsed.args.length > 0 ? literalString(parsed.args[0]) : null;
|
|
12860
14352
|
if (path === null) continue;
|
|
12861
14353
|
if (api === "setAll" || parsed.args.length >= 3) addPrefixes(path, false);
|
|
12862
14354
|
}
|
|
12863
14355
|
}
|
|
12864
14356
|
return roots;
|
|
12865
14357
|
}
|
|
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
14358
|
var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((?:\\.|(?!\1)[^\\])*)\1/g;
|
|
12911
14359
|
function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale3) {
|
|
12912
14360
|
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
@@ -13011,6 +14459,8 @@ function validateDocument(text, options = {}) {
|
|
|
13011
14459
|
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
13012
14460
|
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
13013
14461
|
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
14462
|
+
out.push(...validateScanDeclarations(text, stateTagName, locale3));
|
|
14463
|
+
out.push(...validateRecursion(text, stateTagName, locale3));
|
|
13014
14464
|
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
13015
14465
|
out.push(...validateMountAttributes(text, stateTagName, locale3));
|
|
13016
14466
|
for (const d of validateStateTypes(text, stateTagName, locale3)) {
|