@wcstack/typescript 4.0.3 → 4.1.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 +106 -32
- 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
|
@@ -94,6 +94,7 @@ var WcsDiagnosticCode = {
|
|
|
94
94
|
// 静的にしか検出できない。対象はオブジェクトリテラル初期値のルートだけ(配列は `items`
|
|
95
95
|
// の依存で足りる — 行の置換は `items` 自体を書き換え、in-place 変異は array-mutation が止める)。
|
|
96
96
|
GetterUntrackedRead: "wcs/getter-untracked-read",
|
|
97
|
+
OnHandlerThis: "wcs/on-handler-this",
|
|
97
98
|
// --- <wcs-state> script: $watch declaration ---
|
|
98
99
|
// ランタイム(watch/processWatchDeclaration.ts)が raiseError で落とす宣言。
|
|
99
100
|
// 越境 `@` / `$` 始まり / 空キー・空セグメント / 明らかな非関数ハンドラ。
|
|
@@ -216,6 +217,10 @@ var WcsDiagnosticCode = {
|
|
|
216
217
|
ScriptOrder: "wcs/script-order",
|
|
217
218
|
// router/auto があるのに <base href> がない(SPA の basename 誤導出)。
|
|
218
219
|
BaseHrefMissing: "wcs/base-href-missing",
|
|
220
|
+
// <wcs-router> の直後に無い <wcs-outlet>(どの router にも使われない)と、直後に outlet を持たない
|
|
221
|
+
// router。router は自前の outlet を直後に作り、ページはそこに描かれ、置いた outlet は空のまま。
|
|
222
|
+
// ランタイムも同じ code で警告する(docs/router-outlet-placement.md)。
|
|
223
|
+
RouterOutletMisplaced: "wcs/router-outlet-misplaced",
|
|
219
224
|
// @wcstack/signals と /dom エントリの同一ページ混在(リアクティブコア二重化)。
|
|
220
225
|
SignalsDualEntry: "wcs/signals-dual-entry",
|
|
221
226
|
// --- deprecations ---
|
|
@@ -3235,9 +3240,15 @@ function analyzeWatchEntries(scriptContent) {
|
|
|
3235
3240
|
}));
|
|
3236
3241
|
}
|
|
3237
3242
|
function analyzeWatchHandlerSources(scriptContent) {
|
|
3243
|
+
return analyzeHandlerSources(scriptContent, RESERVED_WATCH_KEY);
|
|
3244
|
+
}
|
|
3245
|
+
function analyzeOnHandlerSources(scriptContent) {
|
|
3246
|
+
return analyzeHandlerSources(scriptContent, "$on");
|
|
3247
|
+
}
|
|
3248
|
+
function analyzeHandlerSources(scriptContent, key) {
|
|
3238
3249
|
const root = locateDefaultExportObject(scriptContent);
|
|
3239
3250
|
if (!root) return [];
|
|
3240
|
-
const prop = parseTopLevelProperties(root.content).find((p) => p.name ===
|
|
3251
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
|
|
3241
3252
|
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
|
|
3242
3253
|
return [];
|
|
3243
3254
|
}
|
|
@@ -3247,6 +3258,7 @@ function analyzeWatchHandlerSources(scriptContent) {
|
|
|
3247
3258
|
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
|
|
3248
3259
|
if (entry.value === void 0 || entry.valueStart === void 0) continue;
|
|
3249
3260
|
out.push({
|
|
3261
|
+
name: entry.name,
|
|
3250
3262
|
kind: entry.kind === "data" ? "value" : "body",
|
|
3251
3263
|
text: entry.value,
|
|
3252
3264
|
start: innerStart + entry.valueStart
|
|
@@ -10941,6 +10953,7 @@ var ja = {
|
|
|
10941
10953
|
delegatedCurrentTarget: (suggested, eventName, handler) => `"${eventName}" \u30A4\u30D9\u30F3\u30C8\u306E\u675F\u7E1B\u306F root \u3078\u59D4\u8B72\u3055\u308C\u308B\u306E\u3067\u3001"${handler}" \u306E\u4E2D\u306E event.currentTarget \u306F\u3053\u306E\u8981\u7D20\u3067\u306F\u306A\u304F root \u306B\u306A\u308A\u307E\u3059\u3002\u8981\u7D20\u306B\u30EA\u30B9\u30CA\u30FC\u3092\u4ED8\u3051\u308B\u306B\u306F "${suggested}:" \u3068\u66F8\u304F\u304B\u3001event.target.closest(...) \u3067\u8981\u7D20\u3092\u63A2\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
10942
10954
|
getterCycle: (cycle) => `\u30D1\u30B9 getter \u304C\u5FAA\u74B0\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${cycle}`,
|
|
10943
10955
|
updatedCallbackUnbound: (p) => `$renderedCallback \u306F binding \u99C6\u52D5\u3067\u3059\u3002"${p}" \u306F\u3053\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u3069\u306E\u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0\u306B\u3082\u73FE\u308C\u306A\u3044\u305F\u3081\u3001\u3053\u306E\u5206\u5C90\u306F\u4E00\u5EA6\u3082\u5B9F\u884C\u3055\u308C\u307E\u305B\u3093\u3002\u63CF\u753B\u306B\u4F9D\u5B58\u305B\u305A\u53CD\u5FDC\u3059\u308B\u306A\u3089 $watch \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044`,
|
|
10956
|
+
onHandlerThis: (name) => `$on \u306E\u30CF\u30F3\u30C9\u30E9 "${name}" \u304C this \u3092\u4F7F\u3063\u3066\u3044\u307E\u3059\u3002$on \u306E\u30CF\u30F3\u30C9\u30E9\u306F state \u3092\u7B2C 1 \u5F15\u6570\u3067\u53D7\u3051\u53D6\u308A\uFF08\`${name}(state, event) { state.x = \u2026 }\`\uFF09\u3001this \u306F undefined \u306A\u306E\u3067\u3001this.\u2026 \u306F\u547C\u3070\u308C\u305F\u3068\u304D\u306B\u4F8B\u5916\u306B\u306A\u308A\u307E\u3059\u3002$watch \u3084\u30E1\u30BD\u30C3\u30C9\u3068\u9055\u3046\u70B9\u306B\u6CE8\u610F`,
|
|
10944
10957
|
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`,
|
|
10945
10958
|
handlerFilterNotAllowed: (prop) => `\u30A4\u30D9\u30F3\u30C8\u30CF\u30F3\u30C9\u30E9 "${prop}" \u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
|
|
10946
10959
|
typeExpectation: (label, expected, resultType) => `"${label}" \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728\u306E\u578B: ${resultType}\uFF09`,
|
|
@@ -10991,13 +11004,14 @@ var ja = {
|
|
|
10991
11004
|
didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
|
|
10992
11005
|
none: () => `\u306A\u3057`,
|
|
10993
11006
|
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`,
|
|
10994
|
-
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`,
|
|
11007
|
+
storageSeedClobber: (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\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`,
|
|
10995
11008
|
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`,
|
|
10996
11009
|
storagePairClobber: (path) => `<wcs-storage> \u306E value \u3092 #init=element \u306A\u3057\u3067\u30A2\u30AF\u30BB\u30B5\u30DA\u30A2 "${path}" \u306B\u675F\u306D\u3066\u3044\u307E\u3059\u3002state \u306E\u5074\u304C\u5148\u306B\u66F8\u304F\u306E\u3067\u3001\u8AAD\u307F\u8FBC\u307F\u306E\u305F\u3073\u306B getter \u306E\u6700\u521D\u306E\u5024\u304C\u4FDD\u5B58\u5024\u306E\u4E0A\u306B\u4FDD\u5B58\u3055\u308C\u3001setter \u306F\u4FDD\u5B58\u5024\u3092\u53D7\u3051\u53D6\u308C\u307E\u305B\u3093 \u2014 #init=element \u3092\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044\uFF08\`value#init=element: ${path}\`\uFF09`,
|
|
10997
11010
|
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`,
|
|
10998
11011
|
inputNestedWriteOneShot: (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\u305A\u3001<${tag}> \u306F\u8981\u6C42\u306E\u305F\u3073\u306B "${input}" \u3092 null \u306B\u623B\u3059\u306E\u3067\u3001\u6B21\u306E\u8981\u6C42\u306F "${input}" \u306A\u3057\u3067\u9001\u3089\u308C\u307E\u3059 \u2014 \u8981\u6C42\u3092\u59CB\u3081\u308B\u30CF\u30F3\u30C9\u30E9\u3067\u65B0\u3057\u3044\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u4F5C\u3063\u3066\u4EE3\u5165\u3057\uFF08\`this.submitBody = { \u2026 }\`\uFF09\u3001\u305D\u308C\u3092\u675F\u306D\u3066\u304F\u3060\u3055\u3044`,
|
|
10999
11012
|
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`,
|
|
11000
11013
|
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`,
|
|
11014
|
+
routerOutletMisplaced: () => `\u3053\u306E <wcs-outlet> \u306F <wcs-router> \u306E\u76F4\u5F8C\u306B\u7121\u3044\u306E\u3067\u3001\u3069\u306E router \u3082\u4F7F\u3044\u307E\u305B\u3093\u3002router \u306F\u81EA\u5206\u306E\u76F4\u5F8C\u306E\u8981\u7D20\u3060\u3051\u3092 outlet \u3068\u3057\u3066\u4F7F\u3044\u3001\u305D\u3053\u306B <wcs-outlet> \u304C\u7121\u3051\u308C\u3070\u81EA\u524D\u306E outlet \u3092\u4F5C\u308A\u307E\u3059\u3002\u30EB\u30FC\u30C8\u306F router \u306E\u76F4\u5F8C\u306B\u63CF\u304B\u308C\u3001\u3053\u306E outlet \u306F\u7A7A\u306E\u307E\u307E\u3067\u3059 \u2014 <wcs-router> \u306E\u76F4\u5F8C\u3078\u79FB\u3057\u3066\u304F\u3060\u3055\u3044`,
|
|
11001
11015
|
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`,
|
|
11002
11016
|
namedStateAttrDeprecated: (name) => `name \u5C5E\u6027\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\uFF081 root 1 \u30C4\u30EA\u30FC\uFF09\u3002\u30EB\u30FC\u30C8\u30C4\u30EA\u30FC\u3078\u306E\u30DE\u30A6\u30F3\u30C8 <wcs-state mount="${name}"> \u306B\u7F6E\u304D\u63DB\u3048\u3001\u30D1\u30B9\u306F "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`,
|
|
11003
11017
|
namedStatePathDeprecated: (name) => name === "default" ? `"@default" \u30BB\u30EC\u30AF\u30BF\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\u3002"@default" \u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09` : `"@name" \u30BB\u30EC\u30AF\u30BF\u306F v2 \u3067\u64A4\u53BB\u3055\u308C\u307E\u3057\u305F\uFF081 root 1 \u30C4\u30EA\u30FC\uFF09\u3002\u30DE\u30A6\u30F3\u30C8\u3057\u305F\u30C4\u30EA\u30FC\u3092 "${name}.<path>" \u3067\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF08docs/state-mount-design.md \xA79\uFF09`,
|
|
@@ -11131,6 +11145,7 @@ var en = {
|
|
|
11131
11145
|
delegatedCurrentTarget: (suggested, eventName, handler) => `"${eventName}" bindings are delegated to the root, so event.currentTarget in "${handler}" is the root, not this element. Write "${suggested}:" to keep the listener on the element, or find the element with event.target.closest(...)`,
|
|
11132
11146
|
getterCycle: (cycle) => `Path getters form a dependency cycle: ${cycle}`,
|
|
11133
11147
|
updatedCallbackUnbound: (p) => `$renderedCallback is binding-driven. "${p}" is not bound anywhere in this document, so this branch never runs. Use $watch to react without depending on what is rendered`,
|
|
11148
|
+
onHandlerThis: (name) => `The $on handler "${name}" uses this. A $on handler receives the state as its first argument (\`${name}(state, event) { state.x = \u2026 }\`) and this is undefined, so this.\u2026 throws when it is called \u2014 unlike $watch handlers and methods`,
|
|
11134
11149
|
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`,
|
|
11135
11150
|
handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
|
|
11136
11151
|
typeExpectation: (label, expected, resultType) => `"${label}" requires ${EN_EXPECTED_LABEL[expected]} (current type: ${resultType})`,
|
|
@@ -11181,13 +11196,14 @@ var en = {
|
|
|
11181
11196
|
didYouMean: (c) => `. Did you mean "${c}"?`,
|
|
11182
11197
|
none: () => `none`,
|
|
11183
11198
|
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`,
|
|
11184
|
-
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)`,
|
|
11199
|
+
storageSeedClobber: (path, raw) => `The <wcs-storage> value-bound slot "${path}" is seeded with ${shortSeed(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)`,
|
|
11185
11200
|
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`,
|
|
11186
11201
|
storagePairClobber: (path) => `The <wcs-storage> value is bound to the accessor pair "${path}" without #init=element. The state's side goes first, so on every load the getter's starting value is saved over the stored one, and the setter never receives it \u2014 add #init=element (\`value#init=element: ${path}\`)`,
|
|
11187
11202
|
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`,
|
|
11188
11203
|
inputNestedWriteOneShot: (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, and <${tag}> resets "${input}" to null after every request, so the next request goes without it \u2014 build a new object in the handler that starts the request (\`this.submitBody = { \u2026 }\`) and bind that`,
|
|
11189
11204
|
devtoolsAfterState: () => `Load @wcstack/devtools/auto BEFORE @wcstack/state/auto (otherwise the wiring ledger is not captured live)`,
|
|
11190
11205
|
baseHrefMissing: () => `An SPA using @wcstack/router needs <base href="/"> in <head> (without it, deep links misderive the basename)`,
|
|
11206
|
+
routerOutletMisplaced: () => `This <wcs-outlet> is not right after a <wcs-router>, so no router uses it: a router uses only the element right after it as its outlet, and makes an outlet of its own there when that is not a <wcs-outlet>. The routes render right after the router and this outlet stays empty \u2014 move it right after the <wcs-router>`,
|
|
11191
11207
|
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`,
|
|
11192
11208
|
namedStateAttrDeprecated: (name) => `The "name" attribute was removed in v2 \u2014 there is a single state tree per root. Mount this state onto the tree instead: <wcs-state mount="${name}"> and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
|
|
11193
11209
|
namedStatePathDeprecated: (name) => name === "default" ? `The "@default" selector was removed in v2 \u2014 drop it (docs/state-mount-design.md \xA79)` : `The "@name" selector was removed in v2 \u2014 there is a single state tree. Mount the named state onto the tree (<wcs-state mount="...">) and read it as "${name}.<path>" (docs/state-mount-design.md \xA79)`,
|
|
@@ -14049,7 +14065,6 @@ var EXPLICIT_PROPERTY_REJECTED_HEADS2 = /* @__PURE__ */ new Set([
|
|
|
14049
14065
|
var EXPLICIT_PROPERTY_REJECTED_HEADS_RE = new RegExp(
|
|
14050
14066
|
`^(?:${[...EXPLICIT_PROPERTY_REJECTED_HEADS2].map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(\\.|$)`
|
|
14051
14067
|
);
|
|
14052
|
-
var EMPTYISH_SEEDS = /* @__PURE__ */ new Set(["''", '""', "``", "null", "[]", "{}"]);
|
|
14053
14068
|
function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale2, fileReader) {
|
|
14054
14069
|
const diagnostics = [];
|
|
14055
14070
|
const msgs = getMessages(locale2);
|
|
@@ -14196,12 +14211,12 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
|
|
|
14196
14211
|
if (tagName === "wcs-storage" && property === "value" && !hasManual && parsed.path && !/(?:^|,)init=(?:element|auto)\b/.test(modifiers)) {
|
|
14197
14212
|
const paths = getPaths();
|
|
14198
14213
|
const cand = findDataSlot(paths, parsed.path);
|
|
14199
|
-
if (cand?.rawInitial !== void 0 &&
|
|
14214
|
+
if (cand?.rawInitial !== void 0 && (cand.rawInitial.trim() === "null" || isNonNullLiteralSeed(cand.rawInitial))) {
|
|
14200
14215
|
diagnostics.push({
|
|
14201
14216
|
code: WcsDiagnosticCode.StorageSeedClobber,
|
|
14202
14217
|
start,
|
|
14203
14218
|
end,
|
|
14204
|
-
severity: "
|
|
14219
|
+
severity: "error",
|
|
14205
14220
|
tag: tagName,
|
|
14206
14221
|
statePath: parsed.path,
|
|
14207
14222
|
message: msgs.storageSeedClobber(parsed.path, cand.rawInitial)
|
|
@@ -14211,7 +14226,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
|
|
|
14211
14226
|
code: WcsDiagnosticCode.StorageSeedClobber,
|
|
14212
14227
|
start,
|
|
14213
14228
|
end,
|
|
14214
|
-
severity: "
|
|
14229
|
+
severity: "error",
|
|
14215
14230
|
tag: tagName,
|
|
14216
14231
|
statePath: parsed.path,
|
|
14217
14232
|
message: msgs.storagePairClobber(parsed.path)
|
|
@@ -14240,10 +14255,6 @@ function isNonNullLiteralSeed(raw) {
|
|
|
14240
14255
|
function findDataSlot(paths, path) {
|
|
14241
14256
|
return paths.find((c) => c.kind === "data" && c.path === path);
|
|
14242
14257
|
}
|
|
14243
|
-
function normalizeSeed(raw) {
|
|
14244
|
-
const compact = raw.replace(/\s+/g, "");
|
|
14245
|
-
return compact === "" ? raw : compact;
|
|
14246
|
-
}
|
|
14247
14258
|
function findBuiltinTagOccurrences(html) {
|
|
14248
14259
|
const out = [];
|
|
14249
14260
|
const regex = /<(wcs-[a-z0-9-]+)((?:"[^"]*"|'[^']*'|[^>"'])*)>/gi;
|
|
@@ -14386,7 +14397,8 @@ function validateDocumentEnv(html, locale2) {
|
|
|
14386
14397
|
});
|
|
14387
14398
|
}
|
|
14388
14399
|
}
|
|
14389
|
-
const
|
|
14400
|
+
const hasRouterTag = /<wcs-router\b/i.test(scanText);
|
|
14401
|
+
const router = autos.find((a) => a.pkgs.includes("router") && (!a.bundle || hasRouterTag)) ?? (hasRouterTag ? collectInlineImports(scanText).find((i) => /@wcstack\/router\b/.test(i.spec) || BUNDLE_AUTO.test(i.spec)) : void 0);
|
|
14390
14402
|
if (router && !/<base\b[^>]*\bhref\s*=/i.test(scanText)) {
|
|
14391
14403
|
diagnostics.push({
|
|
14392
14404
|
code: WcsDiagnosticCode.BaseHrefMissing,
|
|
@@ -14396,6 +14408,15 @@ function validateDocumentEnv(html, locale2) {
|
|
|
14396
14408
|
message: msgs.baseHrefMissing()
|
|
14397
14409
|
});
|
|
14398
14410
|
}
|
|
14411
|
+
for (const outlet of findMisplacedOutlets(scanText)) {
|
|
14412
|
+
diagnostics.push({
|
|
14413
|
+
code: WcsDiagnosticCode.RouterOutletMisplaced,
|
|
14414
|
+
start: outlet.start,
|
|
14415
|
+
end: outlet.end,
|
|
14416
|
+
severity: "warning",
|
|
14417
|
+
message: msgs.routerOutletMisplaced()
|
|
14418
|
+
});
|
|
14419
|
+
}
|
|
14399
14420
|
const refs = collectSignalsRefs(scanText);
|
|
14400
14421
|
const dom = refs.find((r) => r.kind === "dom");
|
|
14401
14422
|
const bare = refs.find((r) => r.kind === "bare");
|
|
@@ -14435,35 +14456,41 @@ function findWcstackAutoScripts(html) {
|
|
|
14435
14456
|
}
|
|
14436
14457
|
function collectSignalsRefs(html) {
|
|
14437
14458
|
const refs = [];
|
|
14438
|
-
const scriptRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)
|
|
14459
|
+
const scriptRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>/gi;
|
|
14439
14460
|
let match;
|
|
14440
14461
|
while ((match = scriptRegex.exec(html)) !== null) {
|
|
14441
|
-
const
|
|
14442
|
-
const
|
|
14443
|
-
if (src) {
|
|
14444
|
-
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
|
|
14448
|
-
|
|
14449
|
-
end: match.index + src.offsetInTag + src.value.length
|
|
14450
|
-
});
|
|
14451
|
-
}
|
|
14452
|
-
continue;
|
|
14462
|
+
const src = extractSrc(match[0]);
|
|
14463
|
+
const kind = src ? classifySignalsSpecifier(src.value) : null;
|
|
14464
|
+
if (src && kind) {
|
|
14465
|
+
refs.push({
|
|
14466
|
+
kind,
|
|
14467
|
+
start: match.index + src.offsetInTag,
|
|
14468
|
+
end: match.index + src.offsetInTag + src.value.length
|
|
14469
|
+
});
|
|
14453
14470
|
}
|
|
14454
|
-
|
|
14471
|
+
}
|
|
14472
|
+
for (const im of collectInlineImports(html)) {
|
|
14473
|
+
const kind = classifySignalsSpecifier(im.spec);
|
|
14474
|
+
if (kind) refs.push({ kind, start: im.start, end: im.end });
|
|
14475
|
+
}
|
|
14476
|
+
return refs.sort((a, b) => a.start - b.start);
|
|
14477
|
+
}
|
|
14478
|
+
function collectInlineImports(html) {
|
|
14479
|
+
const out = [];
|
|
14480
|
+
const scriptRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi;
|
|
14481
|
+
let match;
|
|
14482
|
+
while ((match = scriptRegex.exec(html)) !== null) {
|
|
14483
|
+
if (extractSrc(`<script${match[1]}>`) || !/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
|
|
14455
14484
|
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
14456
14485
|
const body = blankJsComments(match[2]);
|
|
14457
|
-
const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']
|
|
14486
|
+
const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*)\1/g;
|
|
14458
14487
|
let im;
|
|
14459
14488
|
while ((im = importRegex.exec(body)) !== null) {
|
|
14460
|
-
const
|
|
14461
|
-
|
|
14462
|
-
const specStart = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
|
|
14463
|
-
refs.push({ kind, start: specStart, end: specStart + im[2].length });
|
|
14489
|
+
const start = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
|
|
14490
|
+
out.push({ spec: im[2], start, end: start + im[2].length });
|
|
14464
14491
|
}
|
|
14465
14492
|
}
|
|
14466
|
-
return
|
|
14493
|
+
return out;
|
|
14467
14494
|
}
|
|
14468
14495
|
function classifySignalsSpecifier(spec) {
|
|
14469
14496
|
if (!spec.includes("@wcstack/signals")) return null;
|
|
@@ -14483,6 +14510,34 @@ function blankHtmlComments(html) {
|
|
|
14483
14510
|
function blankJsComments(code) {
|
|
14484
14511
|
return code.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/(^|[^:])\/\/[^\n]*/g, (m, pre) => pre + " ".repeat(m.length - pre.length));
|
|
14485
14512
|
}
|
|
14513
|
+
var ROUTER_TAG2 = "wcs-router";
|
|
14514
|
+
var OUTLET_TAG = "wcs-outlet";
|
|
14515
|
+
function findMisplacedOutlets(html) {
|
|
14516
|
+
const scan = html.replace(
|
|
14517
|
+
/(<(script|style)\b[^>]*>)([\s\S]*?)(<\/\2\s*>)/gi,
|
|
14518
|
+
(_m, open, _name, body, close) => open + " ".repeat(body.length) + close
|
|
14519
|
+
);
|
|
14520
|
+
const tokens = [];
|
|
14521
|
+
const tagRegex = /<(\/?)([a-zA-Z][\w-]*)(?:"[^"]*"|'[^']*'|[^>"'])*>/g;
|
|
14522
|
+
let match;
|
|
14523
|
+
while ((match = tagRegex.exec(scan)) !== null) {
|
|
14524
|
+
const nameStart = match.index + 1 + match[1].length;
|
|
14525
|
+
tokens.push({ name: match[2].toLowerCase(), close: match[1] === "/", start: nameStart, end: nameStart + match[2].length });
|
|
14526
|
+
}
|
|
14527
|
+
let routerWithoutOutlet = false;
|
|
14528
|
+
const misplaced = [];
|
|
14529
|
+
tokens.forEach((token, i) => {
|
|
14530
|
+
if (token.name === ROUTER_TAG2 && token.close) {
|
|
14531
|
+
const next = tokens[i + 1];
|
|
14532
|
+
if (!next || next.close || next.name !== OUTLET_TAG) routerWithoutOutlet = true;
|
|
14533
|
+
}
|
|
14534
|
+
if (token.name === OUTLET_TAG && !token.close) {
|
|
14535
|
+
const prev = tokens[i - 1];
|
|
14536
|
+
if (!(prev && prev.close && prev.name === ROUTER_TAG2)) misplaced.push({ start: token.start, end: token.end });
|
|
14537
|
+
}
|
|
14538
|
+
});
|
|
14539
|
+
return routerWithoutOutlet ? misplaced : [];
|
|
14540
|
+
}
|
|
14486
14541
|
|
|
14487
14542
|
// src/service/watchDeclarationValidator.ts
|
|
14488
14543
|
var STATE_NAME_SEPARATOR = "@";
|
|
@@ -16098,6 +16153,24 @@ function hasOutOfRangeIndexName(script) {
|
|
|
16098
16153
|
}
|
|
16099
16154
|
return false;
|
|
16100
16155
|
}
|
|
16156
|
+
var ANY_MEMBER = { has: () => true };
|
|
16157
|
+
function validateOnHandlerThis(script, scriptStart, locale2) {
|
|
16158
|
+
const msgs = getMessages(locale2);
|
|
16159
|
+
const out = [];
|
|
16160
|
+
for (const handler of analyzeOnHandlerSources(script)) {
|
|
16161
|
+
const refs = handler.kind === "body" ? collectThisMemberRefs(handler.text, ANY_MEMBER) : collectThisMemberRefsInValue(handler.text, ANY_MEMBER);
|
|
16162
|
+
const first = refs?.[0];
|
|
16163
|
+
if (first === void 0) continue;
|
|
16164
|
+
out.push({
|
|
16165
|
+
code: WcsDiagnosticCode.OnHandlerThis,
|
|
16166
|
+
start: scriptStart + handler.start + first.start,
|
|
16167
|
+
end: scriptStart + handler.start + first.end,
|
|
16168
|
+
severity: "error",
|
|
16169
|
+
message: msgs.onHandlerThis(handler.name)
|
|
16170
|
+
});
|
|
16171
|
+
}
|
|
16172
|
+
return out;
|
|
16173
|
+
}
|
|
16101
16174
|
function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrName = "data-wcs") {
|
|
16102
16175
|
const out = [];
|
|
16103
16176
|
const blocks = parseLoadedScriptBlocks(html, stateTagName);
|
|
@@ -16109,6 +16182,7 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
|
|
|
16109
16182
|
out.push(...validateIndexParamReads(block.content, block.contentStart, locale2));
|
|
16110
16183
|
out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
|
|
16111
16184
|
out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
|
|
16185
|
+
out.push(...validateOnHandlerThis(block.content, block.contentStart, locale2));
|
|
16112
16186
|
}
|
|
16113
16187
|
out.push(...validateInputNestedWrites(html, stateTagName, bindAttrName, getNestedWriteRoots, locale2));
|
|
16114
16188
|
out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2));
|
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.1.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",
|