@wcstack/typescript 4.0.0 → 4.0.2
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 +739 -33
- 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
|
@@ -199,6 +199,13 @@ var WcsDiagnosticCode = {
|
|
|
199
199
|
TriggerSeededTruthy: "wcs/trigger-seeded-truthy",
|
|
200
200
|
// 非 manual <wcs-storage> value バインド先の空値シード(初期書き戻しが保存値を上書き)。
|
|
201
201
|
StorageSeedClobber: "wcs/storage-seed-clobber",
|
|
202
|
+
// `value#init=element` の <wcs-storage> バインド先の null 以外のリテラルシード
|
|
203
|
+
// (空のキーは null を読むので、初回の引き込みでシードが null に置き換わる)。
|
|
204
|
+
StorageInitSeed: "wcs/storage-init-seed",
|
|
205
|
+
// 組み込み I/O ノードの入力を束ねたパスの下へ、ページが書き込む(`value: cart` と
|
|
206
|
+
// `this["cart.0.quantity"] = …`)。束縛はそのパス自身への代入でしか当て直されないので、
|
|
207
|
+
// 下への書き込みは要素に届かない(storage なら保存されない)。
|
|
208
|
+
InputNestedWrite: "wcs/input-nested-write",
|
|
202
209
|
// --- accessibility (docs/a11y-design.md §8 / D9) ---
|
|
203
210
|
// `attr.aria-*` バインドの属性名が WAI-ARIA に存在しない(タイポ)。
|
|
204
211
|
// setAttribute はそのまま書き、支援技術は黙って無視する。severity は warning
|
|
@@ -1309,6 +1316,10 @@ var SENTENCES = {
|
|
|
1309
1316
|
121
|
|
1310
1317
|
/* ForNoFilters */
|
|
1311
1318
|
]: (t) => `"${t}": "for:" takes no filters \u2014 a row is "<path>.<index>", so the rows of a filtered list would name other elements. Declare a getter that returns the filtered list and loop over it ("for: <getter>").`,
|
|
1319
|
+
[
|
|
1320
|
+
122
|
|
1321
|
+
/* NamespaceNeedsName */
|
|
1322
|
+
]: (t, ns) => ns === "class" ? `"${t}": "class:" needs a class name \u2014 "class.NAME:" toggles one class (a bare "class:" would set an element property named "class", which does nothing). To set the whole class attribute, bind "className:".` : `"${t}": "attr:" needs an attribute name \u2014 "attr.NAME:" sets one attribute (a bare "attr:" would set an element property named "attr", which does nothing).`,
|
|
1312
1323
|
[
|
|
1313
1324
|
119
|
|
1314
1325
|
/* EmptySegment */
|
|
@@ -1573,6 +1584,9 @@ function parseBindTextsForElement(bindText) {
|
|
|
1573
1584
|
bindingType: keyword === SPREAD_PROP ? "spread" : keyword
|
|
1574
1585
|
};
|
|
1575
1586
|
}
|
|
1587
|
+
if (keyword === CLASS_NAMESPACE || keyword === ATTR_NAMESPACE) {
|
|
1588
|
+
raise(122, [bindText2, keyword]);
|
|
1589
|
+
}
|
|
1576
1590
|
const stateResult = parseStatePart(statePart);
|
|
1577
1591
|
const propResult = parsePropPart(propPart);
|
|
1578
1592
|
const [head] = propResult.propSegments;
|
|
@@ -2815,6 +2829,292 @@ function coversSuffix(spec, familySuffix, suffix) {
|
|
|
2815
2829
|
return false;
|
|
2816
2830
|
}
|
|
2817
2831
|
|
|
2832
|
+
// src/service/jsdocType.ts
|
|
2833
|
+
var UNKNOWN = { kind: "unknown" };
|
|
2834
|
+
var NULL_TYPE = { kind: "name", name: "null" };
|
|
2835
|
+
var ARRAY_GENERICS = /* @__PURE__ */ new Set(["Array", "ReadonlyArray"]);
|
|
2836
|
+
var MAX_RESOLVE_DEPTH = 8;
|
|
2837
|
+
function tokenize(text3) {
|
|
2838
|
+
const tokens = [];
|
|
2839
|
+
let i = 0;
|
|
2840
|
+
while (i < text3.length) {
|
|
2841
|
+
const ch = text3[i];
|
|
2842
|
+
if (/\s/.test(ch)) {
|
|
2843
|
+
i++;
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
if (ch === "=" && text3[i + 1] === ">") {
|
|
2847
|
+
tokens.push({ t: "punct", v: "=>" });
|
|
2848
|
+
i += 2;
|
|
2849
|
+
continue;
|
|
2850
|
+
}
|
|
2851
|
+
if ("{}[]<>()|,;:?!*.=".includes(ch)) {
|
|
2852
|
+
tokens.push({ t: "punct", v: ch });
|
|
2853
|
+
i++;
|
|
2854
|
+
continue;
|
|
2855
|
+
}
|
|
2856
|
+
if (ch === '"' || ch === "'") {
|
|
2857
|
+
const end = text3.indexOf(ch, i + 1);
|
|
2858
|
+
if (end === -1) return null;
|
|
2859
|
+
tokens.push({ t: "string", v: text3.slice(i + 1, end) });
|
|
2860
|
+
i = end + 1;
|
|
2861
|
+
continue;
|
|
2862
|
+
}
|
|
2863
|
+
const num = /^-?\d+(?:\.\d+)?/.exec(text3.slice(i));
|
|
2864
|
+
if (num) {
|
|
2865
|
+
tokens.push({ t: "number" });
|
|
2866
|
+
i += num[0].length;
|
|
2867
|
+
continue;
|
|
2868
|
+
}
|
|
2869
|
+
const name = /^[$A-Za-z_][$\w]*/.exec(text3.slice(i));
|
|
2870
|
+
if (name) {
|
|
2871
|
+
tokens.push({ t: "name", v: name[0] });
|
|
2872
|
+
i += name[0].length;
|
|
2873
|
+
continue;
|
|
2874
|
+
}
|
|
2875
|
+
return null;
|
|
2876
|
+
}
|
|
2877
|
+
return tokens;
|
|
2878
|
+
}
|
|
2879
|
+
function parseJsDocType(text3) {
|
|
2880
|
+
const tokens = tokenize(text3);
|
|
2881
|
+
if (tokens === null || tokens.length === 0) return null;
|
|
2882
|
+
let pos = 0;
|
|
2883
|
+
const peek = () => tokens[pos];
|
|
2884
|
+
const isPunct = (v) => {
|
|
2885
|
+
const token = tokens[pos];
|
|
2886
|
+
return token !== void 0 && token.t === "punct" && token.v === v;
|
|
2887
|
+
};
|
|
2888
|
+
const expect = (v) => {
|
|
2889
|
+
if (!isPunct(v)) return false;
|
|
2890
|
+
pos++;
|
|
2891
|
+
return true;
|
|
2892
|
+
};
|
|
2893
|
+
const parseUnion = () => {
|
|
2894
|
+
expect("|");
|
|
2895
|
+
const members = [];
|
|
2896
|
+
for (; ; ) {
|
|
2897
|
+
const member = parsePostfix();
|
|
2898
|
+
if (member === null) return null;
|
|
2899
|
+
members.push(member);
|
|
2900
|
+
if (!expect("|")) break;
|
|
2901
|
+
}
|
|
2902
|
+
if (isPunct("=>")) {
|
|
2903
|
+
pos++;
|
|
2904
|
+
if (parseUnion() === null) return null;
|
|
2905
|
+
return UNKNOWN;
|
|
2906
|
+
}
|
|
2907
|
+
return members.length === 1 ? members[0] : { kind: "union", members };
|
|
2908
|
+
};
|
|
2909
|
+
const parsePostfix = () => {
|
|
2910
|
+
let type2 = parsePrimary();
|
|
2911
|
+
if (type2 === null) return null;
|
|
2912
|
+
while (isPunct("[") && tokens[pos + 1]?.t === "punct" && tokens[pos + 1].v === "]") {
|
|
2913
|
+
pos += 2;
|
|
2914
|
+
type2 = { kind: "array", element: type2 };
|
|
2915
|
+
}
|
|
2916
|
+
return type2;
|
|
2917
|
+
};
|
|
2918
|
+
const parsePrimary = () => {
|
|
2919
|
+
const token = peek();
|
|
2920
|
+
if (token === void 0) return null;
|
|
2921
|
+
if (token.t === "string" || token.t === "number") {
|
|
2922
|
+
pos++;
|
|
2923
|
+
return UNKNOWN;
|
|
2924
|
+
}
|
|
2925
|
+
if (token.t === "punct") {
|
|
2926
|
+
if (token.v === "{") return parseObject();
|
|
2927
|
+
if (token.v === "(") {
|
|
2928
|
+
pos++;
|
|
2929
|
+
if (expect(")")) return isPunct("=>") ? UNKNOWN : null;
|
|
2930
|
+
const inner = parseUnion();
|
|
2931
|
+
if (inner === null) return null;
|
|
2932
|
+
if (isPunct(":") || isPunct(",")) {
|
|
2933
|
+
if (!skipUntilClose("(", ")")) return null;
|
|
2934
|
+
return UNKNOWN;
|
|
2935
|
+
}
|
|
2936
|
+
return expect(")") ? inner : null;
|
|
2937
|
+
}
|
|
2938
|
+
if (token.v === "?") {
|
|
2939
|
+
pos++;
|
|
2940
|
+
const next = peek();
|
|
2941
|
+
if (next === void 0 || next.t === "punct" && ["|", ",", ";", ")", ">", "]", "}", "="].includes(next.v)) return UNKNOWN;
|
|
2942
|
+
const inner = parsePostfix();
|
|
2943
|
+
return inner === null ? null : { kind: "union", members: [inner, NULL_TYPE] };
|
|
2944
|
+
}
|
|
2945
|
+
if (token.v === "!") {
|
|
2946
|
+
pos++;
|
|
2947
|
+
return parsePostfix();
|
|
2948
|
+
}
|
|
2949
|
+
if (token.v === "*") {
|
|
2950
|
+
pos++;
|
|
2951
|
+
return UNKNOWN;
|
|
2952
|
+
}
|
|
2953
|
+
return null;
|
|
2954
|
+
}
|
|
2955
|
+
pos++;
|
|
2956
|
+
let name = token.v;
|
|
2957
|
+
while (isPunct(".") && tokens[pos + 1]?.t === "name") {
|
|
2958
|
+
name += "." + tokens[pos + 1].v;
|
|
2959
|
+
pos += 2;
|
|
2960
|
+
}
|
|
2961
|
+
if ((name === "typeof" || name === "keyof") && peek()?.t === "name") {
|
|
2962
|
+
pos++;
|
|
2963
|
+
return UNKNOWN;
|
|
2964
|
+
}
|
|
2965
|
+
if (name === "function" && isPunct("(")) {
|
|
2966
|
+
pos++;
|
|
2967
|
+
if (!skipUntilClose("(", ")")) return null;
|
|
2968
|
+
if (expect(":")) {
|
|
2969
|
+
if (parsePostfix() === null) return null;
|
|
2970
|
+
}
|
|
2971
|
+
return UNKNOWN;
|
|
2972
|
+
}
|
|
2973
|
+
if (isPunct(".") && tokens[pos + 1]?.t === "punct" && tokens[pos + 1].v === "<") pos++;
|
|
2974
|
+
if (isPunct("<")) {
|
|
2975
|
+
pos++;
|
|
2976
|
+
const args = [];
|
|
2977
|
+
for (; ; ) {
|
|
2978
|
+
const arg = parseUnion();
|
|
2979
|
+
if (arg === null) return null;
|
|
2980
|
+
args.push(arg);
|
|
2981
|
+
if (!expect(",")) break;
|
|
2982
|
+
}
|
|
2983
|
+
if (!expect(">")) return null;
|
|
2984
|
+
return ARRAY_GENERICS.has(name) && args.length === 1 ? { kind: "array", element: args[0] } : UNKNOWN;
|
|
2985
|
+
}
|
|
2986
|
+
return { kind: "name", name };
|
|
2987
|
+
};
|
|
2988
|
+
const parseObject = () => {
|
|
2989
|
+
pos++;
|
|
2990
|
+
const fields = [];
|
|
2991
|
+
while (!isPunct("}")) {
|
|
2992
|
+
const key = peek();
|
|
2993
|
+
if (key === void 0) return null;
|
|
2994
|
+
if (key.t === "punct" && key.v === "[") {
|
|
2995
|
+
pos++;
|
|
2996
|
+
if (!skipUntilClose("[", "]")) return null;
|
|
2997
|
+
if (!expect(":") || parseUnion() === null) return null;
|
|
2998
|
+
} else {
|
|
2999
|
+
if (key.t !== "name" && key.t !== "string") return null;
|
|
3000
|
+
pos++;
|
|
3001
|
+
expect("?");
|
|
3002
|
+
if (!expect(":")) return null;
|
|
3003
|
+
const type2 = parseUnion();
|
|
3004
|
+
if (type2 === null) return null;
|
|
3005
|
+
fields.push({ name: key.v, type: type2 });
|
|
3006
|
+
}
|
|
3007
|
+
if (!expect(",") && !expect(";")) break;
|
|
3008
|
+
}
|
|
3009
|
+
return expect("}") ? { kind: "object", fields } : null;
|
|
3010
|
+
};
|
|
3011
|
+
const skipUntilClose = (open, close) => {
|
|
3012
|
+
let depth = 0;
|
|
3013
|
+
while (pos < tokens.length) {
|
|
3014
|
+
const token = tokens[pos++];
|
|
3015
|
+
if (token.t !== "punct") continue;
|
|
3016
|
+
if (token.v === open) depth++;
|
|
3017
|
+
else if (token.v === close) {
|
|
3018
|
+
if (depth === 0) return true;
|
|
3019
|
+
depth--;
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
return false;
|
|
3023
|
+
};
|
|
3024
|
+
const type = parseUnion();
|
|
3025
|
+
if (type === null || pos !== tokens.length) return null;
|
|
3026
|
+
return type;
|
|
3027
|
+
}
|
|
3028
|
+
function findJsDocComments(script, scan) {
|
|
3029
|
+
const out = [];
|
|
3030
|
+
let from = 0;
|
|
3031
|
+
for (; ; ) {
|
|
3032
|
+
const start = scan.indexOf("/*", from);
|
|
3033
|
+
if (start === -1) break;
|
|
3034
|
+
const close = scan.indexOf("*/", start + 2);
|
|
3035
|
+
if (close === -1) break;
|
|
3036
|
+
from = close + 2;
|
|
3037
|
+
if (script[start + 2] === "*") out.push({ start, end: close + 2, text: script.slice(start, close + 2) });
|
|
3038
|
+
}
|
|
3039
|
+
return out;
|
|
3040
|
+
}
|
|
3041
|
+
function readJsDocTagType(comment, tag) {
|
|
3042
|
+
return readBracedType(comment, tag)?.type.trim() ?? null;
|
|
3043
|
+
}
|
|
3044
|
+
function readBracedType(comment, tag) {
|
|
3045
|
+
const tagMatch = new RegExp(`@${tag}\\s*\\{`).exec(comment);
|
|
3046
|
+
if (!tagMatch) return null;
|
|
3047
|
+
const open = tagMatch.index + tagMatch[0].length - 1;
|
|
3048
|
+
let depth = 0;
|
|
3049
|
+
for (let i = open; i < comment.length; i++) {
|
|
3050
|
+
const ch = comment[i];
|
|
3051
|
+
if (ch === "{") depth++;
|
|
3052
|
+
else if (ch === "}") {
|
|
3053
|
+
depth--;
|
|
3054
|
+
if (depth === 0) {
|
|
3055
|
+
const type = comment.slice(open + 1, i).replace(/\n\s*\*(?!\/)/g, "\n ");
|
|
3056
|
+
return { type, end: i + 1 };
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
return null;
|
|
3061
|
+
}
|
|
3062
|
+
function readJsDocTypeBefore(content, propIndex) {
|
|
3063
|
+
let end = propIndex;
|
|
3064
|
+
while (end > 0 && /\s/.test(content[end - 1])) end--;
|
|
3065
|
+
if (!content.startsWith("*/", end - 2)) return void 0;
|
|
3066
|
+
const start = content.lastIndexOf("/*", end - 2);
|
|
3067
|
+
if (start === -1 || content[start + 2] !== "*") return void 0;
|
|
3068
|
+
return readBracedType(content.slice(start, end), "type")?.type.trim();
|
|
3069
|
+
}
|
|
3070
|
+
function collectJsDocTypedefs(script, scan) {
|
|
3071
|
+
const typedefs = /* @__PURE__ */ new Map();
|
|
3072
|
+
for (const { text: comment } of findJsDocComments(script, scan)) {
|
|
3073
|
+
const braced = readBracedType(comment, "typedef");
|
|
3074
|
+
if (braced === null) continue;
|
|
3075
|
+
const name = /^\s*([$A-Za-z_][$\w]*)/.exec(comment.slice(braced.end));
|
|
3076
|
+
const type = parseJsDocType(braced.type);
|
|
3077
|
+
if (name && type !== null) typedefs.set(name[1], type);
|
|
3078
|
+
}
|
|
3079
|
+
return typedefs;
|
|
3080
|
+
}
|
|
3081
|
+
function flattenJsDocType(type, typedefs, depth = 0) {
|
|
3082
|
+
if (type.kind === "union") return type.members.flatMap((member) => flattenJsDocType(member, typedefs, depth));
|
|
3083
|
+
if (type.kind === "name" && depth < MAX_RESOLVE_DEPTH) {
|
|
3084
|
+
const resolved = typedefs.get(type.name);
|
|
3085
|
+
if (resolved !== void 0) return flattenJsDocType(resolved, typedefs, depth + 1);
|
|
3086
|
+
}
|
|
3087
|
+
return [type];
|
|
3088
|
+
}
|
|
3089
|
+
function jsDocTypeHint(type, typedefs = /* @__PURE__ */ new Map()) {
|
|
3090
|
+
const hints = /* @__PURE__ */ new Set();
|
|
3091
|
+
for (const member of flattenJsDocType(type, typedefs)) {
|
|
3092
|
+
if (member.kind === "array") hints.add("array");
|
|
3093
|
+
else if (member.kind === "object") hints.add("object");
|
|
3094
|
+
else if (member.kind === "name") {
|
|
3095
|
+
const lower2 = member.name.toLowerCase();
|
|
3096
|
+
if (lower2 === "string" || lower2 === "number" || lower2 === "boolean" || lower2 === "object") hints.add(lower2);
|
|
3097
|
+
else if (lower2 === "null" || lower2 === "undefined") hints.add("null");
|
|
3098
|
+
else if (lower2 === "array" || lower2 === "readonlyarray") hints.add("array");
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
return hints.size === 0 ? void 0 : [...hints].sort().join("|");
|
|
3102
|
+
}
|
|
3103
|
+
function jsDocShape(type, typedefs) {
|
|
3104
|
+
const elements = [];
|
|
3105
|
+
const fields = [];
|
|
3106
|
+
for (const member of flattenJsDocType(type, typedefs)) {
|
|
3107
|
+
if (member.kind === "array") elements.push(member.element);
|
|
3108
|
+
else if (member.kind === "object") {
|
|
3109
|
+
for (const field of member.fields) {
|
|
3110
|
+
if (!fields.some((f) => f.name === field.name)) fields.push(field);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
const element2 = elements.length === 0 ? null : elements.length === 1 ? elements[0] : { kind: "union", members: elements };
|
|
3115
|
+
return { element: element2, fields };
|
|
3116
|
+
}
|
|
3117
|
+
|
|
2818
3118
|
// src/service/stateAnalyzer.ts
|
|
2819
3119
|
var RESERVED_STREAMS_KEYS = /* @__PURE__ */ new Set(["$stream", "$streams"]);
|
|
2820
3120
|
var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
|
|
@@ -2832,6 +3132,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
2832
3132
|
const pendingListKeys = [];
|
|
2833
3133
|
const recursionSpec = specFromRecursionValue(topLevelProps.find((p) => p.name === RESERVED_RECURSION_KEY));
|
|
2834
3134
|
const effectiveDescriptors = effectiveTopLevelDescriptors(topLevelProps);
|
|
3135
|
+
const typedefs = scriptContent.includes("@typedef") ? collectJsDocTypedefs(scriptContent, maskCommentsAndStrings(scriptContent)) : /* @__PURE__ */ new Map();
|
|
2835
3136
|
for (const prop of topLevelProps) {
|
|
2836
3137
|
if (prop.name.startsWith("$")) {
|
|
2837
3138
|
collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys);
|
|
@@ -2851,11 +3152,11 @@ function analyzeStatePaths(scriptContent) {
|
|
|
2851
3152
|
}
|
|
2852
3153
|
continue;
|
|
2853
3154
|
}
|
|
2854
|
-
pushDataPropertyPaths(prop, paths);
|
|
3155
|
+
pushDataPropertyPaths(prop, paths, typedefs);
|
|
2855
3156
|
}
|
|
2856
3157
|
for (const streamValue of pendingStreamValues) {
|
|
2857
3158
|
if (paths.some((p) => p.path === streamValue.name && p.kind !== "eventToken")) continue;
|
|
2858
|
-
pushDataPropertyPaths(streamValue, paths);
|
|
3159
|
+
pushDataPropertyPaths(streamValue, paths, typedefs);
|
|
2859
3160
|
}
|
|
2860
3161
|
for (const listKeyEntry of pendingListKeys) {
|
|
2861
3162
|
pushListKeyPaths(listKeyEntry, paths);
|
|
@@ -2870,6 +3171,7 @@ function analyzeStatePaths(scriptContent) {
|
|
|
2870
3171
|
}
|
|
2871
3172
|
}
|
|
2872
3173
|
collectRowShapesFromAssignments(scriptContent, paths);
|
|
3174
|
+
collectGetterResultShapes(topLevelProps, effectiveDescriptors, paths);
|
|
2873
3175
|
return paths;
|
|
2874
3176
|
}
|
|
2875
3177
|
function specFromRecursionValue(prop) {
|
|
@@ -3253,6 +3555,284 @@ function pushRowFieldPaths(path, prop, paths, depth) {
|
|
|
3253
3555
|
}
|
|
3254
3556
|
}
|
|
3255
3557
|
}
|
|
3558
|
+
var ROW_PRESERVING_METHODS = /* @__PURE__ */ new Set([
|
|
3559
|
+
"filter",
|
|
3560
|
+
"toSorted",
|
|
3561
|
+
"toReversed",
|
|
3562
|
+
"slice",
|
|
3563
|
+
"toSpliced",
|
|
3564
|
+
"concat",
|
|
3565
|
+
"with",
|
|
3566
|
+
"sort",
|
|
3567
|
+
"reverse"
|
|
3568
|
+
]);
|
|
3569
|
+
var ROW_PICKING_METHODS = /* @__PURE__ */ new Set(["find", "findLast", "at"]);
|
|
3570
|
+
var MAX_GETTER_SHAPE_PASSES = 4;
|
|
3571
|
+
function collectGetterResultShapes(props, effective, paths) {
|
|
3572
|
+
const getters = /* @__PURE__ */ new Map();
|
|
3573
|
+
for (const prop of props) {
|
|
3574
|
+
if (prop.kind !== "getter" || prop.accessor !== "get" || prop.value === void 0) continue;
|
|
3575
|
+
if (prop.name.startsWith("$") || hasRecursionWildcard(prop.name) || effective.get(prop.name) === "data") continue;
|
|
3576
|
+
getters.set(prop.name, prop);
|
|
3577
|
+
}
|
|
3578
|
+
for (let pass = 0; pass < MAX_GETTER_SHAPE_PASSES; pass++) {
|
|
3579
|
+
const before = paths.length;
|
|
3580
|
+
for (const [name, prop] of getters) {
|
|
3581
|
+
const shape = getterResultShape(prop.value, paths);
|
|
3582
|
+
if (shape !== null) applyResultShape(name, shape, paths);
|
|
3583
|
+
}
|
|
3584
|
+
if (paths.length === before) break;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
function getterResultShape(body, paths) {
|
|
3588
|
+
const scan = maskCommentsAndStrings(body);
|
|
3589
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
3590
|
+
for (const m of scan.matchAll(/\b(?:const|let|var)\s+([$\w]+)\s*=(?![=>])/g)) {
|
|
3591
|
+
const name = m[1];
|
|
3592
|
+
if (aliases.has(name)) continue;
|
|
3593
|
+
const start = m.index + m[0].length;
|
|
3594
|
+
const shape = expressionShape(readExpression(body, scan, start), paths, aliases);
|
|
3595
|
+
if (shape !== null) aliases.set(name, shape);
|
|
3596
|
+
}
|
|
3597
|
+
const shapes = [];
|
|
3598
|
+
for (const m of scan.matchAll(/\breturn\b/g)) {
|
|
3599
|
+
const start = m.index + m[0].length;
|
|
3600
|
+
const shape = expressionShape(readExpression(body, scan, start), paths, aliases);
|
|
3601
|
+
if (shape !== null) shapes.push(shape);
|
|
3602
|
+
}
|
|
3603
|
+
return shapes.length === 0 ? null : mergeShapes(shapes);
|
|
3604
|
+
}
|
|
3605
|
+
function readExpression(text3, scan, start) {
|
|
3606
|
+
let depth = 0;
|
|
3607
|
+
let i = start;
|
|
3608
|
+
for (; i < scan.length; i++) {
|
|
3609
|
+
const ch = scan[i];
|
|
3610
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
3611
|
+
else if (ch === ")" || ch === "]" || ch === "}") {
|
|
3612
|
+
if (depth === 0) break;
|
|
3613
|
+
depth--;
|
|
3614
|
+
} else if (depth === 0 && (ch === ";" || ch === ",")) break;
|
|
3615
|
+
else if (depth === 0 && ch === "\n") {
|
|
3616
|
+
const before = scan.slice(start, i).trimEnd();
|
|
3617
|
+
const after = scan.slice(i + 1).trimStart();
|
|
3618
|
+
if (before === "") continue;
|
|
3619
|
+
if (!/[.?:|&+\-*/=(,]$/.test(before) && !/^[.?:|&]/.test(after)) break;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
return text3.slice(start, i).trim();
|
|
3623
|
+
}
|
|
3624
|
+
function expressionShape(text3, paths, aliases) {
|
|
3625
|
+
const expr = stripWrappingParens(text3.trim());
|
|
3626
|
+
if (expr === "") return null;
|
|
3627
|
+
const scan = maskCommentsAndStrings(expr);
|
|
3628
|
+
const ternary = splitTernary(scan);
|
|
3629
|
+
if (ternary !== null) {
|
|
3630
|
+
const branches = [expr.slice(ternary[0] + 1, ternary[1]), expr.slice(ternary[1] + 1)].map((part) => expressionShape(part, paths, aliases)).filter((s) => s !== null);
|
|
3631
|
+
return branches.length === 0 ? null : mergeShapes(branches);
|
|
3632
|
+
}
|
|
3633
|
+
const operands = splitTopLevel(scan, /\|\||\?\?/g);
|
|
3634
|
+
if (operands.length > 1) {
|
|
3635
|
+
const sides = operands.map(([s, e]) => expressionShape(expr.slice(s, e), paths, aliases)).filter((s) => s !== null);
|
|
3636
|
+
return sides.length === 0 ? null : mergeShapes(sides);
|
|
3637
|
+
}
|
|
3638
|
+
if (scan.startsWith("{")) {
|
|
3639
|
+
if (extractDelimitedContent(expr, scan, 0, "{", "}").length !== expr.length - 2) return null;
|
|
3640
|
+
return { isList: false, mirrors: [], fields: extractRowLiteralFields(expr).map((field) => ({ to: "", field })) };
|
|
3641
|
+
}
|
|
3642
|
+
let shape;
|
|
3643
|
+
let rest;
|
|
3644
|
+
if (scan.startsWith("[")) {
|
|
3645
|
+
const inner = extractDelimitedContent(expr, scan, 0, "[", "]");
|
|
3646
|
+
rest = inner.length + 2;
|
|
3647
|
+
const spread = /^\s*\.\.\.([\s\S]+)$/.exec(inner);
|
|
3648
|
+
if (spread === null || splitTopLevelElements(inner).length !== 1) return null;
|
|
3649
|
+
const source = expressionShape(spread[1], paths, aliases);
|
|
3650
|
+
if (source === null || !source.isList) return null;
|
|
3651
|
+
shape = source;
|
|
3652
|
+
} else {
|
|
3653
|
+
const base = readBase(expr, scan, aliases, paths);
|
|
3654
|
+
if (base === null) return null;
|
|
3655
|
+
({ shape, rest } = base);
|
|
3656
|
+
}
|
|
3657
|
+
return applyChain(expr, scan, rest, shape);
|
|
3658
|
+
}
|
|
3659
|
+
function readBase(expr, scan, aliases, paths) {
|
|
3660
|
+
const alias = /^([$\w]+)/.exec(scan);
|
|
3661
|
+
if (alias === null) return null;
|
|
3662
|
+
if (alias[1] !== "this") {
|
|
3663
|
+
const shape = aliases.get(alias[1]);
|
|
3664
|
+
return shape === void 0 ? null : { shape, rest: alias[1].length };
|
|
3665
|
+
}
|
|
3666
|
+
const segments = [];
|
|
3667
|
+
let i = 4;
|
|
3668
|
+
for (; ; ) {
|
|
3669
|
+
const dot = /^\s*\??\.\s*([$\w]+)/.exec(scan.slice(i));
|
|
3670
|
+
if (dot !== null) {
|
|
3671
|
+
if (/^\s*\(/.test(scan.slice(i + dot[0].length))) break;
|
|
3672
|
+
segments.push(dot[1]);
|
|
3673
|
+
i += dot[0].length;
|
|
3674
|
+
continue;
|
|
3675
|
+
}
|
|
3676
|
+
const bracket = /^\s*\[\s*(["'])/.exec(scan.slice(i));
|
|
3677
|
+
if (bracket !== null) {
|
|
3678
|
+
const open = i + bracket[0].length - 1;
|
|
3679
|
+
const close = scan.indexOf("]", open);
|
|
3680
|
+
const literal2 = /^(["'])([^"']*)\1\s*$/.exec(expr.slice(open, close));
|
|
3681
|
+
if (close === -1 || literal2 === null) return null;
|
|
3682
|
+
segments.push(literal2[2]);
|
|
3683
|
+
i = close + 1;
|
|
3684
|
+
continue;
|
|
3685
|
+
}
|
|
3686
|
+
break;
|
|
3687
|
+
}
|
|
3688
|
+
const path = segments.join(".");
|
|
3689
|
+
if (path === "" || path.startsWith("$")) return null;
|
|
3690
|
+
return {
|
|
3691
|
+
shape: { isList: hasPath(paths, `${path}.*`), mirrors: [{ from: path, to: "" }], fields: [] },
|
|
3692
|
+
rest: i
|
|
3693
|
+
};
|
|
3694
|
+
}
|
|
3695
|
+
function applyChain(expr, scan, start, initial) {
|
|
3696
|
+
let shape = initial;
|
|
3697
|
+
let i = start;
|
|
3698
|
+
while (scan.slice(i).trim() !== "") {
|
|
3699
|
+
const call = /^\s*\??\.\s*([$\w]+)\s*\(/.exec(scan.slice(i));
|
|
3700
|
+
if (call === null) return null;
|
|
3701
|
+
const open = i + call[0].length - 1;
|
|
3702
|
+
const args = extractDelimitedContent(expr, scan, open, "(", ")");
|
|
3703
|
+
i = open + args.length + 2;
|
|
3704
|
+
const method = call[1];
|
|
3705
|
+
if (ROW_PRESERVING_METHODS.has(method)) {
|
|
3706
|
+
if (!shape.isList) return null;
|
|
3707
|
+
} else if (ROW_PICKING_METHODS.has(method)) {
|
|
3708
|
+
if (!shape.isList) return null;
|
|
3709
|
+
shape = pickRow(shape);
|
|
3710
|
+
} else if (method === "map") {
|
|
3711
|
+
if (!shape.isList) return null;
|
|
3712
|
+
const mapped = mapRows(args, shape);
|
|
3713
|
+
if (mapped === null) return null;
|
|
3714
|
+
shape = mapped;
|
|
3715
|
+
} else {
|
|
3716
|
+
return null;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
return shape;
|
|
3720
|
+
}
|
|
3721
|
+
function pickRow(shape) {
|
|
3722
|
+
return {
|
|
3723
|
+
isList: false,
|
|
3724
|
+
mirrors: shape.mirrors.map((m) => m.to === "" ? { from: `${m.from}.*`, to: "" } : { from: m.from, to: "" }),
|
|
3725
|
+
fields: shape.fields.filter((f) => f.to === ".*").map((f) => ({ to: "", field: f.field }))
|
|
3726
|
+
};
|
|
3727
|
+
}
|
|
3728
|
+
function mapRows(args, shape) {
|
|
3729
|
+
const [callback] = splitTopLevelElements(args);
|
|
3730
|
+
if (callback === void 0) return null;
|
|
3731
|
+
const head = /^(?:async\s+)?(?:function\b[^(]*\(\s*([$\w]+)[^)]*\)|\(\s*([$\w]+)[^)]*\)|([$\w]+))\s*(=>)?\s*/.exec(callback);
|
|
3732
|
+
if (head === null) return null;
|
|
3733
|
+
const param = head[1] ?? head[2] ?? head[3];
|
|
3734
|
+
const body = callback.slice(head[0].length);
|
|
3735
|
+
let literal2 = null;
|
|
3736
|
+
const bodyScan = maskCommentsAndStrings(body);
|
|
3737
|
+
if (bodyScan.startsWith("(")) {
|
|
3738
|
+
const inner = stripWrappingParens(body).trim();
|
|
3739
|
+
if (inner.startsWith("{")) literal2 = inner;
|
|
3740
|
+
} else if (bodyScan.startsWith("{")) {
|
|
3741
|
+
const block = extractDelimitedContent(body, bodyScan, 0, "{", "}");
|
|
3742
|
+
const blockScan = maskCommentsAndStrings(block);
|
|
3743
|
+
const ret = /\breturn\s*\{/.exec(blockScan);
|
|
3744
|
+
if (ret !== null) {
|
|
3745
|
+
const open = ret.index + ret[0].length - 1;
|
|
3746
|
+
literal2 = `{${extractDelimitedContent(block, blockScan, open, "{", "}")}}`;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
if (literal2 === null) return null;
|
|
3750
|
+
const content = extractObjectContent(literal2);
|
|
3751
|
+
const spreadsRow = splitTopLevelElements(content).some((e) => e === `...${param}`);
|
|
3752
|
+
return {
|
|
3753
|
+
isList: true,
|
|
3754
|
+
mirrors: spreadsRow ? shape.mirrors.map((m) => m.to === "" ? { from: `${m.from}.*`, to: ".*" } : m) : [],
|
|
3755
|
+
fields: [
|
|
3756
|
+
...spreadsRow ? shape.fields.filter((f) => f.to === ".*") : [],
|
|
3757
|
+
...extractRowLiteralFields(literal2).map((field) => ({ to: ".*", field }))
|
|
3758
|
+
]
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
function applyResultShape(getter, shape, paths) {
|
|
3762
|
+
if (shape.isList) {
|
|
3763
|
+
if (!hasPath(paths, `${getter}.*`)) paths.push({ path: `${getter}.*`, kind: "list" });
|
|
3764
|
+
if (!hasPath(paths, `${getter}.length`)) paths.push({ path: `${getter}.length`, kind: "data", typeHint: "number" });
|
|
3765
|
+
}
|
|
3766
|
+
for (const { from, to } of shape.mirrors) {
|
|
3767
|
+
if (from === getter || from.startsWith(`${getter}.`)) continue;
|
|
3768
|
+
const sources = paths.filter((p) => p.path.startsWith(`${from}.`) && (p.kind === "data" || p.kind === "list"));
|
|
3769
|
+
for (const source of sources) {
|
|
3770
|
+
const target = `${getter}${to}${source.path.slice(from.length)}`;
|
|
3771
|
+
if (!hasPath(paths, target)) paths.push(withHint({ path: target, kind: source.kind }, source.typeHint));
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
for (const { to, field } of shape.fields) {
|
|
3775
|
+
pushRowFieldPaths(`${getter}${to}.${field.name}`, field, paths, 1);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
function mergeShapes(shapes) {
|
|
3779
|
+
return {
|
|
3780
|
+
isList: shapes.some((s) => s.isList),
|
|
3781
|
+
mirrors: shapes.flatMap((s) => s.mirrors),
|
|
3782
|
+
fields: shapes.flatMap((s) => s.fields)
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
function stripWrappingParens(text3) {
|
|
3786
|
+
let expr = text3.trim();
|
|
3787
|
+
for (; ; ) {
|
|
3788
|
+
if (!expr.startsWith("(")) return expr;
|
|
3789
|
+
const scan = maskCommentsAndStrings(expr);
|
|
3790
|
+
const inner = extractDelimitedContent(expr, scan, 0, "(", ")");
|
|
3791
|
+
if (inner.length !== expr.length - 2) return expr;
|
|
3792
|
+
expr = inner.trim();
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
function splitTernary(scan) {
|
|
3796
|
+
let depth = 0;
|
|
3797
|
+
let question = -1;
|
|
3798
|
+
let nested = 0;
|
|
3799
|
+
for (let i = 0; i < scan.length; i++) {
|
|
3800
|
+
const ch = scan[i];
|
|
3801
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
3802
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
3803
|
+
else if (depth !== 0) continue;
|
|
3804
|
+
else if (ch === "?") {
|
|
3805
|
+
if (scan[i + 1] === "." || scan[i + 1] === "?" || scan[i - 1] === "?") continue;
|
|
3806
|
+
if (question === -1) question = i;
|
|
3807
|
+
else nested++;
|
|
3808
|
+
} else if (ch === ":" && question !== -1) {
|
|
3809
|
+
if (nested > 0) nested--;
|
|
3810
|
+
else return [question, i];
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
return null;
|
|
3814
|
+
}
|
|
3815
|
+
function splitTopLevel(scan, separator) {
|
|
3816
|
+
const out = [];
|
|
3817
|
+
let depth = 0;
|
|
3818
|
+
let start = 0;
|
|
3819
|
+
for (let i = 0; i < scan.length; i++) {
|
|
3820
|
+
const ch = scan[i];
|
|
3821
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
3822
|
+
else if (ch === ")" || ch === "]" || ch === "}") depth--;
|
|
3823
|
+
else if (depth === 0) {
|
|
3824
|
+
separator.lastIndex = i;
|
|
3825
|
+
const m = separator.exec(scan);
|
|
3826
|
+
if (m !== null && m.index === i) {
|
|
3827
|
+
out.push([start, i]);
|
|
3828
|
+
start = i + m[0].length;
|
|
3829
|
+
i = start - 1;
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
out.push([start, scan.length]);
|
|
3834
|
+
return out;
|
|
3835
|
+
}
|
|
3256
3836
|
function hasPath(paths, path) {
|
|
3257
3837
|
return paths.some((p) => p.path === path);
|
|
3258
3838
|
}
|
|
@@ -3306,17 +3886,37 @@ function effectiveTopLevelDescriptors(props) {
|
|
|
3306
3886
|
}
|
|
3307
3887
|
return effective;
|
|
3308
3888
|
}
|
|
3309
|
-
function pushDataPropertyPaths(prop, paths) {
|
|
3310
|
-
pushDataPropertyPathsAt(prop.name, prop, paths, 0);
|
|
3889
|
+
function pushDataPropertyPaths(prop, paths, typedefs) {
|
|
3890
|
+
pushDataPropertyPathsAt(prop.name, prop, paths, 0, typedefs);
|
|
3311
3891
|
}
|
|
3312
|
-
function pushDataPropertyPathsAt(path, prop, paths, depth) {
|
|
3892
|
+
function pushDataPropertyPathsAt(path, prop, paths, depth, typedefs) {
|
|
3313
3893
|
paths.push({ path, kind: "data", typeHint: prop.typeHint, rawInitial: prop.value?.trim() });
|
|
3894
|
+
pushLiteralStructurePaths(path, prop, paths, depth, typedefs);
|
|
3895
|
+
if (prop.declaredType !== void 0) pushDeclaredShapePaths(path, prop.declaredType, paths, depth, typedefs);
|
|
3896
|
+
}
|
|
3897
|
+
function pushDeclaredShapePaths(path, type, paths, depth, typedefs) {
|
|
3898
|
+
const shape = jsDocShape(type, typedefs);
|
|
3899
|
+
if (shape.element !== null) {
|
|
3900
|
+
if (!hasPath(paths, `${path}.*`)) paths.push({ path: `${path}.*`, kind: "list" });
|
|
3901
|
+
if (!hasPath(paths, `${path}.length`)) paths.push({ path: `${path}.length`, kind: "data", typeHint: "number" });
|
|
3902
|
+
if (depth < MAX_OBJECT_NEST_DEPTH) pushDeclaredShapePaths(`${path}.*`, shape.element, paths, depth + 1, typedefs);
|
|
3903
|
+
}
|
|
3904
|
+
if (depth >= MAX_OBJECT_NEST_DEPTH) return;
|
|
3905
|
+
for (const field of shape.fields) {
|
|
3906
|
+
const fieldPath = `${path}.${field.name}`;
|
|
3907
|
+
if (!hasPath(paths, fieldPath)) {
|
|
3908
|
+
paths.push(withHint({ path: fieldPath, kind: "data" }, jsDocTypeHint(field.type, typedefs)));
|
|
3909
|
+
}
|
|
3910
|
+
pushDeclaredShapePaths(fieldPath, field.type, paths, depth + 1, typedefs);
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
function pushLiteralStructurePaths(path, prop, paths, depth, typedefs) {
|
|
3314
3914
|
if (prop.value && isArrayLiteral(prop.value)) {
|
|
3315
3915
|
paths.push({ path: `${path}.*`, kind: "list" });
|
|
3316
3916
|
paths.push({ path: `${path}.length`, kind: "data", typeHint: "number" });
|
|
3317
3917
|
if (depth >= MAX_OBJECT_NEST_DEPTH) return;
|
|
3318
3918
|
for (const childProp of extractArrayElementDataProperties(prop.value)) {
|
|
3319
|
-
pushDataPropertyPathsAt(`${path}.*.${childProp.name}`, childProp, paths, depth + 1);
|
|
3919
|
+
pushDataPropertyPathsAt(`${path}.*.${childProp.name}`, childProp, paths, depth + 1, typedefs);
|
|
3320
3920
|
}
|
|
3321
3921
|
return;
|
|
3322
3922
|
}
|
|
@@ -3325,7 +3925,7 @@ function pushDataPropertyPathsAt(path, prop, paths, depth) {
|
|
|
3325
3925
|
const childProps = parseTopLevelProperties(extractObjectContent(prop.value));
|
|
3326
3926
|
for (const childProp of childProps) {
|
|
3327
3927
|
if (childProp.kind !== "data") continue;
|
|
3328
|
-
pushDataPropertyPathsAt(`${path}.${childProp.name}`, childProp, paths, depth + 1);
|
|
3928
|
+
pushDataPropertyPathsAt(`${path}.${childProp.name}`, childProp, paths, depth + 1, typedefs);
|
|
3329
3929
|
}
|
|
3330
3930
|
}
|
|
3331
3931
|
}
|
|
@@ -3436,13 +4036,16 @@ function parseTopLevelProperties(objectContent, allowEmptyDataKeys = false) {
|
|
|
3436
4036
|
if (propName !== void 0) {
|
|
3437
4037
|
const valueStartIndex = match.index + match[0].length;
|
|
3438
4038
|
const value = extractFullValue(objectContent, scan, valueStartIndex);
|
|
3439
|
-
const
|
|
4039
|
+
const jsdocText = readJsDocTypeBefore(objectContent, match.index);
|
|
4040
|
+
const declaredType = jsdocText === void 0 ? null : parseJsDocType(jsdocText);
|
|
4041
|
+
const jsdocType = declaredType !== null ? jsDocTypeHint(declaredType) : jsdocText === void 0 ? void 0 : normalizeJsDocType(jsdocText);
|
|
3440
4042
|
const typeHint = jsdocType ?? inferTypeHint(value);
|
|
3441
4043
|
props.push({
|
|
3442
4044
|
name: propName,
|
|
3443
4045
|
kind: "data",
|
|
3444
4046
|
value,
|
|
3445
4047
|
typeHint,
|
|
4048
|
+
...declaredType !== null ? { declaredType } : {},
|
|
3446
4049
|
nameStart: nameSpan[0],
|
|
3447
4050
|
nameEnd: nameSpan[1],
|
|
3448
4051
|
valueStart: valueStartIndex
|
|
@@ -3563,13 +4166,6 @@ function extractArrayElementDataProperties(value) {
|
|
|
3563
4166
|
const objectContent = extractBracedContent(trimmed, scan, first);
|
|
3564
4167
|
return parseTopLevelProperties(objectContent).filter((prop) => prop.kind === "data");
|
|
3565
4168
|
}
|
|
3566
|
-
function extractJsDocType(content, propIndex) {
|
|
3567
|
-
const before = content.slice(Math.max(0, propIndex - 200), propIndex);
|
|
3568
|
-
const jsdocMatch = before.match(/\/\*\*\s*@type\s*\{([^}]+)\}\s*\*\/\s*$/);
|
|
3569
|
-
if (!jsdocMatch) return void 0;
|
|
3570
|
-
const typeExpr = jsdocMatch[1].trim();
|
|
3571
|
-
return normalizeJsDocType(typeExpr);
|
|
3572
|
-
}
|
|
3573
4169
|
function normalizeJsDocType(typeExpr) {
|
|
3574
4170
|
const parts = typeExpr.split("|").map((p) => p.trim());
|
|
3575
4171
|
const normalized = parts.map((p) => {
|
|
@@ -10310,6 +10906,10 @@ function resolveLocale(locale2) {
|
|
|
10310
10906
|
if (locale2 === void 0 || locale2 === "" || /^ja\b|^ja[-_]/i.test(locale2) || locale2.toLowerCase() === "ja") return "ja";
|
|
10311
10907
|
return "en";
|
|
10312
10908
|
}
|
|
10909
|
+
function shortSeed(raw) {
|
|
10910
|
+
const flat = raw.replace(/\s+/g, " ");
|
|
10911
|
+
return flat.length <= 32 ? flat : `${flat.slice(0, 29)}\u2026`;
|
|
10912
|
+
}
|
|
10313
10913
|
var JA_EXPECTED_LABEL = {
|
|
10314
10914
|
array: "\u914D\u5217\u578B\u306E\u30D1\u30B9",
|
|
10315
10915
|
boolean: "\u30D6\u30FC\u30EA\u30A2\u30F3\u578B",
|
|
@@ -10389,7 +10989,9 @@ var ja = {
|
|
|
10389
10989
|
didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
|
|
10390
10990
|
none: () => `\u306A\u3057`,
|
|
10391
10991
|
triggerSeededTruthy: (path) => `trigger \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C true \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002trigger \u306F\u30A8\u30C3\u30B8\u691C\u51FA\u306A\u3057\uFF08truthy \u66F8\u304D\u8FBC\u307F\u3067\u5373\u767A\u706B\u30FBmanual \u3082\u30D0\u30A4\u30D1\u30B9\uFF09\u306E\u305F\u3081\u3001\u30D0\u30A4\u30F3\u30C9\u6642\u306B\u5373\u767A\u706B\u3057\u307E\u3059\u3002false \u3067\u30B7\u30FC\u30C9\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
10392
|
-
storageSeedClobber: (path, raw) => `<wcs-storage> \u306E value \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C ${raw} \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u521D\u671F\u66F8\u304D\u623B\u3057\u304C\u4FDD\u5B58\u5024\u3092\u4E0A\u66F8\u304D\u3057\u307E\u3059 \u2014
|
|
10992
|
+
storageSeedClobber: (path, raw) => `<wcs-storage> \u306E value \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C ${raw} \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u8AAD\u307F\u8FBC\u307F\u306E\u305F\u3073\u306B\u521D\u671F\u66F8\u304D\u623B\u3057\u304C\u4FDD\u5B58\u5024\u3092\u4E0A\u66F8\u304D\u3057\u307E\u3059 \u2014 \u30D0\u30A4\u30F3\u30C9\u306B #init=element \u3092\u4ED8\u3051\u3066\uFF08\`value#init=element: ${path}\`\uFF09\u4FDD\u5B58\u5024\u3092\u52DD\u305F\u305B\u3001\u30B9\u30ED\u30C3\u30C8\u306F null\uFF08\u7A7A\u306E\u30AD\u30FC\u304C\u8AAD\u3080\u5024\uFF09\u3067\u30B7\u30FC\u30C9\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
10993
|
+
storageInitSeed: (path, raw) => `<wcs-storage> \u306E value \u30D0\u30A4\u30F3\u30C9\u5148 "${path}" \u304C ${shortSeed(raw)} \u3067\u30B7\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u3059\u304C\u3001#init=element \u306F\u8981\u7D20\u306E\u5024\u3092\u3053\u306E\u30B9\u30ED\u30C3\u30C8\u3078\u5F15\u304D\u8FBC\u307F\u307E\u3059\u3002\u521D\u56DE\uFF08\u7A7A\u306E\u30AD\u30FC\uFF09\u306F null \u306A\u306E\u3067\u3001\u30B7\u30FC\u30C9\u306F null \u306B\u7F6E\u304D\u63DB\u308F\u308A\u307E\u3059 \u2014 null \u3067\u30B7\u30FC\u30C9\u3057\u3066\u8AAD\u3080\u5074\u3067\u65E2\u5B9A\u5024\u3092\u88DC\u3046\uFF08\`this.${path} ?? \u2026\`\uFF09\u304B\u3001null \u3092\u7121\u8996\u3059\u308B setter \u3092\u6301\u3064\u30A2\u30AF\u30BB\u30B5\u30DA\u30A2\u3092\u675F\u306D\u3066\u304F\u3060\u3055\u3044`,
|
|
10994
|
+
inputNestedWrite: (tag, input, path, example) => `<${tag}> \u306E\u5165\u529B "${input}" \u306B "${path}" \u3092\u675F\u306D\u3066\u3044\u307E\u3059\u304C\u3001\u30DA\u30FC\u30B8\u306F\u305D\u306E\u4E0B\uFF08"${example}"\uFF09\u3078\u66F8\u304D\u8FBC\u3093\u3067\u3044\u307E\u3059\u3002\u675F\u7E1B\u306F "${path}" \u81EA\u8EAB\u3078\u306E\u4EE3\u5165\u3067\u3057\u304B\u5F53\u3066\u76F4\u3055\u308C\u306A\u3044\u306E\u3067\u3001\u4E0B\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306F <${tag}> \u306B\u5C4A\u304D\u307E\u305B\u3093 \u2014 \u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u30D1\u30B9\u3067\u8AAD\u3093\u3067\u65B0\u3057\u3044\u5024\u3092\u8FD4\u3059 getter\uFF08\`this["${example}"]\`\u30FB\`$getAll\`\uFF09\u3092\u675F\u306D\u308B\u304B\u3001\u4E0B\u3078\u66F8\u304B\u305A\u306B "${path}" \u3092\u5DEE\u3057\u66FF\u3048\u3066\u304F\u3060\u3055\u3044`,
|
|
10393
10995
|
devtoolsAfterState: () => `@wcstack/devtools/auto \u306F @wcstack/state/auto \u3088\u308A\u5148\u306B\u8AAD\u307F\u8FBC\u3093\u3067\u304F\u3060\u3055\u3044\uFF08\u5F8C\u3060\u3068\u914D\u7DDA\u53F0\u5E33\u304C\u30E9\u30A4\u30D6\u3067 captured \u3055\u308C\u307E\u305B\u3093\uFF09`,
|
|
10394
10996
|
baseHrefMissing: () => `@wcstack/router \u3092\u4F7F\u3046 SPA \u306B\u306F <head> \u5185\u306E <base href="/"> \u304C\u5FC5\u8981\u3067\u3059\uFF08\u7121\u3044\u3068\u30C7\u30A3\u30FC\u30D7\u30EA\u30F3\u30AF\u3067 basename \u304C\u8AA4\u5C0E\u51FA\u3055\u308C\u307E\u3059\uFF09`,
|
|
10395
10997
|
signalsDualEntry: () => `@wcstack/signals \u3068 @wcstack/signals/dom \u304C\u540C\u4E00\u30DA\u30FC\u30B8\u304B\u3089 import \u3055\u308C\u3066\u3044\u307E\u3059\u3002CDN \u3067\u306F\u5404\u30A8\u30F3\u30C8\u30EA\u304C\u81EA\u5DF1\u5B8C\u7D50\u30D0\u30F3\u30C9\u30EB\u306E\u305F\u3081\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u30B3\u30A2\u304C\u4E8C\u91CD\u5316\u3057\u3001\u5883\u754C\u3067\u53CD\u5FDC\u304C\u58CA\u308C\u307E\u3059 \u2014 \u3059\u3079\u3066 /dom \u30A8\u30F3\u30C8\u30EA\u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
@@ -10575,7 +11177,9 @@ var en = {
|
|
|
10575
11177
|
didYouMean: (c) => `. Did you mean "${c}"?`,
|
|
10576
11178
|
none: () => `none`,
|
|
10577
11179
|
triggerSeededTruthy: (path) => `The trigger-bound slot "${path}" is seeded with true. trigger has no edge detection (any truthy write fires, and it bypasses manual), so it fires immediately at bind. Seed it with false`,
|
|
10578
|
-
storageSeedClobber: (path, raw) => `The <wcs-storage> value-bound slot "${path}" is seeded with ${raw}. The initial write-back overwrites the persisted value \u2014
|
|
11180
|
+
storageSeedClobber: (path, raw) => `The <wcs-storage> value-bound slot "${path}" is seeded with ${raw}. The initial write-back overwrites the persisted value on every load \u2014 add #init=element to the binding (\`value#init=element: ${path}\`) so the stored value wins, and seed the slot with null (what an empty key loads as)`,
|
|
11181
|
+
storageInitSeed: (path, raw) => `The <wcs-storage> value-bound slot "${path}" is seeded with ${shortSeed(raw)}, but #init=element pulls the element's value into it. On a first visit (an empty key) that is null, so the seed is replaced by null \u2014 seed it with null and supply the default where you read it (\`this.${path} ?? \u2026\`), or bind an accessor pair whose setter ignores null`,
|
|
11182
|
+
inputNestedWrite: (tag, input, path, example) => `"${path}" is bound to the input "${input}" of <${tag}>, but the page writes below it ("${example}"). A binding is re-applied only when "${path}" itself is assigned, so those writes never reach <${tag}> \u2014 bind a getter that reads the fields by path (\`this["${example}"]\`, \`$getAll\`) and returns a new value, or replace "${path}" instead of writing below it`,
|
|
10579
11183
|
devtoolsAfterState: () => `Load @wcstack/devtools/auto BEFORE @wcstack/state/auto (otherwise the wiring ledger is not captured live)`,
|
|
10580
11184
|
baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
|
|
10581
11185
|
signalsDualEntry: () => `Both @wcstack/signals and @wcstack/signals/dom are imported on this page. On a CDN each entry is a self-contained bundle, so the reactive core is duplicated and reactivity breaks at the seam \u2014 import everything from the single /dom entry`,
|
|
@@ -11290,7 +11894,7 @@ function validateFilterUsage(filter, bindingStart, msgs) {
|
|
|
11290
11894
|
start: bindingStart + filter.offset,
|
|
11291
11895
|
end: bindingStart + filter.offset + filter.name.length,
|
|
11292
11896
|
message: removedFilterMessage(filter.name, filter.args, msgs) ?? msgs.filterUnknown(filter.name),
|
|
11293
|
-
severity: "
|
|
11897
|
+
severity: "error"
|
|
11294
11898
|
});
|
|
11295
11899
|
return diagnostics;
|
|
11296
11900
|
}
|
|
@@ -11520,18 +12124,24 @@ function validateStateTypes(html, stateTagName = "wcs-state", locale2) {
|
|
|
11520
12124
|
}
|
|
11521
12125
|
return diagnostics;
|
|
11522
12126
|
}
|
|
12127
|
+
var PROPERTY_AFTER_COMMENT = /^\s*(?:"([^"]+)"|'([^']+)'|(\w+))\s*:\s*/;
|
|
11523
12128
|
function findJsDocTypedProperties(script) {
|
|
11524
12129
|
const results = [];
|
|
11525
|
-
const
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
const
|
|
11529
|
-
|
|
11530
|
-
const
|
|
12130
|
+
const scan = maskCommentsAndStrings(script);
|
|
12131
|
+
const typedefs = script.includes("@typedef") ? collectJsDocTypedefs(script, scan) : /* @__PURE__ */ new Map();
|
|
12132
|
+
for (const comment of findJsDocComments(script, scan)) {
|
|
12133
|
+
const typeText = readJsDocTagType(comment.text, "type");
|
|
12134
|
+
if (typeText === null) continue;
|
|
12135
|
+
const match = PROPERTY_AFTER_COMMENT.exec(script.slice(comment.end));
|
|
12136
|
+
if (!match) continue;
|
|
12137
|
+
const rawType = typeText.replace(/\s+/g, " ");
|
|
12138
|
+
const name = match[1] ?? match[2] ?? match[3];
|
|
12139
|
+
const valueStart = comment.end + match[0].length;
|
|
11531
12140
|
const valueText = extractValue(script, valueStart);
|
|
11532
12141
|
const valueType = inferValueType(valueText);
|
|
11533
12142
|
if (valueType) {
|
|
11534
|
-
const declaredTypes = rawType
|
|
12143
|
+
const declaredTypes = declaredTypesOf(rawType, typedefs);
|
|
12144
|
+
if (declaredTypes === null) continue;
|
|
11535
12145
|
results.push({
|
|
11536
12146
|
name,
|
|
11537
12147
|
rawType,
|
|
@@ -11544,6 +12154,20 @@ function findJsDocTypedProperties(script) {
|
|
|
11544
12154
|
}
|
|
11545
12155
|
return results;
|
|
11546
12156
|
}
|
|
12157
|
+
var ANY_TYPES = /* @__PURE__ */ new Set(["any", "unknown", "*"]);
|
|
12158
|
+
function declaredTypesOf(rawType, typedefs) {
|
|
12159
|
+
if (rawType.split("|").some((t) => ANY_TYPES.has(t.trim().toLowerCase()))) return null;
|
|
12160
|
+
const parsed = parseJsDocType(rawType);
|
|
12161
|
+
if (parsed === null) return rawType.split("|").map((t) => normalizeType(t.trim()));
|
|
12162
|
+
const out = [];
|
|
12163
|
+
for (const member of flattenJsDocType(parsed, typedefs)) {
|
|
12164
|
+
if (member.kind === "unknown") return null;
|
|
12165
|
+
if (member.kind === "array") out.push("array");
|
|
12166
|
+
else if (member.kind === "object") out.push("object");
|
|
12167
|
+
else if (member.kind === "name") out.push(normalizeType(member.name));
|
|
12168
|
+
}
|
|
12169
|
+
return out;
|
|
12170
|
+
}
|
|
11547
12171
|
function extractValue(script, start) {
|
|
11548
12172
|
let depth = 0;
|
|
11549
12173
|
let inString = null;
|
|
@@ -11978,7 +12602,7 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
11978
12602
|
start: item.exprStart + filterOffset,
|
|
11979
12603
|
end: item.exprStart + filterOffset + filterName.length,
|
|
11980
12604
|
message: removedFilterMessage(filterName, filterArgsOf(segment), msgs) ?? msgs.filterUnknown(filterName),
|
|
11981
|
-
severity: "
|
|
12605
|
+
severity: "error"
|
|
11982
12606
|
});
|
|
11983
12607
|
}
|
|
11984
12608
|
}
|
|
@@ -13577,6 +14201,24 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
|
|
|
13577
14201
|
});
|
|
13578
14202
|
}
|
|
13579
14203
|
}
|
|
14204
|
+
if (tagName === "wcs-storage" && property === "value" && parsed.path && /(?:^|,)init=element\b/.test(modifiers)) {
|
|
14205
|
+
const cand = findDataSlot(getPaths(), parsed.path);
|
|
14206
|
+
if (cand?.rawInitial !== void 0 && isNonNullLiteralSeed(cand.rawInitial)) {
|
|
14207
|
+
diagnostics.push({
|
|
14208
|
+
code: WcsDiagnosticCode.StorageInitSeed,
|
|
14209
|
+
start,
|
|
14210
|
+
end,
|
|
14211
|
+
severity: "warning",
|
|
14212
|
+
tag: tagName,
|
|
14213
|
+
statePath: parsed.path,
|
|
14214
|
+
message: msgs.storageInitSeed(parsed.path, cand.rawInitial)
|
|
14215
|
+
});
|
|
14216
|
+
}
|
|
14217
|
+
}
|
|
14218
|
+
}
|
|
14219
|
+
function isNonNullLiteralSeed(raw) {
|
|
14220
|
+
const v = raw.trim();
|
|
14221
|
+
return /^(?:["'`[{]|-?\d)/.test(v) || v === "true" || v === "false";
|
|
13580
14222
|
}
|
|
13581
14223
|
function findDataSlot(paths, path) {
|
|
13582
14224
|
return paths.find((c) => c.kind === "data" && c.path === path);
|
|
@@ -15220,15 +15862,63 @@ function validateGetterUntrackedReads(script, scriptStart, nestedWriteRoots, loc
|
|
|
15220
15862
|
}
|
|
15221
15863
|
return out;
|
|
15222
15864
|
}
|
|
15865
|
+
var NESTED_WRITE_INPUTS = {
|
|
15866
|
+
"wcs-storage": { inputs: ["value"], exemptWhenManual: true }
|
|
15867
|
+
};
|
|
15868
|
+
var MANUAL_ATTRIBUTE = /(?:^|\s)manual(?:\s|=|$)/i;
|
|
15869
|
+
function validateInputNestedWrites(html, stateTagName, bindAttrName, nestedWriteRoots, locale2) {
|
|
15870
|
+
const tags = findBuiltinTagOccurrences(html).filter((occ) => {
|
|
15871
|
+
const target = NESTED_WRITE_INPUTS[occ.tagName];
|
|
15872
|
+
return target !== void 0 && !(target.exemptWhenManual && MANUAL_ATTRIBUTE.test(occ.attrsText));
|
|
15873
|
+
}).map((occ) => ({ tagName: occ.tagName, start: occ.tagStart, end: occ.attrsStart + occ.attrsText.length }));
|
|
15874
|
+
if (tags.length === 0) return [];
|
|
15875
|
+
const msgs = getMessages(locale2);
|
|
15876
|
+
const out = [];
|
|
15877
|
+
const index = buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName });
|
|
15878
|
+
for (const occ of index.occurrences) {
|
|
15879
|
+
if (occ.kind !== "path" || occ.source !== "attribute" || occ.bindingType !== "prop" || occ.propName === null) continue;
|
|
15880
|
+
if (occ.path.startsWith(".")) continue;
|
|
15881
|
+
const at2 = occ.exprRange.start;
|
|
15882
|
+
const tag = tags.find((t) => t.start <= at2 && at2 <= t.end);
|
|
15883
|
+
if (tag === void 0) continue;
|
|
15884
|
+
const input = occ.propName.startsWith(".") ? occ.propName.slice(1) : occ.propName;
|
|
15885
|
+
if (!NESTED_WRITE_INPUTS[tag.tagName].inputs.includes(input)) continue;
|
|
15886
|
+
const example = nestedWriteRoots().get(occ.path);
|
|
15887
|
+
if (example === void 0) continue;
|
|
15888
|
+
out.push({
|
|
15889
|
+
code: WcsDiagnosticCode.InputNestedWrite,
|
|
15890
|
+
start: occ.pathRange.start,
|
|
15891
|
+
end: occ.pathRange.end,
|
|
15892
|
+
severity: "warning",
|
|
15893
|
+
tag: tag.tagName,
|
|
15894
|
+
statePath: occ.path,
|
|
15895
|
+
message: msgs.inputNestedWrite(tag.tagName, input, occ.path, example)
|
|
15896
|
+
});
|
|
15897
|
+
}
|
|
15898
|
+
return out;
|
|
15899
|
+
}
|
|
15223
15900
|
var TWO_WAY_PROPS = /* @__PURE__ */ new Set(["value", "checked"]);
|
|
15224
15901
|
var BRACKET_WRITE = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
15225
15902
|
var PRE_BRACKET_INCDEC2 = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
15903
|
+
var TEMPLATE_BRACKET = String.raw`\bthis\s*(?:\?\.)?\s*\[\s*` + "`([^`]*)`" + String.raw`\s*\]`;
|
|
15904
|
+
var TEMPLATE_BRACKET_WRITE = new RegExp(`${TEMPLATE_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
15905
|
+
var PRE_TEMPLATE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${TEMPLATE_BRACKET}`, "g");
|
|
15906
|
+
var TEMPLATE_KEY_PATH = /^[A-Za-z_][\w]*(?:\.(?:\*|[\w]+))*$/;
|
|
15907
|
+
function templateKeyPath(raw) {
|
|
15908
|
+
if (raw.includes("`")) return null;
|
|
15909
|
+
const path = raw.replace(/\$\{[^{}]*\}/g, "*");
|
|
15910
|
+
return TEMPLATE_KEY_PATH.test(path) ? path : null;
|
|
15911
|
+
}
|
|
15226
15912
|
function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
15227
|
-
const roots = /* @__PURE__ */ new
|
|
15913
|
+
const roots = /* @__PURE__ */ new Map();
|
|
15228
15914
|
const addPrefixes = (path, inclusive) => {
|
|
15229
15915
|
const segments = path.split(".");
|
|
15230
15916
|
const last = inclusive ? segments.length : segments.length - 1;
|
|
15231
|
-
|
|
15917
|
+
const example = inclusive ? `${path}.*` : path;
|
|
15918
|
+
for (let i = 1; i <= last; i++) {
|
|
15919
|
+
const prefix = segments.slice(0, i).join(".");
|
|
15920
|
+
if (!roots.has(prefix)) roots.set(prefix, example);
|
|
15921
|
+
}
|
|
15232
15922
|
};
|
|
15233
15923
|
const tags = findBuiltinTagOccurrences(html).map((occ) => ({
|
|
15234
15924
|
contract: BUILTIN_TAGS[occ.tagName],
|
|
@@ -15237,23 +15927,29 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
15237
15927
|
}));
|
|
15238
15928
|
const index = buildReferenceIndex(html, { bindAttribute: bindAttrName, stateTagName });
|
|
15239
15929
|
for (const occ of index.occurrences) {
|
|
15240
|
-
if (occ.kind !== "path" || occ.
|
|
15930
|
+
if (occ.kind !== "path" || occ.source !== "attribute") continue;
|
|
15931
|
+
let path = occ.path;
|
|
15932
|
+
if (path.startsWith(".")) {
|
|
15933
|
+
const list = getRowShorthandForPath(html, occ.pathRange.start, bindAttrName) === null ? null : getResolvedForListPath(html, occ.pathRange.start, bindAttrName);
|
|
15934
|
+
if (list === null) continue;
|
|
15935
|
+
path = path === "." ? `${list}.*` : `${list}.*.${path.slice(1)}`;
|
|
15936
|
+
}
|
|
15241
15937
|
if (occ.bindingType === "spread") {
|
|
15242
|
-
addPrefixes(
|
|
15938
|
+
addPrefixes(path, true);
|
|
15243
15939
|
continue;
|
|
15244
15940
|
}
|
|
15245
15941
|
if (occ.bindingType === "radio" || occ.bindingType === "checkbox") {
|
|
15246
|
-
addPrefixes(
|
|
15942
|
+
addPrefixes(path, false);
|
|
15247
15943
|
continue;
|
|
15248
15944
|
}
|
|
15249
15945
|
if (occ.bindingType !== "prop" || occ.propName === null) continue;
|
|
15250
15946
|
if (TWO_WAY_PROPS.has(occ.propName)) {
|
|
15251
|
-
addPrefixes(
|
|
15947
|
+
addPrefixes(path, false);
|
|
15252
15948
|
continue;
|
|
15253
15949
|
}
|
|
15254
15950
|
const at2 = occ.exprRange.start;
|
|
15255
15951
|
const tag = tags.find((t) => t.start <= at2 && at2 <= t.end);
|
|
15256
|
-
if (tag?.contract?.hasWcBindable && tag.contract.properties.includes(occ.propName)) addPrefixes(
|
|
15952
|
+
if (tag?.contract?.hasWcBindable && tag.contract.properties.includes(occ.propName)) addPrefixes(path, false);
|
|
15257
15953
|
}
|
|
15258
15954
|
for (const block of blocks) {
|
|
15259
15955
|
if (block.mountPath !== null) addPrefixes(block.mountPath, true);
|
|
@@ -15266,6 +15962,15 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
15266
15962
|
addPrefixes(block.content.slice(keyStart, keyStart + match[1].length), false);
|
|
15267
15963
|
}
|
|
15268
15964
|
}
|
|
15965
|
+
for (const regex of [TEMPLATE_BRACKET_WRITE, PRE_TEMPLATE_BRACKET_INCDEC]) {
|
|
15966
|
+
regex.lastIndex = 0;
|
|
15967
|
+
let match;
|
|
15968
|
+
while ((match = regex.exec(scan)) !== null) {
|
|
15969
|
+
const keyStart = match.index + match[0].indexOf("`") + 1;
|
|
15970
|
+
const path = templateKeyPath(block.content.slice(keyStart, keyStart + match[1].length));
|
|
15971
|
+
if (path !== null) addPrefixes(path, false);
|
|
15972
|
+
}
|
|
15973
|
+
}
|
|
15269
15974
|
API_CALL.lastIndex = 0;
|
|
15270
15975
|
let call;
|
|
15271
15976
|
while ((call = API_CALL.exec(scan)) !== null) {
|
|
@@ -15381,6 +16086,7 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
|
|
|
15381
16086
|
out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
|
|
15382
16087
|
out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
|
|
15383
16088
|
}
|
|
16089
|
+
out.push(...validateInputNestedWrites(html, stateTagName, bindAttrName, getNestedWriteRoots, locale2));
|
|
15384
16090
|
out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2));
|
|
15385
16091
|
return out;
|
|
15386
16092
|
}
|
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": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
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",
|