@wcstack/typescript 4.0.4 → 4.1.1

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 CHANGED
@@ -572,7 +572,7 @@ function loadSchemaCore() {
572
572
  return cached;
573
573
  }
574
574
 
575
- var version = "4.0.4";
575
+ var version = "4.1.1";
576
576
  var pkg = {
577
577
  version: version};
578
578
 
@@ -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
  // 越境 `@` / `$` 始まり / 空キー・空セグメント / 明らかな非関数ハンドラ。
@@ -3239,9 +3240,15 @@ function analyzeWatchEntries(scriptContent) {
3239
3240
  }));
3240
3241
  }
3241
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) {
3242
3249
  const root = locateDefaultExportObject(scriptContent);
3243
3250
  if (!root) return [];
3244
- const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_WATCH_KEY);
3251
+ const prop = parseTopLevelProperties(root.content).find((p) => p.name === key);
3245
3252
  if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
3246
3253
  return [];
3247
3254
  }
@@ -3251,6 +3258,7 @@ function analyzeWatchHandlerSources(scriptContent) {
3251
3258
  for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
3252
3259
  if (entry.value === void 0 || entry.valueStart === void 0) continue;
3253
3260
  out.push({
3261
+ name: entry.name,
3254
3262
  kind: entry.kind === "data" ? "value" : "body",
3255
3263
  text: entry.value,
3256
3264
  start: innerStart + entry.valueStart
@@ -10906,6 +10914,35 @@ function propertyKeyIs(property, name) {
10906
10914
  if (!property.computed && key.type === "Identifier") return key.name === name;
10907
10915
  return key.type === "Literal" && key.value === name;
10908
10916
  }
10917
+ function findOwnThis(text3, kind) {
10918
+ let program;
10919
+ const prefix = kind === "body" ? PREFIX : VALUE_PREFIX;
10920
+ try {
10921
+ program = parse3(prefix + text3 + (kind === "body" ? SUFFIX : VALUE_SUFFIX), { ecmaVersion: "latest", sourceType: "module" });
10922
+ } catch {
10923
+ return null;
10924
+ }
10925
+ const wrapper = unwrapWrapper(program);
10926
+ if (wrapper === null) return null;
10927
+ let root = wrapper.body;
10928
+ if (kind === "value") {
10929
+ const statement = wrapper.body.body.length === 1 ? wrapper.body.body[0] : null;
10930
+ if (statement === null || statement.type !== "ExpressionStatement" || statement.expression.type !== "FunctionExpression") return null;
10931
+ root = statement.expression.body;
10932
+ }
10933
+ let found = null;
10934
+ const walk = (node) => {
10935
+ if (found !== null || isClassNode(node)) return;
10936
+ if (isFunctionNode(node) && node.type !== "ArrowFunctionExpression") return;
10937
+ if (node.type === "ThisExpression") {
10938
+ found = { start: node.start - prefix.length, end: node.end - prefix.length };
10939
+ return;
10940
+ }
10941
+ forEachChild(node, walk);
10942
+ };
10943
+ forEachChild(root, walk);
10944
+ return found;
10945
+ }
10909
10946
 
10910
10947
  // src/core/messages.ts
10911
10948
  function resolveLocale(locale2) {
@@ -10945,6 +10982,7 @@ var ja = {
10945
10982
  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`,
10946
10983
  getterCycle: (cycle) => `\u30D1\u30B9 getter \u304C\u5FAA\u74B0\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${cycle}`,
10947
10984
  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`,
10985
+ 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`,
10948
10986
  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`,
10949
10987
  handlerFilterNotAllowed: (prop) => `\u30A4\u30D9\u30F3\u30C8\u30CF\u30F3\u30C9\u30E9 "${prop}" \u306B\u30D5\u30A3\u30EB\u30BF\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093`,
10950
10988
  typeExpectation: (label, expected, resultType) => `"${label}" \u306B\u306F${JA_EXPECTED_LABEL[expected]}\u304C\u5FC5\u8981\u3067\u3059\uFF08\u73FE\u5728\u306E\u578B: ${resultType}\uFF09`,
@@ -10995,7 +11033,7 @@ var ja = {
10995
11033
  didYouMean: (c) => `\u3002\u3082\u3057\u304B\u3057\u3066: "${c}"`,
10996
11034
  none: () => `\u306A\u3057`,
10997
11035
  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`,
10998
- 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`,
11036
+ 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`,
10999
11037
  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`,
11000
11038
  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`,
11001
11039
  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`,
@@ -11136,6 +11174,7 @@ var en = {
11136
11174
  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(...)`,
11137
11175
  getterCycle: (cycle) => `Path getters form a dependency cycle: ${cycle}`,
11138
11176
  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`,
11177
+ 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`,
11139
11178
  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`,
11140
11179
  handlerFilterNotAllowed: (prop) => `Filters cannot be applied to event handler "${prop}"`,
11141
11180
  typeExpectation: (label, expected, resultType) => `"${label}" requires ${EN_EXPECTED_LABEL[expected]} (current type: ${resultType})`,
@@ -11186,7 +11225,7 @@ var en = {
11186
11225
  didYouMean: (c) => `. Did you mean "${c}"?`,
11187
11226
  none: () => `none`,
11188
11227
  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`,
11189
- 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)`,
11228
+ 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)`,
11190
11229
  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`,
11191
11230
  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}\`)`,
11192
11231
  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`,
@@ -13534,7 +13573,8 @@ var BUILTIN_TAGS = {
13534
13573
  "downlink",
13535
13574
  "rtt",
13536
13575
  "saveData",
13537
- "supported"
13576
+ "supported",
13577
+ "offline"
13538
13578
  ],
13539
13579
  "commands": []
13540
13580
  },
@@ -14055,7 +14095,6 @@ var EXPLICIT_PROPERTY_REJECTED_HEADS2 = /* @__PURE__ */ new Set([
14055
14095
  var EXPLICIT_PROPERTY_REJECTED_HEADS_RE = new RegExp(
14056
14096
  `^(?:${[...EXPLICIT_PROPERTY_REJECTED_HEADS2].map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(\\.|$)`
14057
14097
  );
14058
- var EMPTYISH_SEEDS = /* @__PURE__ */ new Set(["''", '""', "``", "null", "[]", "{}"]);
14059
14098
  function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale2, fileReader) {
14060
14099
  const diagnostics = [];
14061
14100
  const msgs = getMessages(locale2);
@@ -14202,12 +14241,12 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
14202
14241
  if (tagName === "wcs-storage" && property === "value" && !hasManual && parsed.path && !/(?:^|,)init=(?:element|auto)\b/.test(modifiers)) {
14203
14242
  const paths = getPaths();
14204
14243
  const cand = findDataSlot(paths, parsed.path);
14205
- if (cand?.rawInitial !== void 0 && EMPTYISH_SEEDS.has(normalizeSeed(cand.rawInitial))) {
14244
+ if (cand?.rawInitial !== void 0 && (cand.rawInitial.trim() === "null" || isNonNullLiteralSeed(cand.rawInitial))) {
14206
14245
  diagnostics.push({
14207
14246
  code: WcsDiagnosticCode.StorageSeedClobber,
14208
14247
  start,
14209
14248
  end,
14210
- severity: "warning",
14249
+ severity: "error",
14211
14250
  tag: tagName,
14212
14251
  statePath: parsed.path,
14213
14252
  message: msgs.storageSeedClobber(parsed.path, cand.rawInitial)
@@ -14217,7 +14256,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
14217
14256
  code: WcsDiagnosticCode.StorageSeedClobber,
14218
14257
  start,
14219
14258
  end,
14220
- severity: "warning",
14259
+ severity: "error",
14221
14260
  tag: tagName,
14222
14261
  statePath: parsed.path,
14223
14262
  message: msgs.storagePairClobber(parsed.path)
@@ -14246,10 +14285,6 @@ function isNonNullLiteralSeed(raw) {
14246
14285
  function findDataSlot(paths, path) {
14247
14286
  return paths.find((c) => c.kind === "data" && c.path === path);
14248
14287
  }
14249
- function normalizeSeed(raw) {
14250
- const compact = raw.replace(/\s+/g, "");
14251
- return compact === "" ? raw : compact;
14252
- }
14253
14288
  function findBuiltinTagOccurrences(html) {
14254
14289
  const out = [];
14255
14290
  const regex = /<(wcs-[a-z0-9-]+)((?:"[^"]*"|'[^']*'|[^>"'])*)>/gi;
@@ -14392,7 +14427,8 @@ function validateDocumentEnv(html, locale2) {
14392
14427
  });
14393
14428
  }
14394
14429
  }
14395
- const router = autos.find((a) => a.pkgs.includes("router") && (!a.bundle || /<wcs-router\b/i.test(scanText)));
14430
+ const hasRouterTag = /<wcs-router\b/i.test(scanText);
14431
+ 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);
14396
14432
  if (router && !/<base\b[^>]*\bhref\s*=/i.test(scanText)) {
14397
14433
  diagnostics.push({
14398
14434
  code: WcsDiagnosticCode.BaseHrefMissing,
@@ -14450,35 +14486,41 @@ function findWcstackAutoScripts(html) {
14450
14486
  }
14451
14487
  function collectSignalsRefs(html) {
14452
14488
  const refs = [];
14453
- const scriptRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi;
14489
+ const scriptRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>/gi;
14454
14490
  let match;
14455
14491
  while ((match = scriptRegex.exec(html)) !== null) {
14456
- const openTag = html.slice(match.index, match.index + match[0].indexOf(">") + 1);
14457
- const src = extractSrc(openTag);
14458
- if (src) {
14459
- const kind = classifySignalsSpecifier(src.value);
14460
- if (kind) {
14461
- refs.push({
14462
- kind,
14463
- start: match.index + src.offsetInTag,
14464
- end: match.index + src.offsetInTag + src.value.length
14465
- });
14466
- }
14467
- continue;
14492
+ const src = extractSrc(match[0]);
14493
+ const kind = src ? classifySignalsSpecifier(src.value) : null;
14494
+ if (src && kind) {
14495
+ refs.push({
14496
+ kind,
14497
+ start: match.index + src.offsetInTag,
14498
+ end: match.index + src.offsetInTag + src.value.length
14499
+ });
14468
14500
  }
14469
- if (!/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
14501
+ }
14502
+ for (const im of collectInlineImports(html)) {
14503
+ const kind = classifySignalsSpecifier(im.spec);
14504
+ if (kind) refs.push({ kind, start: im.start, end: im.end });
14505
+ }
14506
+ return refs.sort((a, b) => a.start - b.start);
14507
+ }
14508
+ function collectInlineImports(html) {
14509
+ const out = [];
14510
+ const scriptRegex = /<script\b((?:"[^"]*"|'[^']*'|[^>"'])*)>([\s\S]*?)<\/script\s*>/gi;
14511
+ let match;
14512
+ while ((match = scriptRegex.exec(html)) !== null) {
14513
+ if (extractSrc(`<script${match[1]}>`) || !/\btype\s*=\s*(["'])module\1/i.test(match[1])) continue;
14470
14514
  const bodyStart = match.index + match[0].indexOf(">") + 1;
14471
14515
  const body = blankJsComments(match[2]);
14472
- const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*@wcstack\/signals[^"']*)\1/g;
14516
+ const importRegex = /(?:\bfrom\s*|\bimport\s*\(?\s*)(["'])([^"']*)\1/g;
14473
14517
  let im;
14474
14518
  while ((im = importRegex.exec(body)) !== null) {
14475
- const kind = classifySignalsSpecifier(im[2]);
14476
- if (!kind) continue;
14477
- const specStart = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
14478
- refs.push({ kind, start: specStart, end: specStart + im[2].length });
14519
+ const start = bodyStart + im.index + im[0].indexOf(im[1]) + 1;
14520
+ out.push({ spec: im[2], start, end: start + im[2].length });
14479
14521
  }
14480
14522
  }
14481
- return refs;
14523
+ return out;
14482
14524
  }
14483
14525
  function classifySignalsSpecifier(spec) {
14484
14526
  if (!spec.includes("@wcstack/signals")) return null;
@@ -16141,6 +16183,22 @@ function hasOutOfRangeIndexName(script) {
16141
16183
  }
16142
16184
  return false;
16143
16185
  }
16186
+ function validateOnHandlerThis(script, scriptStart, locale2) {
16187
+ const msgs = getMessages(locale2);
16188
+ const out = [];
16189
+ for (const handler of analyzeOnHandlerSources(script)) {
16190
+ const first = findOwnThis(handler.text, handler.kind);
16191
+ if (first === null) continue;
16192
+ out.push({
16193
+ code: WcsDiagnosticCode.OnHandlerThis,
16194
+ start: scriptStart + handler.start + first.start,
16195
+ end: scriptStart + handler.start + first.end,
16196
+ severity: "error",
16197
+ message: msgs.onHandlerThis(handler.name)
16198
+ });
16199
+ }
16200
+ return out;
16201
+ }
16144
16202
  function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrName = "data-wcs") {
16145
16203
  const out = [];
16146
16204
  const blocks = parseLoadedScriptBlocks(html, stateTagName);
@@ -16152,6 +16210,7 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
16152
16210
  out.push(...validateIndexParamReads(block.content, block.contentStart, locale2));
16153
16211
  out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
16154
16212
  out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
16213
+ out.push(...validateOnHandlerThis(block.content, block.contentStart, locale2));
16155
16214
  }
16156
16215
  out.push(...validateInputNestedWrites(html, stateTagName, bindAttrName, getNestedWriteRoots, locale2));
16157
16216
  out.push(...validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2));
@@ -573,7 +573,7 @@ function loadSchemaCore() {
573
573
  return cached;
574
574
  }
575
575
 
576
- var version = "4.0.4";
576
+ var version = "4.1.1";
577
577
  var pkg = {
578
578
  version: version};
579
579
 
package/dist/wcs-tsc.mjs CHANGED
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
5
5
  import { dirname, join, isAbsolute, resolve, basename } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
- var version = "4.0.4";
8
+ var version = "4.1.1";
9
9
  var pkg = {
10
10
  version: version};
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/typescript",
3
- "version": "4.0.4",
3
+ "version": "4.1.1",
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",