@wcstack/lint 1.26.0 → 1.27.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.
Files changed (2) hide show
  1. package/dist/cli.cjs +130 -10
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -89,6 +89,13 @@ var WcsDiagnosticCode = {
89
89
  TokenUndeclared: "wcs/token-undeclared",
90
90
  TokenMisconfigured: "wcs/token-misconfigured",
91
91
  NestedAssign: "wcs/nested-assign",
92
+ // --- <wcs-state> script: $watch declaration ---
93
+ // ランタイム(watch/processWatchDeclaration.ts)が raiseError で落とす宣言。
94
+ // 越境 `@` / `$` 始まり / 空キー・空セグメント / 明らかな非関数ハンドラ。
95
+ WatchDeclarationInvalid: "wcs/watch-declaration-invalid",
96
+ // `$watch` のキーが状態定義に存在しない。バインディング側と違い黙って発火しない
97
+ // だけなので気づけない。severity は binding-path-missing に揃える(warning)。
98
+ WatchPathMissing: "wcs/watch-path-missing",
92
99
  TypeAnnotation: "wcs/type-annotation",
93
100
  TemplateSyntax: "wcs/template-syntax",
94
101
  // --- <wcs-state> script: array reactivity hazards ---
@@ -134,6 +141,8 @@ var builtinFilterMeta = {
134
141
  mul: { description: "\u4E57\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
135
142
  div: { description: "\u9664\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
136
143
  mod: { description: "\u5270\u4F59", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
144
+ abs: { description: "\u7D76\u5BFE\u5024", hasArgs: false, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 0 },
145
+ clamp: { description: "\u7BC4\u56F2\u5185\u306B\u4E38\u3081\u308B (min,max)", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 2, maxArgs: 2, argTypes: ["number", "number"] },
137
146
  // 数値フォーマット
138
147
  fix: { description: "\u56FA\u5B9A\u5C0F\u6570\u70B9\u8868\u8A18", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
139
148
  locale: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u3067\u6570\u5024\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
@@ -147,6 +156,8 @@ var builtinFilterMeta = {
147
156
  pad: { description: "\u30D1\u30C7\u30A3\u30F3\u30B0 (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
148
157
  rep: { description: "\u7E70\u308A\u8FD4\u3057 (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
149
158
  rev: { description: "\u6587\u5B57\u9806\u3092\u53CD\u8EE2", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
159
+ truncate: { description: "\u5207\u308A\u8A70\u3081\u3066\u7701\u7565\u8A18\u53F7 (length[,suffix])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
160
+ join: { description: "\u914D\u5217\u3092\u9023\u7D50 ([separator])", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
150
161
  // 数値パース・丸め
151
162
  int: { description: "\u6574\u6570\u306B\u30D1\u30FC\u30B9", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
152
163
  float: { description: "\u6D6E\u52D5\u5C0F\u6570\u70B9\u6570\u306B\u30D1\u30FC\u30B9", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
@@ -154,11 +165,15 @@ var builtinFilterMeta = {
154
165
  floor: { description: "\u5207\u308A\u4E0B\u3052", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
155
166
  ceil: { description: "\u5207\u308A\u4E0A\u3052", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
156
167
  percent: { description: "\u30D1\u30FC\u30BB\u30F3\u30C6\u30FC\u30B8\u5F62\u5F0F", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
168
+ // number だけでなく string も受ける。実用チェーンは fix / percent の後ろに繋がり、
169
+ // それらは既に string を返すため(builtinFilters.ts の unit を参照)
170
+ unit: { description: "\u5358\u4F4D\uFF08\u63A5\u5C3E\u8F9E\uFF09\u3092\u4ED8\u52A0", hasArgs: true, resultType: "string", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["string"] },
157
171
  // 日付・時刻
158
172
  date: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u4ED8", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
159
173
  time: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u6642\u523B", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
160
174
  datetime: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u6642", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
161
175
  ymd: { description: "YYYY-MM-DD \u5F62\u5F0F", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
176
+ hms: { description: "HH:MM:SS \u5F62\u5F0F", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
162
177
  // 真偽値・変換
163
178
  falsy: { description: "\u507D\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
164
179
  truthy: { description: "\u771F\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
@@ -419,6 +434,7 @@ var RESERVED_STREAMS_KEY = "$streams";
419
434
  var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
420
435
  var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
421
436
  var RESERVED_LIST_KEYS_KEY = "$listKeys";
437
+ var RESERVED_WATCH_KEY = "$watch";
422
438
  function analyzeStatePaths(scriptContent, stateName = "default") {
423
439
  const objectContent = extractDefaultExportObject(scriptContent);
424
440
  if (!objectContent) return [];
@@ -452,6 +468,36 @@ function analyzeStatePaths(scriptContent, stateName = "default") {
452
468
  }
453
469
  return paths;
454
470
  }
471
+ function analyzeWatchEntries(scriptContent) {
472
+ const root = locateDefaultExportObject(scriptContent);
473
+ if (!root) return [];
474
+ const watchProp = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_WATCH_KEY);
475
+ if (!watchProp || watchProp.kind !== "data" || !watchProp.value || !isObjectLiteral(watchProp.value) || watchProp.valueStart === void 0) {
476
+ return [];
477
+ }
478
+ const leading = watchProp.value.length - watchProp.value.trimStart().length;
479
+ const innerStart = root.start + watchProp.valueStart + leading + 1;
480
+ const entries = [];
481
+ for (const entry of parseTopLevelProperties(extractObjectContent(watchProp.value))) {
482
+ if (entry.nameStart === void 0 || entry.nameEnd === void 0) continue;
483
+ entries.push({
484
+ key: entry.name,
485
+ start: innerStart + entry.nameStart,
486
+ end: innerStart + entry.nameEnd,
487
+ // メソッド短縮記法は関数。data は値リテラルの形で判定し、識別子参照は疑わない。
488
+ definitelyNotFunction: entry.kind === "data" && isNonFunctionLiteral(entry.value)
489
+ });
490
+ }
491
+ return entries;
492
+ }
493
+ function isNonFunctionLiteral(value) {
494
+ if (value === void 0) return false;
495
+ const trimmed = value.trim();
496
+ if (trimmed.length === 0) return false;
497
+ const scan = maskCommentsAndStrings(trimmed);
498
+ if (/^(?:async\s+)?function\b/.test(trimmed) || scan.includes("=>")) return false;
499
+ return /^["'`]/.test(trimmed) || /^-?\d/.test(trimmed) || /^(?:true|false|null|undefined)\b/.test(trimmed) || trimmed.startsWith("[") || trimmed.startsWith("{");
500
+ }
455
501
  function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys, stateName) {
456
502
  if (prop.name === RESERVED_STREAMS_KEY && prop.kind === "data" && prop.value && isObjectLiteral(prop.value)) {
457
503
  const entries = parseTopLevelProperties(extractObjectContent(prop.value));
@@ -598,23 +644,29 @@ function inferJsonTypeHint(value) {
598
644
  if (typeof value === "object") return "object";
599
645
  return void 0;
600
646
  }
601
- function extractDefaultExportObject(script) {
647
+ function locateDefaultExportObject(script) {
602
648
  const scan = maskCommentsAndStrings(script);
603
649
  const match = scan.match(/export\s+default\s+(?:defineState\s*\(\s*)?(\{)/);
604
650
  if (!match) return null;
605
- const startIndex = scan.indexOf(match[1], match.index);
606
- return extractBracedContent(script, scan, startIndex);
651
+ const braceIndex = scan.indexOf(match[1], match.index);
652
+ return { content: extractBracedContent(script, scan, braceIndex), start: braceIndex + 1 };
653
+ }
654
+ function extractDefaultExportObject(script) {
655
+ return locateDefaultExportObject(script)?.content ?? null;
607
656
  }
608
657
  function parseTopLevelProperties(objectContent) {
609
658
  const props = [];
610
659
  const scan = maskCommentsAndStrings(objectContent);
611
- const regex = /(?:(?:get|set)\s+(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:async\s+)?([$\w]+)\s*\([^)]*\)\s*\{)|(?:(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*:\s*)/gd;
660
+ const regex = /(?:(?:get|set)\s+(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:async\s+)?(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*\([^)]*\)\s*\{)|(?:(?:"([^"]+)"|'([^']+)'|([$\w]+))\s*:\s*)/gd;
612
661
  let match;
613
662
  while ((match = regex.exec(scan)) !== null) {
614
663
  const indices = match.indices;
664
+ let nameSpan;
615
665
  const nameAt = (group) => {
616
666
  const span = indices[group];
617
- return span ? objectContent.slice(span[0], span[1]) : void 0;
667
+ if (!span) return void 0;
668
+ nameSpan = [span[0], span[1]];
669
+ return objectContent.slice(span[0], span[1]);
618
670
  };
619
671
  const skipBody = () => {
620
672
  const braceStart = match.index + match[0].length - 1;
@@ -623,23 +675,31 @@ function parseTopLevelProperties(objectContent) {
623
675
  };
624
676
  const accessorName = nameAt(1) ?? nameAt(2) ?? nameAt(3);
625
677
  if (accessorName) {
626
- props.push({ name: accessorName, kind: "getter" });
678
+ props.push({ name: accessorName, kind: "getter", nameStart: nameSpan[0], nameEnd: nameSpan[1] });
627
679
  skipBody();
628
680
  continue;
629
681
  }
630
- const methodName = nameAt(4);
682
+ const methodName = nameAt(4) ?? nameAt(5) ?? nameAt(6);
631
683
  if (methodName) {
632
- props.push({ name: methodName, kind: "method" });
684
+ props.push({ name: methodName, kind: "method", nameStart: nameSpan[0], nameEnd: nameSpan[1] });
633
685
  skipBody();
634
686
  continue;
635
687
  }
636
- const propName = nameAt(5) ?? nameAt(6) ?? nameAt(7);
688
+ const propName = nameAt(7) ?? nameAt(8) ?? nameAt(9);
637
689
  if (propName) {
638
690
  const valueStartIndex = match.index + match[0].length;
639
691
  const value = extractFullValue(objectContent, scan, valueStartIndex);
640
692
  const jsdocType = extractJsDocType(objectContent, match.index);
641
693
  const typeHint = jsdocType ?? inferTypeHint(value);
642
- props.push({ name: propName, kind: "data", value, typeHint });
694
+ props.push({
695
+ name: propName,
696
+ kind: "data",
697
+ value,
698
+ typeHint,
699
+ nameStart: nameSpan[0],
700
+ nameEnd: nameSpan[1],
701
+ valueStart: valueStartIndex
702
+ });
643
703
  regex.lastIndex = valueStartIndex + value.length;
644
704
  }
645
705
  }
@@ -971,6 +1031,11 @@ var ja = {
971
1031
  wcsTextInfo: (e) => `wcs-text \u30D0\u30A4\u30F3\u30C7\u30A3\u30F3\u30B0: ${e}`,
972
1032
  moustacheFouc: (e) => `<template> \u5916\u306E {{ }} \u69CB\u6587\u306F FOUC\uFF08\u521D\u671F\u8868\u793A\u6642\u306B\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u6587\u5B57\u5217\u304C\u898B\u3048\u308B\uFF09\u306E\u539F\u56E0\u306B\u306A\u308A\u307E\u3059\u3002<!--@@:${e}--> \u307E\u305F\u306F\u30B3\u30E1\u30F3\u30C8\u69CB\u6587\u306E\u4F7F\u7528\u3092\u691C\u8A0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
973
1033
  nestedAssign: (sp) => `\u30CD\u30B9\u30C8\u3055\u308C\u305F\u30D7\u30ED\u30D1\u30C6\u30A3\u3078\u306E\u4EE3\u5165\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002this["${sp}"] \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
1034
+ watchKeyCrossState: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306F\u4ED6\u306E state \u3092\u6307\u3057\u3066\u3044\u307E\u3059\u3002@ \u4ED8\u304D\u306E\u8D8A\u5883 watch \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08\u81EA state \u306E\u30D1\u30B9\u306E\u307F\uFF09`,
1035
+ watchKeyReserved: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306F "$" \u3067\u59CB\u3081\u3089\u308C\u307E\u305B\u3093\uFF08\u4E88\u7D04\u540D\u524D\u7A7A\u9593\uFF09`,
1036
+ watchKeyEmptySegment: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306B\u7A7A\u306E\u30D1\u30B9\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059`,
1037
+ watchHandlerNotFunction: (k) => `$watch \u306E\u30A8\u30F3\u30C8\u30EA "${k}" \u306E\u5024\u306F\u95A2\u6570\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`,
1038
+ watchPathMissing: (k) => `$watch \u306E\u30AD\u30FC "${k}" \u306F\u72B6\u614B\u5B9A\u7FA9\u306B\u5B58\u5728\u3057\u307E\u305B\u3093\uFF08\u4E00\u5EA6\u3082\u767A\u706B\u3057\u307E\u305B\u3093\uFF09`,
974
1039
  typeAnnotationIncompatible: (vt, rt) => `\u578B "${vt}" \u306F @type {${rt}} \u3068\u4E92\u63DB\u6027\u304C\u3042\u308A\u307E\u305B\u3093`,
975
1040
  arrayMutation: (m, alt) => `\u914D\u5217\u306E\u7834\u58CA\u7684\u30E1\u30BD\u30C3\u30C9 "${m}" \u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\uFF08\u540C\u4E00\u53C2\u7167\u306E\u81EA\u5DF1\u518D\u4EE3\u5165\u3067\u3082\u8981\u7D20\u306E\u8FFD\u52A0\u30FB\u524A\u9664\u306F\u53CD\u6620\u3055\u308C\u307E\u305B\u3093\uFF09\u3002\u975E\u7834\u58CA\u30E1\u30BD\u30C3\u30C9\u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: ${alt}\uFF09\u3002`,
976
1041
  arrayIndexAssign: (sp) => `\u914D\u5217\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3078\u306E\u76F4\u63A5\u4EE3\u5165\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002this["${sp}"] \u306E\u3088\u3046\u306A\u30C9\u30C3\u30C8\u30D1\u30B9\u4EE3\u5165\u3001\u307E\u305F\u306F with() \u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
@@ -1013,6 +1078,11 @@ var en = {
1013
1078
  wcsTextInfo: (e) => `wcs-text binding: ${e}`,
1014
1079
  moustacheFouc: (e) => `{{ }} outside a <template> causes FOUC (the raw template string is visible before binding). Consider the comment syntax <!--@@:${e}--> instead.`,
1015
1080
  nestedAssign: (sp) => `Assigning to a nested property does not trigger a reactive update. Use this["${sp}"] instead.`,
1081
+ watchKeyCrossState: (k) => `$watch key "${k}" targets another state. Cross-state watching with @ is not supported (own paths only)`,
1082
+ watchKeyReserved: (k) => `$watch key "${k}" must not start with "$" (reserved namespace)`,
1083
+ watchKeyEmptySegment: (k) => `$watch key "${k}" has an empty path segment`,
1084
+ watchHandlerNotFunction: (k) => `The value of $watch entry "${k}" must be a function`,
1085
+ watchPathMissing: (k) => `$watch key "${k}" does not exist in the state definition (it will never fire)`,
1016
1086
  typeAnnotationIncompatible: (vt, rt) => `Type "${vt}" is not compatible with @type {${rt}}`,
1017
1087
  arrayMutation: (m, alt) => `Destructive array method "${m}" does not trigger a reactive update (re-assigning the same reference does not reflect added/removed elements either). Use a non-destructive method with reassignment (e.g. ${alt}).`,
1018
1088
  arrayIndexAssign: (sp) => `Assigning directly to an array index does not trigger a reactive update. Use a dot-path assignment like this["${sp}"], or with() plus reassignment.`,
@@ -3078,6 +3148,55 @@ function blankJsComments(code) {
3078
3148
  return code.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/(^|[^:])\/\/[^\n]*/g, (m, pre) => pre + " ".repeat(m.length - pre.length));
3079
3149
  }
3080
3150
 
3151
+ // src/service/watchDeclarationValidator.ts
3152
+ var STATE_NAME_SEPARATOR = "@";
3153
+ function validateWatchDeclarations(html, stateTagName = "wcs-state", locale) {
3154
+ const msgs = getMessages(locale);
3155
+ const out = [];
3156
+ for (const block of parseWcsScriptBlocks(html, stateTagName)) {
3157
+ const entries = analyzeWatchEntries(block.content);
3158
+ if (entries.length === 0) continue;
3159
+ const paths = analyzeStatePaths(block.content, block.stateName);
3160
+ const pathSet = new Set(paths.map((p) => p.path));
3161
+ for (const entry of entries) {
3162
+ const diagnostic = validateEntry(entry, pathSet, msgs);
3163
+ if (diagnostic === null) continue;
3164
+ out.push({
3165
+ code: diagnostic.code,
3166
+ start: block.contentStart + entry.start,
3167
+ end: block.contentStart + entry.end,
3168
+ message: diagnostic.message,
3169
+ severity: diagnostic.severity
3170
+ });
3171
+ }
3172
+ }
3173
+ return out;
3174
+ }
3175
+ function validateEntry(entry, pathSet, msgs) {
3176
+ const { key } = entry;
3177
+ const invalid = (message) => ({ code: WcsDiagnosticCode.WatchDeclarationInvalid, message, severity: "error" });
3178
+ if (key.includes(STATE_NAME_SEPARATOR)) {
3179
+ return invalid(msgs.watchKeyCrossState(key));
3180
+ }
3181
+ if (key.startsWith("$")) {
3182
+ return invalid(msgs.watchKeyReserved(key));
3183
+ }
3184
+ if (key.split(".").some((segment) => segment.length === 0)) {
3185
+ return invalid(msgs.watchKeyEmptySegment(key));
3186
+ }
3187
+ if (entry.definitelyNotFunction) {
3188
+ return invalid(msgs.watchHandlerNotFunction(key));
3189
+ }
3190
+ if (pathSet.size > 0 && !pathSet.has(key)) {
3191
+ return {
3192
+ code: WcsDiagnosticCode.WatchPathMissing,
3193
+ message: msgs.watchPathMissing(key),
3194
+ severity: "warning"
3195
+ };
3196
+ }
3197
+ return null;
3198
+ }
3199
+
3081
3200
  // src/core/validateDocument.ts
3082
3201
  function validateDocument(text, options = {}) {
3083
3202
  const bindAttribute = options.bindAttribute ?? "data-wcs";
@@ -3089,6 +3208,7 @@ function validateDocument(text, options = {}) {
3089
3208
  out.push(...validateIoNodes(text, bindAttribute, stateTagName, locale));
3090
3209
  out.push(...validateDocumentEnv(text, locale));
3091
3210
  out.push(...validateArrayMutations(text, stateTagName, locale));
3211
+ out.push(...validateWatchDeclarations(text, stateTagName, locale));
3092
3212
  for (const d of validateStateTypes(text, stateTagName, locale)) {
3093
3213
  out.push({ code: WcsDiagnosticCode.TypeAnnotation, start: d.start, end: d.end, message: d.message, severity: d.severity });
3094
3214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/lint",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "Static-contract validator CLI (wcs-validate) for wcstack data-wcs bindings and wcstack.manifest.json sidecars. Thin npm wrapper around the wcstack-intellisense validator core.",
5
5
  "type": "module",
6
6
  "bin": {