@wcstack/typescript 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +1 -1
- package/dist/schema-core.cjs +521 -19
- package/dist/tsc-core.cjs +20 -2
- package/dist/wcs-schema.mjs +1 -1
- package/dist/wcs-tsc.mjs +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
package/dist/schema-core.cjs
CHANGED
|
@@ -93,6 +93,14 @@ 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",
|
|
96
104
|
// --- <wcs-state> script: $recursion declaration / `**` paths ---
|
|
97
105
|
// ランタイムと同じ code 語彙(@wcstack/state src/recursion/ が正本。
|
|
98
106
|
// docs/state-recursive-path-impl-plan.md §7)。静的に出すのは**パス文字列と宣言だけで
|
|
@@ -1300,6 +1308,7 @@ var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
|
1300
1308
|
var STATE_ON_NAME = "$on";
|
|
1301
1309
|
var STATE_STREAMS_NAME = "$streams";
|
|
1302
1310
|
var STATE_WATCH_NAME = "$watch";
|
|
1311
|
+
var STATE_SCAN_NAME = "$scan";
|
|
1303
1312
|
var STATE_RECURSION_NAME = "$recursion";
|
|
1304
1313
|
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
1305
1314
|
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
@@ -1362,6 +1371,7 @@ function getWcsManifest() {
|
|
|
1362
1371
|
STATE_ON_NAME,
|
|
1363
1372
|
STATE_STREAMS_NAME,
|
|
1364
1373
|
STATE_WATCH_NAME,
|
|
1374
|
+
STATE_SCAN_NAME,
|
|
1365
1375
|
STATE_LIST_KEYS_NAME,
|
|
1366
1376
|
STATE_RECURSION_NAME,
|
|
1367
1377
|
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
@@ -1551,6 +1561,7 @@ var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
|
1551
1561
|
var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
|
|
1552
1562
|
var RESERVED_LIST_KEYS_KEY = "$listKeys";
|
|
1553
1563
|
var RESERVED_WATCH_KEY = "$watch";
|
|
1564
|
+
var RESERVED_SCAN_KEY = "$scan";
|
|
1554
1565
|
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
1555
1566
|
function analyzeStatePaths(scriptContent) {
|
|
1556
1567
|
const objectContent = extractDefaultExportObject(scriptContent);
|
|
@@ -1560,25 +1571,36 @@ function analyzeStatePaths(scriptContent) {
|
|
|
1560
1571
|
const pendingStreamValues = [];
|
|
1561
1572
|
const pendingListKeys = [];
|
|
1562
1573
|
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
1574
|
+
const effectiveDescriptors = effectiveTopLevelDescriptors(topLevelProps);
|
|
1563
1575
|
for (const prop of topLevelProps) {
|
|
1564
1576
|
if (prop.name.startsWith("$")) {
|
|
1565
1577
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
1566
1578
|
continue;
|
|
1567
1579
|
}
|
|
1580
|
+
const effective = effectiveDescriptors.get(prop.name);
|
|
1581
|
+
if (prop.kind === "getter" !== (effective !== "data")) {
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1568
1584
|
if (prop.kind === "method") {
|
|
1569
1585
|
paths.push({ path: prop.name, kind: "method" });
|
|
1570
1586
|
continue;
|
|
1571
1587
|
}
|
|
1572
1588
|
if (prop.kind === "getter") {
|
|
1573
|
-
|
|
1574
|
-
|
|
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;
|
|
1575
1597
|
}
|
|
1576
1598
|
continue;
|
|
1577
1599
|
}
|
|
1578
1600
|
pushDataPropertyPaths(prop, paths);
|
|
1579
1601
|
}
|
|
1580
1602
|
for (const streamValue of pendingStreamValues) {
|
|
1581
|
-
if (paths.some((p) => p.path === streamValue.name)) continue;
|
|
1603
|
+
if (paths.some((p) => p.path === streamValue.name && p.kind !== "eventToken")) continue;
|
|
1582
1604
|
pushDataPropertyPaths(streamValue, paths);
|
|
1583
1605
|
}
|
|
1584
1606
|
for (const listKeyEntry of pendingListKeys) {
|
|
@@ -1657,6 +1679,146 @@ function analyzeWatchEntries(scriptContent) {
|
|
|
1657
1679
|
function analyzeListKeyEntries(scriptContent) {
|
|
1658
1680
|
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1659
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
|
+
}
|
|
1660
1822
|
function hasDefaultExportObject(scriptContent) {
|
|
1661
1823
|
return locateDefaultExportObject(scriptContent) !== null;
|
|
1662
1824
|
}
|
|
@@ -1673,7 +1835,7 @@ function hasTopLevelSpread(scriptContent) {
|
|
|
1673
1835
|
}
|
|
1674
1836
|
return false;
|
|
1675
1837
|
}
|
|
1676
|
-
function analyzeObjectEntries(scriptContent, key) {
|
|
1838
|
+
function analyzeObjectEntries(scriptContent, key, allowEmptyKeys = false) {
|
|
1677
1839
|
const root = locateDefaultExportObject(scriptContent);
|
|
1678
1840
|
if (!root) return [];
|
|
1679
1841
|
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
@@ -1683,14 +1845,15 @@ function analyzeObjectEntries(scriptContent, key) {
|
|
|
1683
1845
|
const leading = prop.value.length - prop.value.trimStart().length;
|
|
1684
1846
|
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
1685
1847
|
const entries = [];
|
|
1686
|
-
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
|
|
1848
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value), allowEmptyKeys)) {
|
|
1687
1849
|
if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
|
|
1688
1850
|
entries.push({
|
|
1689
1851
|
key: entry.name,
|
|
1690
1852
|
start: innerStart + entry.nameStart,
|
|
1691
1853
|
end: innerStart + entry.nameEnd,
|
|
1692
1854
|
kind: entry.kind,
|
|
1693
|
-
value: entry.value
|
|
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)
|
|
1694
1857
|
});
|
|
1695
1858
|
}
|
|
1696
1859
|
return entries;
|
|
@@ -1738,20 +1901,27 @@ function isNonFunctionLiteral(value) {
|
|
|
1738
1901
|
return /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false|null|undefined)\b/.test(trimmed) || trimmed.startsWith("[") || trimmed.startsWith("{");
|
|
1739
1902
|
}
|
|
1740
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) {
|
|
1741
1910
|
const root = locateDefaultExportObject(scriptContent);
|
|
1742
1911
|
if (!root) return null;
|
|
1743
|
-
const
|
|
1744
|
-
if (!
|
|
1912
|
+
const declarationProp = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
1913
|
+
if (!declarationProp || declarationProp.nameStart === void 0 || declarationProp.nameEnd === void 0) {
|
|
1745
1914
|
return null;
|
|
1746
1915
|
}
|
|
1747
|
-
const span = { start: root.start +
|
|
1748
|
-
if (
|
|
1916
|
+
const span = { start: root.start + declarationProp.nameStart, end: root.start + declarationProp.nameEnd };
|
|
1917
|
+
if (declarationProp.kind === "method") {
|
|
1749
1918
|
return span;
|
|
1750
1919
|
}
|
|
1751
|
-
if (
|
|
1752
|
-
const trimmed =
|
|
1920
|
+
if (declarationProp.kind !== "data" || !declarationProp.value) return null;
|
|
1921
|
+
const trimmed = declarationProp.value.trim();
|
|
1753
1922
|
if (trimmed.startsWith("{")) return null;
|
|
1754
1923
|
const scan = maskCommentsAndStrings(trimmed).trim();
|
|
1924
|
+
if (rejectArray && scan.startsWith("[") && isWholeBracketLiteral(scan)) return span;
|
|
1755
1925
|
const isArrowFunction = /^(?:async\s+)?\([^()]*\)\s*=>/.test(scan) || /^(?:async\s+)?[$\w]+\s*=>/.test(scan);
|
|
1756
1926
|
const isWholeLiteral = /^(["'`])[^"'`]*\1$/.test(scan) || /^-?\d[\w.]*$/.test(scan) || /^(?:true|false|null)$/.test(scan) || /^(?:async\s+)?function\b[\s\S]*\}$/.test(scan);
|
|
1757
1927
|
if (!isArrowFunction && !isWholeLiteral) return null;
|
|
@@ -1774,6 +1944,19 @@ function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKe
|
|
|
1774
1944
|
}
|
|
1775
1945
|
return;
|
|
1776
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
|
+
}
|
|
1777
1960
|
if (prop.name === RESERVED_COMMAND_TOKENS_KEY && prop.value) {
|
|
1778
1961
|
for (const name of extractStringArrayItems(prop.value)) {
|
|
1779
1962
|
paths.push({ path: `$command.${name}`, kind: "command" });
|
|
@@ -1947,6 +2130,9 @@ function findStreamInitialProperty(entryValue) {
|
|
|
1947
2130
|
const defProps = parseTopLevelProperties(extractObjectContent(entryValue));
|
|
1948
2131
|
return defProps.find((p) => p.kind === "data" && p.name === "initial");
|
|
1949
2132
|
}
|
|
2133
|
+
function isFlatScanOutputName(name) {
|
|
2134
|
+
return name.length > 0 && !name.startsWith("$") && !name.includes(".") && !name.includes("*");
|
|
2135
|
+
}
|
|
1950
2136
|
function extractStringArrayItems(value) {
|
|
1951
2137
|
if (!isArrayLiteral(value)) return [];
|
|
1952
2138
|
const items = [];
|
|
@@ -1958,6 +2144,19 @@ function extractStringArrayItems(value) {
|
|
|
1958
2144
|
return items;
|
|
1959
2145
|
}
|
|
1960
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
|
+
}
|
|
1961
2160
|
function pushDataPropertyPaths(prop, paths) {
|
|
1962
2161
|
pushDataPropertyPathsAt(prop.name, prop, paths, 0);
|
|
1963
2162
|
}
|
|
@@ -2036,10 +2235,10 @@ function locateDefaultExportObject(script) {
|
|
|
2036
2235
|
function extractDefaultExportObject(script) {
|
|
2037
2236
|
return locateDefaultExportObject(script)?.content ?? null;
|
|
2038
2237
|
}
|
|
2039
|
-
function parseTopLevelProperties(objectContent) {
|
|
2238
|
+
function parseTopLevelProperties(objectContent, allowEmptyDataKeys = false) {
|
|
2040
2239
|
const props = [];
|
|
2041
2240
|
const scan = maskCommentsAndStrings(objectContent);
|
|
2042
|
-
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;
|
|
2043
2242
|
let match;
|
|
2044
2243
|
while ((match = regex.exec(scan)) !== null) {
|
|
2045
2244
|
const indices = match.indices;
|
|
@@ -2085,7 +2284,7 @@ function parseTopLevelProperties(objectContent) {
|
|
|
2085
2284
|
continue;
|
|
2086
2285
|
}
|
|
2087
2286
|
const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
|
|
2088
|
-
if (propName) {
|
|
2287
|
+
if (propName !== void 0) {
|
|
2089
2288
|
const valueStartIndex = match.index + match[0].length;
|
|
2090
2289
|
const value = extractFullValue(objectContent, scan, valueStartIndex);
|
|
2091
2290
|
const jsdocType = extractJsDocType(objectContent, match.index);
|
|
@@ -2446,6 +2645,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
2446
2645
|
continue;
|
|
2447
2646
|
}
|
|
2448
2647
|
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
2648
|
+
const bindComponent = parseAttributeNames(wcsMatch.tagContent).has("bind-component");
|
|
2449
2649
|
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
2450
2650
|
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
2451
2651
|
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
@@ -2487,7 +2687,7 @@ function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
|
2487
2687
|
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
2488
2688
|
if (pos === 0) break;
|
|
2489
2689
|
}
|
|
2490
|
-
elements.push({ mountPath, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2690
|
+
elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2491
2691
|
pos = wcsEnd;
|
|
2492
2692
|
if (wcsCloseIdx !== -1) {
|
|
2493
2693
|
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
@@ -2577,6 +2777,15 @@ function findCloseTag(html, startPos, tagName) {
|
|
|
2577
2777
|
}
|
|
2578
2778
|
return -1;
|
|
2579
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
|
+
}
|
|
2580
2789
|
function extractAttribute(tagContent, attrName) {
|
|
2581
2790
|
const regex = new RegExp(
|
|
2582
2791
|
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
@@ -2816,6 +3025,33 @@ var ja = {
|
|
|
2816
3025
|
watchKeyEmptySegment: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`,
|
|
2817
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`,
|
|
2818
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`,
|
|
2819
3055
|
typeAnnotationIncompatible: (vt, rt) => `\u578B "${vt}" \u306F @type {${rt}} \u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093`,
|
|
2820
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`,
|
|
2821
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`,
|
|
@@ -2861,6 +3097,8 @@ var ja = {
|
|
|
2861
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`;
|
|
2862
3098
|
case "listKeys":
|
|
2863
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`;
|
|
2864
3102
|
default:
|
|
2865
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`;
|
|
2866
3104
|
}
|
|
@@ -2975,6 +3213,33 @@ var en = {
|
|
|
2975
3213
|
watchKeyEmptySegment: (k) => `$watch key "${k}" has an empty path segment`,
|
|
2976
3214
|
watchHandlerNotFunction: (k) => `The value of $watch entry "${k}" must be a function`,
|
|
2977
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"})`,
|
|
2978
3243
|
typeAnnotationIncompatible: (vt, rt) => `Type "${vt}" is not compatible with @type {${rt}}`,
|
|
2979
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}).`,
|
|
2980
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.`,
|
|
@@ -3020,6 +3285,8 @@ var en = {
|
|
|
3020
3285
|
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
3021
3286
|
case "listKeys":
|
|
3022
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 "*")`;
|
|
3023
3290
|
default:
|
|
3024
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`;
|
|
3025
3292
|
}
|
|
@@ -5865,6 +6132,240 @@ function validateEntry(entry, pathSet, paths, msgs) {
|
|
|
5865
6132
|
return null;
|
|
5866
6133
|
}
|
|
5867
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
|
+
|
|
5868
6369
|
// src/service/scriptCallArgs.ts
|
|
5869
6370
|
var STRING_LITERAL = /^\s*(["'])((?:\\.|(?!\1)[^\\])*)\1\s*$/;
|
|
5870
6371
|
var TEMPLATE_NO_SUBST = /^\s*`((?:\\.|[^\\`$]|\$(?!\{))*)`\s*$/;
|
|
@@ -5995,7 +6496,7 @@ function validateRecursion(html, stateTagName = "wcs-state", locale3) {
|
|
|
5995
6496
|
const msgs = getMessages(locale3);
|
|
5996
6497
|
const out = [];
|
|
5997
6498
|
for (const element of parseWcsStateElements(html, stateTagName)) {
|
|
5998
|
-
const mounted =
|
|
6499
|
+
const mounted = element.bindComponent;
|
|
5999
6500
|
for (const block of element.scriptBlocks) {
|
|
6000
6501
|
if (!hasRecursionWildcard(block.content) && block.content.indexOf("$recursion") === -1) continue;
|
|
6001
6502
|
const declaration = analyzeRecursionDeclaration(block.content);
|
|
@@ -6303,7 +6804,7 @@ function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msg
|
|
|
6303
6804
|
const indexes = literalArrayLength(indexesArg);
|
|
6304
6805
|
if (indexes !== null && indexes > 0) {
|
|
6305
6806
|
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "prefix"));
|
|
6306
|
-
} else if (indexes === null &&
|
|
6807
|
+
} else if (indexes === null && isDefiniteNonArrayLiteral2(indexesArg)) {
|
|
6307
6808
|
push(out, WcsDiagnosticCode.RecursionGetAllForm, start, end, msgs.recursionGetAllForm(path, "notArray"));
|
|
6308
6809
|
}
|
|
6309
6810
|
}
|
|
@@ -6391,7 +6892,7 @@ function validateAssignments(script, offset2, spec, getterSuffixes, msgs, out) {
|
|
|
6391
6892
|
}
|
|
6392
6893
|
}
|
|
6393
6894
|
}
|
|
6394
|
-
function
|
|
6895
|
+
function isDefiniteNonArrayLiteral2(arg) {
|
|
6395
6896
|
const trimmed = arg.trim();
|
|
6396
6897
|
return trimmed === "null" || /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false)$/.test(trimmed) || trimmed.startsWith("{");
|
|
6397
6898
|
}
|
|
@@ -13958,6 +14459,7 @@ function validateDocument(text, options = {}) {
|
|
|
13958
14459
|
out.push(...validateSemantics(text, stateTagName, locale3, bindAttribute));
|
|
13959
14460
|
out.push(...validateArrayMutations(text, stateTagName, locale3));
|
|
13960
14461
|
out.push(...validateWatchDeclarations(text, stateTagName, locale3));
|
|
14462
|
+
out.push(...validateScanDeclarations(text, stateTagName, locale3));
|
|
13961
14463
|
out.push(...validateRecursion(text, stateTagName, locale3));
|
|
13962
14464
|
out.push(...validateNamedState(text, bindAttribute, stateTagName, locale3));
|
|
13963
14465
|
out.push(...validateMountAttributes(text, stateTagName, locale3));
|
package/dist/tsc-core.cjs
CHANGED
|
@@ -224,7 +224,14 @@ interface WcsStateApi {
|
|
|
224
224
|
// \u672B\u5C3E\u304C \`.**\` \u306E\u30AD\u30FC\u306B\u3082\u540C\u3058\u7D22\u5F15\u3092\u7F6E\u304F\uFF08@wcstack/state \u306E defineState \u3068\u5BFE\uFF09\u3002
|
|
225
225
|
readonly [key: \`\${string}.**\`]: any;
|
|
226
226
|
}
|
|
227
|
-
|
|
227
|
+
// $scan \u306E\u51FA\u529B\u3068 $streams \u306E\u5024\u306F\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u304C\u5BA3\u8A00\u30AD\u30FC\u306E\u540D\u524D\u3067\u5B9F\u4F53\u5316\u3059\u308B\u30D7\u30ED\u30D1\u30C6\u30A3\u3002T \u306B\u306F\u5BA3\u8A00
|
|
228
|
+
// \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u4E2D\u306B\u3057\u304B\u73FE\u308C\u306A\u3044\u306E\u3067\u3001getter \u3084\u30E1\u30BD\u30C3\u30C9\u306E this \u304B\u3089\u8AAD\u3081\u308B\u3088\u3046 any \u3067\u5199\u3059\u3002initial \u306E
|
|
229
|
+
// \u578B\u306F\u4F7F\u308F\u306A\u3044 \u2014 \u7A7A\u914D\u5217\u306E initial \u304C never[] \u306B\u306A\u308A\u3001\u6B63\u3057\u3044\u8AAD\u307F\uFF08\u8981\u7D20\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\uFF09\u307E\u3067\u578B\u30A8\u30E9\u30FC\u306B\u306A\u308B\u305F\u3081\u3002
|
|
230
|
+
// T \u306B\u540C\u540D\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u660E\u793A\u7684\u306B\u4E8B\u524D\u5BA3\u8A00\u3057\u3066\u3044\u308C\u3070\u5199\u3055\u306A\u3044\uFF08\u4EA4\u5DEE\u3067\u305D\u306E\u578B\u307E\u3067 any \u306B\u6F70\u3055\u306A\u3044\u305F\u3081\uFF09\u3002
|
|
231
|
+
type _WcsDeclaredValues<T> =
|
|
232
|
+
(T extends { $scan: infer S } ? { [K in Exclude<keyof S & string, keyof T>]: any } : {}) &
|
|
233
|
+
(T extends { $streams: infer S } ? { [K in Exclude<keyof S & string, keyof T>]: any } : {});
|
|
234
|
+
type _WcsThis<T> = T & WcsStateApi & _WcsPathAccessor<T> & _WcsDeclaredValues<T>;
|
|
228
235
|
// $listKeys: { "<listPath>": "<field>" | (row) => key }\uFF08list/listKeys.ts\uFF09\u3002
|
|
229
236
|
// \u30AD\u30FC\u6307\u5B9A\u306E\u95A2\u6570\u5F15\u6570\u306B\u6587\u8108\u578B\u3092\u4E0E\u3048\u308B\u305F\u3081\u3060\u3051\u306E\u5BA3\u8A00\uFF08noImplicitAny \u4E0B\u306E\u507D\u30A8\u30E9\u30FC\u56DE\u907F\uFF09\u3002
|
|
230
237
|
type _WcsListKeys = Record<string, string | ((row: any) => unknown)>;
|
|
@@ -232,12 +239,23 @@ type _WcsListKeys = Record<string, string | ((row: any) => unknown)>;
|
|
|
232
239
|
// \u30CF\u30F3\u30C9\u30E9\u5F15\u6570\u306B\u6587\u8108\u578B\u3092\u4E0E\u3048\u308B\u305F\u3081\u3060\u3051\u306E\u5BA3\u8A00\uFF08$listKeys \u3068\u540C\u3058\u7406\u7531\uFF09\u3002
|
|
233
240
|
// this \u306F ThisType<_WcsThis<T>> \u306B\u3088\u308A state \u578B\u306B\u306A\u308B\u3002
|
|
234
241
|
type _WcsWatch = Record<string, (cur: any, prev: any, ...indexes: number[]) => void>;
|
|
242
|
+
// $scan: { "<output>": { from | on, initial, fold, resetOn? } }\uFF08scan/processScanDeclaration.ts\uFF09\u3002
|
|
243
|
+
// fold \u306E\u5F15\u6570\u306B\u6587\u8108\u578B\u3092\u4E0E\u3048\u308B\u305F\u3081\u3060\u3051\u306E\u5BA3\u8A00\u3002from \u3068 on \u3067\u7B2C 2 \u5F15\u6570\u306E\u610F\u5473\u304C\u5909\u308F\u308B\uFF08cur \u304B event\uFF09\u306E\u3067
|
|
244
|
+
// \u5F15\u6570\u306F any \u306B\u5012\u3059\u3002fold \u306B this \u306F\u6E21\u3089\u306A\u3044\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F this \u7121\u3057\u3067\u547C\u3076\uFF09\u306E\u3067 this: void \u3068\u66F8\u304F \u2014
|
|
245
|
+
// \u66F8\u304B\u306A\u3044\u3068 defineState \u306E ThisType \u304C\u30E1\u30BD\u30C3\u30C9\u5F62\u306E fold \u306B\u3082 state \u578B\u306E this \u3092\u4E0E\u3048\u3066\u3057\u307E\u3046\u3002
|
|
246
|
+
type _WcsScan = Record<string, {
|
|
247
|
+
from?: string;
|
|
248
|
+
on?: string;
|
|
249
|
+
initial: any;
|
|
250
|
+
fold: (this: void, acc: any, ...args: any[]) => any;
|
|
251
|
+
resetOn?: string[];
|
|
252
|
+
}>;
|
|
235
253
|
// $recursion: { "<anchor>": "<repeat>" }\uFF08recursion/declaration.ts\uFF09\u3002\u521D\u7248\u306F\u5358\u4E00\u306E\u81EA\u5DF1\u518D\u5E30
|
|
236
254
|
// \u306E\u307F\u3067\u3001\u30A2\u30F3\u30AB\u30FC\u3082\u53CD\u5FA9\u30B5\u30D6\u30D1\u30B9\u3082\u300C\u56FA\u5B9A\u30D7\u30ED\u30D1\u30C6\u30A3\u5217 + \u672B\u5C3E\u306E .*\u300D\u306B\u9650\u308B\u3002\u5F62\u306E\u691C\u8A3C\u306F
|
|
237
255
|
// service/recursionValidator.ts\uFF08wcs/recursion-declaration-invalid\uFF09\u304C\u62C5\u3046\u3002
|
|
238
256
|
type _WcsRecursion = Record<string, string>;
|
|
239
257
|
function defineState<T extends Record<string, any>>(
|
|
240
|
-
def: T & { $listKeys?: _WcsListKeys; $watch?: _WcsWatch; $recursion?: _WcsRecursion } & ThisType<_WcsThis<T>>
|
|
258
|
+
def: T & { $listKeys?: _WcsListKeys; $watch?: _WcsWatch; $scan?: _WcsScan; $recursion?: _WcsRecursion } & ThisType<_WcsThis<T>>
|
|
241
259
|
): T { return def; }
|
|
242
260
|
// --- end preamble ---
|
|
243
261
|
`;
|
package/dist/wcs-schema.mjs
CHANGED
package/dist/wcs-tsc.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcstack/typescript",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "TypeScript tooling for wcstack apps: wcs-schema generates the sidecar stateSchema from a typed state file, so wcs-validate checks data-wcs paths against real types in CI and every editor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.esm.js",
|