@wcstack/typescript 4.8.0 → 4.9.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 +138 -24
- 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
|
@@ -115,6 +115,9 @@ var WcsDiagnosticCode = {
|
|
|
115
115
|
// (state README「依存追跡の境界」規則 1)。ランタイムは素のアクセスと区別できないので
|
|
116
116
|
// 静的にしか検出できない。対象はオブジェクトリテラル初期値のルートだけ(配列は `items`
|
|
117
117
|
// の依存で足りる — 行の置換は `items` 自体を書き換え、in-place 変異は array-mutation が止める)。
|
|
118
|
+
// 読んだパスをページの束縛(組み込みタグの spread・書き戻す束縛・要素の出力)が書くなら error(その書き込みは
|
|
119
|
+
// getter に届かないと断定できる — 継ぎ目アプリ family-ledger)、スクリプトの書き込みだけなら warning(ルートも
|
|
120
|
+
// 置き換えるなら壊れない)。時刻・`location` を読む getter も同じ code(warning)。
|
|
118
121
|
GetterUntrackedRead: "wcs/getter-untracked-read",
|
|
119
122
|
OnHandlerThis: "wcs/on-handler-this",
|
|
120
123
|
// --- <wcs-state> script: $watch declaration ---
|
|
@@ -1213,7 +1216,7 @@ var SENTENCES = {
|
|
|
1213
1216
|
[
|
|
1214
1217
|
29
|
|
1215
1218
|
/* FilterValueNotDate */
|
|
1216
|
-
]: (fn, v) => `filter ${fn} requires a date value (a Date), not ${was(v)}${typeof v === "string" || typeof v === "number" ? " \u2014 make the Date where it is read: a getter returning new Date(\u2026)" : ""}`,
|
|
1219
|
+
]: (fn, v) => `filter ${fn} requires a date value (a Date), not ${was(v)}${typeof v === "string" || typeof v === "number" ? " \u2014 make the Date where it is read: a getter returning new Date(\u2026)" : ""}${typeof v === "string" && /^\d{4}-\d\d-\d\d$/.test(v) ? `. A date-only string is a day, not a moment: new Date(${JSON.stringify(v)}) is midnight UTC, the day before west of Greenwich \u2014 new Date(y, m - 1, d) is that day where the page is` : ""}`,
|
|
1217
1220
|
[
|
|
1218
1221
|
52
|
|
1219
1222
|
/* FilterStyleInvalid */
|
|
@@ -3311,10 +3314,10 @@ var RESERVED_WATCH_KEY = "$watch";
|
|
|
3311
3314
|
var RESERVED_SCAN_KEY = "$scan";
|
|
3312
3315
|
var RESERVED_RECURSION_KEY = RECURSION_KEY;
|
|
3313
3316
|
function analyzeStatePaths(scriptContent) {
|
|
3314
|
-
const
|
|
3315
|
-
if (!
|
|
3317
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
3318
|
+
if (!root || !root.content) return [];
|
|
3316
3319
|
const paths = [];
|
|
3317
|
-
const topLevelProps = parseTopLevelProperties(
|
|
3320
|
+
const topLevelProps = parseTopLevelProperties(root.content);
|
|
3318
3321
|
const pendingStreamValues = [];
|
|
3319
3322
|
const pendingListKeys = [];
|
|
3320
3323
|
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
@@ -3361,7 +3364,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
3361
3364
|
}
|
|
3362
3365
|
}
|
|
3363
3366
|
collectRowShapesFromAssignments(scriptContent, paths);
|
|
3364
|
-
collectGetterResultShapes(topLevelProps, effectiveDescriptors, paths);
|
|
3367
|
+
collectGetterResultShapes(scriptContent, root.start, topLevelProps, effectiveDescriptors, paths);
|
|
3365
3368
|
return paths;
|
|
3366
3369
|
}
|
|
3367
3370
|
function specFromRecursionValue(prop) {
|
|
@@ -3773,23 +3776,45 @@ var ROW_PRESERVING_METHODS = /* @__PURE__ */ new Set([
|
|
|
3773
3776
|
]);
|
|
3774
3777
|
var ROW_PICKING_METHODS = /* @__PURE__ */ new Set(["find", "findLast", "at"]);
|
|
3775
3778
|
var MAX_GETTER_SHAPE_PASSES = 4;
|
|
3776
|
-
function collectGetterResultShapes(props, effective, paths) {
|
|
3779
|
+
function collectGetterResultShapes(script, rootStart, props, effective, paths) {
|
|
3777
3780
|
const getters = /* @__PURE__ */ new Map();
|
|
3778
3781
|
for (const prop of props) {
|
|
3779
3782
|
if (prop.kind !== "getter" || prop.accessor !== "get" || prop.value === void 0) continue;
|
|
3780
3783
|
if (prop.name.startsWith("$") || hasRecursionWildcard(prop.name) || effective.get(prop.name) === "data") continue;
|
|
3781
3784
|
getters.set(prop.name, prop);
|
|
3782
3785
|
}
|
|
3786
|
+
const assignments = collectAssignments(script, rootStart, props);
|
|
3783
3787
|
for (let pass = 0; pass < MAX_GETTER_SHAPE_PASSES; pass++) {
|
|
3784
3788
|
const before = paths.length;
|
|
3785
3789
|
for (const [name, prop] of getters) {
|
|
3786
3790
|
const shape = getterResultShape(prop.value, paths);
|
|
3787
3791
|
if (shape !== null) applyResultShape(name, shape, paths);
|
|
3788
3792
|
}
|
|
3793
|
+
const aliasesByBody = /* @__PURE__ */ new Map();
|
|
3794
|
+
for (const assignment of assignments) {
|
|
3795
|
+
let aliases = /* @__PURE__ */ new Map();
|
|
3796
|
+
if (assignment.body !== null) {
|
|
3797
|
+
aliases = aliasesByBody.get(assignment.body) ?? collectAliases(assignment.body, paths);
|
|
3798
|
+
aliasesByBody.set(assignment.body, aliases);
|
|
3799
|
+
}
|
|
3800
|
+
const shape = expressionShape(assignment.expr, paths, aliases);
|
|
3801
|
+
if (shape !== null) applyAssignedShape(assignment.target, shape, paths);
|
|
3802
|
+
}
|
|
3789
3803
|
if (paths.length === before) break;
|
|
3790
3804
|
}
|
|
3791
3805
|
}
|
|
3792
3806
|
function getterResultShape(body, paths) {
|
|
3807
|
+
const scan = maskCommentsAndStrings(body);
|
|
3808
|
+
const aliases = collectAliases(body, paths);
|
|
3809
|
+
const shapes = [];
|
|
3810
|
+
for (const m of scan.matchAll(/\breturn\b/g)) {
|
|
3811
|
+
const start = m.index + m[0].length;
|
|
3812
|
+
const shape = expressionShape(readExpression(body, scan, start), paths, aliases);
|
|
3813
|
+
if (shape !== null) shapes.push(shape);
|
|
3814
|
+
}
|
|
3815
|
+
return shapes.length === 0 ? null : mergeShapes(shapes);
|
|
3816
|
+
}
|
|
3817
|
+
function collectAliases(body, paths) {
|
|
3793
3818
|
const scan = maskCommentsAndStrings(body);
|
|
3794
3819
|
const aliases = /* @__PURE__ */ new Map();
|
|
3795
3820
|
for (const m of scan.matchAll(/\b(?:const|let|var)\s+([$\w]+)\s*=(?![=>])/g)) {
|
|
@@ -3799,13 +3824,36 @@ function getterResultShape(body, paths) {
|
|
|
3799
3824
|
const shape = expressionShape(readExpression(body, scan, start), paths, aliases);
|
|
3800
3825
|
if (shape !== null) aliases.set(name, shape);
|
|
3801
3826
|
}
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3827
|
+
return aliases;
|
|
3828
|
+
}
|
|
3829
|
+
var PLAIN_ASSIGN = new RegExp(String.raw`\bthis\s*(?:\.\s*([$\w]+)|\[\s*["']([^"']+)["']\s*\])\s*=(?![=>])`, "gd");
|
|
3830
|
+
function collectAssignments(script, rootStart, props) {
|
|
3831
|
+
const scan = maskCommentsAndStrings(script);
|
|
3832
|
+
const bodies = props.filter((p) => (p.kind === "getter" || p.kind === "method") && p.value !== void 0 && p.valueStart !== void 0).map((p) => ({ start: rootStart + p.valueStart, body: p.value }));
|
|
3833
|
+
const out = [];
|
|
3834
|
+
PLAIN_ASSIGN.lastIndex = 0;
|
|
3835
|
+
let match;
|
|
3836
|
+
while ((match = PLAIN_ASSIGN.exec(scan)) !== null) {
|
|
3837
|
+
if (/\.\s*$/.test(scan.slice(Math.max(0, match.index - 8), match.index))) continue;
|
|
3838
|
+
const span = match.indices[1] ?? match.indices[2];
|
|
3839
|
+
const target = script.slice(span[0], span[1]);
|
|
3840
|
+
if (target.startsWith("$") || target.includes("*")) continue;
|
|
3841
|
+
const expr = readExpression(script, scan, match.index + match[0].length);
|
|
3842
|
+
const owner = bodies.find((b) => b.start <= match.index && match.index < b.start + b.body.length);
|
|
3843
|
+
out.push({ target, expr, body: owner?.body ?? null });
|
|
3844
|
+
}
|
|
3845
|
+
return out;
|
|
3846
|
+
}
|
|
3847
|
+
function applyAssignedShape(target, shape, paths) {
|
|
3848
|
+
const declared = paths.find((p) => p.path === target);
|
|
3849
|
+
if (declared === void 0 || declared.kind !== "data") return;
|
|
3850
|
+
if (hasPath(paths, `${target}.*`)) {
|
|
3851
|
+
if (shape.isList) applyResultShape(target, shape, paths);
|
|
3852
|
+
return;
|
|
3853
|
+
}
|
|
3854
|
+
if (!shape.isList && declared.rawInitial !== void 0 && isObjectLiteral(declared.rawInitial)) {
|
|
3855
|
+
applyResultShape(target, shape, paths);
|
|
3807
3856
|
}
|
|
3808
|
-
return shapes.length === 0 ? null : mergeShapes(shapes);
|
|
3809
3857
|
}
|
|
3810
3858
|
function readExpression(text3, scan, start) {
|
|
3811
3859
|
let depth = 0;
|
|
@@ -3860,9 +3908,7 @@ function expressionShape(text3, paths, aliases) {
|
|
|
3860
3908
|
}
|
|
3861
3909
|
} else if (scan.startsWith("(")) {
|
|
3862
3910
|
const inner = extractDelimitedContent(expr, scan, 0, "(", ")");
|
|
3863
|
-
|
|
3864
|
-
if (base === null) return null;
|
|
3865
|
-
shape = base;
|
|
3911
|
+
shape = expressionShape(inner, paths, aliases) ?? OPAQUE;
|
|
3866
3912
|
rest = inner.length + 2;
|
|
3867
3913
|
} else {
|
|
3868
3914
|
const base = readBase(expr, scan, aliases, paths);
|
|
@@ -4211,9 +4257,6 @@ function locateDefaultExportObject(script) {
|
|
|
4211
4257
|
const braceIndex = scan.indexOf(match[1], match.index);
|
|
4212
4258
|
return { content: extractBracedContent(script, scan, braceIndex), start: braceIndex + 1 };
|
|
4213
4259
|
}
|
|
4214
|
-
function extractDefaultExportObject(script) {
|
|
4215
|
-
return locateDefaultExportObject(script)?.content ?? null;
|
|
4216
|
-
}
|
|
4217
4260
|
function parseTopLevelProperties(objectContent, allowEmptyDataKeys = false) {
|
|
4218
4261
|
const props = [];
|
|
4219
4262
|
const scan = maskCommentsAndStrings(objectContent);
|
|
@@ -11408,6 +11451,7 @@ var ja = {
|
|
|
11408
11451
|
pathReadonlyTwoWay: (p, prop) => `"${p}" \u306F setter \u306E\u7121\u3044 getter \u3067\u3059\u304C\u3001${prop}: \u306F\u8AAD\u8005\u306E\u5165\u529B\u3092\u66F8\u304D\u623B\u3059\u306E\u3067\u3001\u5165\u529B\u306E\u305F\u3073\u306B\u66F8\u304D\u8FBC\u307F\u304C\u5931\u6557\u3057\u307E\u3059\uFF08"${p}" is a getter without a setter\uFF09\u3002setter \u3092\u8DB3\u3059\uFF08set "${p}"(v) { \u2026 }\uFF09\u304B\u3001#ro \u3067\u4E00\u65B9\u5411\u306B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08${prop}#ro: ${p}\uFF09`,
|
|
11409
11452
|
submitWithoutPrevent: (h) => `onsubmit \u306B #prevent \u304C\u7121\u304F\u3001"${h}" \u3082 preventDefault \u3092\u547C\u3073\u307E\u305B\u3093\u3002\u30D6\u30E9\u30A6\u30B6\u306F\u30D5\u30A9\u30FC\u30E0\u3082\u9001\u308B\u306E\u3067\u3001\u30DA\u30FC\u30B8\u304C\u8AAD\u307F\u76F4\u3055\u308C\u308B\u304B\u3001router \u304C\u3042\u308C\u3070 GET \u304C\u6B04\u306E\u5024\uFF08\u30D1\u30B9\u30EF\u30FC\u30C9\u3082\uFF09\u3092 URL \u3068\u5C65\u6B74\u306B\u8F09\u305B\u307E\u3059\u3002onsubmit#prevent: ${h} \u3068\u66F8\u304F\u304B\u3001\u30CF\u30F3\u30C9\u30E9\u3067 event.preventDefault() \u3092\u547C\u3093\u3067\u304F\u3060\u3055\u3044`,
|
|
11410
11453
|
getterUntrackedRead: (root, sp) => `\u3053\u3053\u3067\u8FFD\u8DE1\u3055\u308C\u308B\u306E\u306F "${root}" \u3060\u3051\u3067\u3059\u3002"${sp}" \u304C\u5909\u308F\u3063\u3066\u3082\u3053\u306E getter \u306F\u518D\u8A55\u4FA1\u3055\u308C\u307E\u305B\u3093\uFF08\u30D1\u30B9\u8AAD\u307F\u53D6\u308A\u306E\u5148\u306E\u7D20\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u30A2\u30AF\u30BB\u30B9\u306F\u8FFD\u8DE1\u3055\u308C\u306A\u3044\uFF09\u3002this["${sp}"] \u3067\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044`,
|
|
11454
|
+
getterUntrackedReadBound: (root, sp, binding, tag, written) => `\u3053\u3053\u3067\u8FFD\u8DE1\u3055\u308C\u308B\u306E\u306F "${root}" \u3060\u3051\u3067\u3059\u304C\u3001<${tag}> \u306E\u675F\u7E1B "${binding}" \u306F "${written}" \u3092\u30D1\u30B9\u3067\u66F8\u304D\u307E\u3059\u3002\u305D\u306E\u66F8\u304D\u8FBC\u307F\u306F\u3053\u306E getter \u306B\u5C4A\u304B\u305A\u3001getter \u306F\u524D\u306B\u8A08\u7B97\u3057\u305F\u5024\u3092\u8FD4\u3057\u7D9A\u3051\u307E\u3059\uFF08\u30D1\u30B9\u8AAD\u307F\u53D6\u308A\u306E\u5148\u306E\u7D20\u306E\u30D7\u30ED\u30D1\u30C6\u30A3\u30A2\u30AF\u30BB\u30B9\u306F\u8FFD\u8DE1\u3055\u308C\u306A\u3044\uFF09\u3002this["${sp}"] \u3067\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044`,
|
|
11411
11455
|
handlerFilterNotAllowed: (prop) => `\u30A4\u30D9\u30F3\u30C8\u30CF\u30F3\u30C9\u30E9 "${prop}" \u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
|
|
11412
11456
|
typeExpectation: (label, expected, resultType) => `"${label}" \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728\u306E\u578B: ${resultType}\uFF09`,
|
|
11413
11457
|
filterUnknown: (n) => `\u30D5\u30A3\u30EB\u30BF "${n}" \u306F\u7D44\u307F\u8FBC\u307F\u30D5\u30A3\u30EB\u30BF\u306B\u5B58\u5728\u3057\u307E\u305B\u3093`,
|
|
@@ -11618,6 +11662,7 @@ var en = {
|
|
|
11618
11662
|
pathReadonlyTwoWay: (p, prop) => `"${p}" is a getter without a setter, and ${prop}: writes back what the reader enters, so every edit fails ("${p}" is a getter without a setter). Add a setter (set "${p}"(v) { \u2026 }), or bind it one way with #ro (${prop}#ro: ${p})`,
|
|
11619
11663
|
submitWithoutPrevent: (h) => `onsubmit has no #prevent and "${h}" does not call preventDefault, so the browser submits the form too: the page reloads, or with the router a GET puts the fields (a password too) in the URL and the history. Write onsubmit#prevent: ${h}, or call event.preventDefault() in the handler`,
|
|
11620
11664
|
getterUntrackedRead: (root, sp) => `Only "${root}" is tracked here: the getter is not re-evaluated when "${sp}" changes (plain property access after a path read is not tracked). Read this["${sp}"] instead`,
|
|
11665
|
+
getterUntrackedReadBound: (root, sp, binding, tag, written) => `Only "${root}" is tracked here, but the binding "${binding}" on <${tag}> writes "${written}" by path: that write never reaches this getter, which keeps returning the value it last computed (plain property access after a path read is not tracked). Read this["${sp}"] instead`,
|
|
11621
11666
|
handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
|
|
11622
11667
|
typeExpectation: (label, expected, resultType) => `"${label}" requires ${EN_EXPECTED_LABEL[expected]} (current type: ${resultType})`,
|
|
11623
11668
|
filterUnknown: (n) => `Filter "${n}" is not a built-in filter`,
|
|
@@ -12355,7 +12400,8 @@ function openTagBefore(html, valueStart) {
|
|
|
12355
12400
|
return lt2 === -1 ? "" : html.slice(lt2, valueStart);
|
|
12356
12401
|
}
|
|
12357
12402
|
function writesBack(attr, prop, written) {
|
|
12358
|
-
|
|
12403
|
+
const modifiers = written.split("#").slice(1).flatMap((part) => part.split(","));
|
|
12404
|
+
if (modifiers.some((modifier) => modifier.trim() === "ro")) return false;
|
|
12359
12405
|
if (prop === "radio" || prop === "checkbox") return true;
|
|
12360
12406
|
const tag = attr.tagName;
|
|
12361
12407
|
if (tag === void 0 || tag.includes("-")) return false;
|
|
@@ -14298,6 +14344,9 @@ var BUILTIN_TAGS = {
|
|
|
14298
14344
|
"withCredentials": "with-credentials",
|
|
14299
14345
|
"events": "events",
|
|
14300
14346
|
"raw": "raw",
|
|
14347
|
+
"autoReconnect": "auto-reconnect",
|
|
14348
|
+
"reconnectInterval": "reconnect-interval",
|
|
14349
|
+
"maxReconnects": "max-reconnects",
|
|
14301
14350
|
"staleAfter": "stale-after",
|
|
14302
14351
|
"manual": "manual",
|
|
14303
14352
|
"trigger": null
|
|
@@ -14425,6 +14474,14 @@ var BUILTIN_TAGS = {
|
|
|
14425
14474
|
"abort"
|
|
14426
14475
|
]
|
|
14427
14476
|
},
|
|
14477
|
+
"wcs-upload-header": {
|
|
14478
|
+
"package": "upload",
|
|
14479
|
+
"hasWcBindable": false,
|
|
14480
|
+
"observedAttributes": [],
|
|
14481
|
+
"inputs": {},
|
|
14482
|
+
"properties": [],
|
|
14483
|
+
"commands": []
|
|
14484
|
+
},
|
|
14428
14485
|
"wcs-view-transition": {
|
|
14429
14486
|
"package": "view-transition",
|
|
14430
14487
|
"hasWcBindable": true,
|
|
@@ -16713,7 +16770,7 @@ function validateGetterCycles(script, scriptStart, locale2) {
|
|
|
16713
16770
|
}
|
|
16714
16771
|
return out;
|
|
16715
16772
|
}
|
|
16716
|
-
function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, locale2) {
|
|
16773
|
+
function validateGetterUntrackedReads(script, scriptStart, mountPath, nestedWriteRoots, bindingWrites, locale2) {
|
|
16717
16774
|
const getters = analyzeCallableBodies(script).filter((entry) => entry.kind === "getter" && entry.accessor === "get");
|
|
16718
16775
|
if (getters.length === 0) return [];
|
|
16719
16776
|
const objectRoots = /* @__PURE__ */ new Set();
|
|
@@ -16731,18 +16788,66 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
|
|
|
16731
16788
|
if (read.written) continue;
|
|
16732
16789
|
const segments = read.callee ? read.chain.slice(0, -1) : read.chain;
|
|
16733
16790
|
if (segments.length < 2 || !objectRoots.has(segments[0])) continue;
|
|
16734
|
-
|
|
16791
|
+
const prefix = mountPath === null ? [] : mountPath.split(".");
|
|
16792
|
+
const write = boundWriteOf([...prefix, ...segments], prefix.length + read.path.split(".").length, bindingWrites());
|
|
16793
|
+
if (write === void 0 && !nestedWriteRoots().has([...prefix, segments[0]].join("."))) continue;
|
|
16794
|
+
const sp = segments.join(".");
|
|
16735
16795
|
out.push({
|
|
16736
16796
|
code: WcsDiagnosticCode.GetterUntrackedRead,
|
|
16737
16797
|
start: scriptStart + getter.bodyStart + read.start,
|
|
16738
16798
|
end: scriptStart + getter.bodyStart + read.end,
|
|
16739
|
-
message: msgs.getterUntrackedRead(segments[0],
|
|
16740
|
-
severity: "warning"
|
|
16799
|
+
message: write === void 0 ? msgs.getterUntrackedRead(segments[0], sp) : msgs.getterUntrackedReadBound(segments[0], sp, write.binding, write.tag, write.path),
|
|
16800
|
+
severity: write === void 0 ? "warning" : "error"
|
|
16741
16801
|
});
|
|
16742
16802
|
}
|
|
16743
16803
|
}
|
|
16744
16804
|
return out;
|
|
16745
16805
|
}
|
|
16806
|
+
function boundWriteOf(segments, tracked, writes) {
|
|
16807
|
+
return writes.find((write) => {
|
|
16808
|
+
const written = write.path.split(".");
|
|
16809
|
+
if (written.length <= tracked || written.length > segments.length) return false;
|
|
16810
|
+
return written.every((s, i) => s === segments[i] || s === "*" && /^\d+$/.test(segments[i]));
|
|
16811
|
+
});
|
|
16812
|
+
}
|
|
16813
|
+
function collectBindingWrites(html, bindAttrName) {
|
|
16814
|
+
const out = [];
|
|
16815
|
+
const ignored = findIgnoredRanges(html, bindAttrName);
|
|
16816
|
+
for (const attr of findAllBindAttributes(html, bindAttrName)) {
|
|
16817
|
+
if (ignored.some(([start, end]) => attr.valueStart >= start && attr.valueStart < end)) continue;
|
|
16818
|
+
const tag = attr.tagName ?? "";
|
|
16819
|
+
const contract = BUILTIN_TAGS[tag];
|
|
16820
|
+
const outputs = contract?.hasWcBindable === true ? contract.properties : null;
|
|
16821
|
+
const bindings = parseBindTextWithPositions(attr.value);
|
|
16822
|
+
bindings.forEach((binding, index) => {
|
|
16823
|
+
const parsed = binding.parsed;
|
|
16824
|
+
if (parsed === null || binding.pathRange === null) return;
|
|
16825
|
+
let path = parsed.statePathName;
|
|
16826
|
+
if (path.startsWith(".")) {
|
|
16827
|
+
const at2 = attr.valueStart + binding.pathRange.start;
|
|
16828
|
+
const list = getRowShorthandForPath(html, at2, bindAttrName) === null ? null : getResolvedForListPath(html, at2, bindAttrName);
|
|
16829
|
+
if (list === null) return;
|
|
16830
|
+
path = path === "." ? `${list}.*` : `${list}.*.${path.slice(1)}`;
|
|
16831
|
+
}
|
|
16832
|
+
const write = (written) => {
|
|
16833
|
+
out.push({ path: written, binding: binding.exprText, tag });
|
|
16834
|
+
};
|
|
16835
|
+
if (parsed.bindingType === "spread") {
|
|
16836
|
+
if (outputs === null) return;
|
|
16837
|
+
const later = new Set(bindings.slice(index + 1).flatMap((b) => b.parsed?.bindingType === "prop" ? [b.parsed.propName] : []));
|
|
16838
|
+
for (const output of outputs) if (!later.has(output)) write(`${path}.${output}`);
|
|
16839
|
+
return;
|
|
16840
|
+
}
|
|
16841
|
+
if (parsed.propModifiers.includes("ro")) return;
|
|
16842
|
+
if (parsed.bindingType === "radio" || parsed.bindingType === "checkbox") {
|
|
16843
|
+
write(path);
|
|
16844
|
+
} else if (parsed.bindingType === "prop" && (writesBack(attr, parsed.propName, parsed.propName) || outputs?.includes(parsed.propName) === true)) {
|
|
16845
|
+
write(path);
|
|
16846
|
+
}
|
|
16847
|
+
});
|
|
16848
|
+
}
|
|
16849
|
+
return out;
|
|
16850
|
+
}
|
|
16746
16851
|
function validateGetterLocationReads(script, scriptStart, locale2) {
|
|
16747
16852
|
if (!script.includes("location")) return [];
|
|
16748
16853
|
const msgs = getMessages(locale2);
|
|
@@ -17073,12 +17178,21 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
|
|
|
17073
17178
|
const blocks = parseLoadedScriptBlocks(html, stateTagName);
|
|
17074
17179
|
let nestedWriteRoots = null;
|
|
17075
17180
|
const getNestedWriteRoots = () => nestedWriteRoots ??= collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks);
|
|
17181
|
+
let bindingWrites = null;
|
|
17182
|
+
const getBindingWrites = () => bindingWrites ??= collectBindingWrites(html, bindAttrName);
|
|
17076
17183
|
for (const block of blocks) {
|
|
17077
17184
|
out.push(...validateIndexArity(block.content, block.contentStart, locale2));
|
|
17078
17185
|
out.push(...validateNameAliases(block.content, block.contentStart, locale2, block.mountPath));
|
|
17079
17186
|
out.push(...validateIndexParamReads(block.content, block.contentStart, locale2));
|
|
17080
17187
|
out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
|
|
17081
|
-
out.push(...validateGetterUntrackedReads(
|
|
17188
|
+
out.push(...validateGetterUntrackedReads(
|
|
17189
|
+
block.content,
|
|
17190
|
+
block.contentStart,
|
|
17191
|
+
block.mountPath,
|
|
17192
|
+
getNestedWriteRoots,
|
|
17193
|
+
getBindingWrites,
|
|
17194
|
+
locale2
|
|
17195
|
+
));
|
|
17082
17196
|
out.push(...validateGetterLocationReads(block.content, block.contentStart, locale2));
|
|
17083
17197
|
out.push(...validateGetterClockReads(block.content, block.contentStart, locale2));
|
|
17084
17198
|
out.push(...validateOnHandlerThis(block.content, block.contentStart, locale2));
|
package/dist/wcs-schema.mjs
CHANGED
package/dist/wcs-tsc.mjs
CHANGED
|
@@ -25,7 +25,7 @@ function unsupportedTypeScriptMessage(version) {
|
|
|
25
25
|
return `@wcstack/typescript runs on TypeScript 5 or 6, and the project has TypeScript ${version}, which has no JavaScript compiler API: npm i -D typescript@6`;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
var version = "4.
|
|
28
|
+
var version = "4.9.0";
|
|
29
29
|
var pkg = {
|
|
30
30
|
version: version};
|
|
31
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcstack/typescript",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.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",
|