@wcstack/lint 3.0.0 → 3.2.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 +190 -53
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -201,6 +201,12 @@ var WcsDiagnosticCode = {
201
201
  // --- built-in wcs-* tag contract (generated/builtinTags.generated.ts が正本) ---
202
202
  // 未知メンバーへのバインド(プロパティ / command. / eventToken. キー)。黙って無視される。
203
203
  TagMemberUnknown: "wcs/tag-member-unknown",
204
+ // タグのメンバー名が "on" で始まる(`once` 等)のに先頭ドット無しで束縛した: ランタイムはイベント束縛にして
205
+ // "ce" イベントを待ち、値は届かない。明示のプロパティ形 `.once:` を提案する(@wcstack/state 3.1・要件 B5 / 3.x 計画 D36)
206
+ OnPrefixedMember: "wcs/on-prefixed-member",
207
+ // 3.x の間だけ残る旧名(フィルタ `uc` → `upper`、`$trackDependency` → `$dependOn` …)を書いた(info)。
208
+ // 動くが 4.0 で外れるので正式名を提案する(@wcstack/state 3.2・要件 B12 / 3.x 計画 D39)
209
+ NameAlias: "wcs/name-alias",
204
210
  // wcBindable 無宣言タグ(wcs-fetch-header 等のヘルパー)への spread。
205
211
  // ランタイム(expandSpread)は raiseError で落とす。
206
212
  SpreadNoBindable: "wcs/spread-no-bindable",
@@ -264,6 +270,18 @@ function valueMustBeDate(fnName) {
264
270
  function valueMustBeArray(fnName) {
265
271
  raiseError(`filter ${fnName} requires an array value`);
266
272
  }
273
+ var builtinFilterAliases = {
274
+ inc: "add",
275
+ dec: "sub",
276
+ fix: "toFixed",
277
+ uc: "upper",
278
+ lc: "lower",
279
+ cap: "capitalize",
280
+ rep: "repeat",
281
+ rev: "reverse",
282
+ pad: "padStart",
283
+ null: "nullIfEmpty"
284
+ };
267
285
  function validateNumberString(value) {
268
286
  if (!value || isNaN(Number(value))) {
269
287
  return false;
@@ -359,25 +377,25 @@ var ge = (options) => {
359
377
  };
360
378
  };
361
379
  var inc = (options) => {
362
- const opt = options?.[0] ?? optionsRequired("inc");
380
+ const opt = options?.[0] ?? optionsRequired("add");
363
381
  if (!validateNumberString(opt)) {
364
- optionMustBeNumber("inc");
382
+ optionMustBeNumber("add");
365
383
  }
366
384
  return (value) => {
367
385
  if (typeof value !== "number") {
368
- valueMustBeNumber("inc");
386
+ valueMustBeNumber("add");
369
387
  }
370
388
  return value + Number(opt);
371
389
  };
372
390
  };
373
391
  var dec = (options) => {
374
- const opt = options?.[0] ?? optionsRequired("dec");
392
+ const opt = options?.[0] ?? optionsRequired("sub");
375
393
  if (!validateNumberString(opt)) {
376
- optionMustBeNumber("dec");
394
+ optionMustBeNumber("sub");
377
395
  }
378
396
  return (value) => {
379
397
  if (typeof value !== "number") {
380
- valueMustBeNumber("dec");
398
+ valueMustBeNumber("sub");
381
399
  }
382
400
  return value - Number(opt);
383
401
  };
@@ -447,11 +465,11 @@ var clamp = (options) => {
447
465
  var fix = (options) => {
448
466
  const opt = options?.[0] ?? "0";
449
467
  if (!validateNumberString(opt)) {
450
- optionMustBeNumber("fix");
468
+ optionMustBeNumber("toFixed");
451
469
  }
452
470
  return (value) => {
453
471
  if (typeof value !== "number") {
454
- valueMustBeNumber("fix");
472
+ valueMustBeNumber("toFixed");
455
473
  }
456
474
  return value.toFixed(Number(opt));
457
475
  };
@@ -524,19 +542,29 @@ var substr = (options) => {
524
542
  };
525
543
  };
526
544
  var pad = (options) => {
527
- const opt1 = options?.[0] ?? optionsRequired("pad");
545
+ const opt1 = options?.[0] ?? optionsRequired("padStart");
528
546
  if (!validateNumberString(opt1)) {
529
- optionMustBeNumber("pad");
547
+ optionMustBeNumber("padStart");
530
548
  }
531
549
  const opt2 = options?.[1] ?? "0";
532
550
  return (value) => {
533
551
  return String(value).padStart(Number(opt1), opt2);
534
552
  };
535
553
  };
554
+ var padEnd = (options) => {
555
+ const opt1 = options?.[0] ?? optionsRequired("padEnd");
556
+ if (!validateNumberString(opt1)) {
557
+ optionMustBeNumber("padEnd");
558
+ }
559
+ const opt2 = options?.[1] ?? " ";
560
+ return (value) => {
561
+ return String(value).padEnd(Number(opt1), opt2);
562
+ };
563
+ };
536
564
  var rep = (options) => {
537
- const opt = options?.[0] ?? optionsRequired("rep");
565
+ const opt = options?.[0] ?? optionsRequired("repeat");
538
566
  if (!validateNumberString(opt)) {
539
- optionMustBeNumber("rep");
567
+ optionMustBeNumber("repeat");
540
568
  }
541
569
  return (value) => {
542
570
  return String(value).repeat(Number(opt));
@@ -708,6 +736,11 @@ var defaults = (options, literals) => {
708
736
  return value;
709
737
  };
710
738
  };
739
+ var coalesce = (options, literals) => {
740
+ const opt = options?.[0] ?? optionsRequired("coalesce");
741
+ const fallback = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
742
+ return (value) => value ?? fallback;
743
+ };
711
744
  var boolean = (_options) => {
712
745
  return (value) => {
713
746
  return Boolean(value);
@@ -736,24 +769,25 @@ var builtinFilters = {
736
769
  "le": le,
737
770
  "gt": gt,
738
771
  "ge": ge,
739
- "inc": inc,
740
- "dec": dec,
772
+ "add": inc,
773
+ "sub": dec,
741
774
  "mul": mul,
742
775
  "div": div,
743
776
  "mod": mod,
744
777
  "abs": abs,
745
778
  "clamp": clamp,
746
- "fix": fix,
779
+ "toFixed": fix,
747
780
  "locale": locale,
748
- "uc": uc,
749
- "lc": lc,
750
- "cap": cap,
781
+ "upper": uc,
782
+ "lower": lc,
783
+ "capitalize": cap,
751
784
  "trim": trim,
752
785
  "slice": slice,
753
786
  "substr": substr,
754
- "pad": pad,
755
- "rep": rep,
756
- "rev": rev,
787
+ "padStart": pad,
788
+ "padEnd": padEnd,
789
+ "repeat": rep,
790
+ "reverse": rev,
757
791
  "truncate": truncate,
758
792
  "join": join,
759
793
  "int": int,
@@ -771,10 +805,11 @@ var builtinFilters = {
771
805
  "falsy": falsy,
772
806
  "truthy": truthy,
773
807
  "defaults": defaults,
808
+ "coalesce": coalesce,
774
809
  "boolean": boolean,
775
810
  "number": number,
776
811
  "string": string,
777
- "null": _null
812
+ "nullIfEmpty": _null
778
813
  };
779
814
  var outputBuiltinFilters = builtinFilters;
780
815
  var builtinFilterMeta = {
@@ -787,26 +822,27 @@ var builtinFilterMeta = {
787
822
  gt: { description: "\u3088\u308A\u5927\u304D\u3044\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
788
823
  ge: { description: "\u4EE5\u4E0A\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
789
824
  // 算術
790
- inc: { description: "\u52A0\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
791
- dec: { description: "\u6E1B\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
825
+ add: { description: "\u52A0\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
826
+ sub: { description: "\u6E1B\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
792
827
  mul: { description: "\u4E57\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
793
828
  div: { description: "\u9664\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
794
829
  mod: { description: "\u5270\u4F59", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
795
830
  abs: { description: "\u7D76\u5BFE\u5024", hasArgs: false, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 0 },
796
831
  clamp: { description: "\u7BC4\u56F2\u5185\u306B\u4E38\u3081\u308B (min,max)", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 2, maxArgs: 2, argTypes: ["number", "number"] },
797
832
  // 数値フォーマット
798
- fix: { description: "\u56FA\u5B9A\u5C0F\u6570\u70B9\u8868\u8A18", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
833
+ toFixed: { description: "\u56FA\u5B9A\u5C0F\u6570\u70B9\u8868\u8A18", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
799
834
  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"] },
800
835
  // 文字列
801
- uc: { description: "\u5927\u6587\u5B57\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
802
- lc: { description: "\u5C0F\u6587\u5B57\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
803
- cap: { description: "\u5148\u982D\u6587\u5B57\u3092\u5927\u6587\u5B57\u306B", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
836
+ upper: { description: "\u5927\u6587\u5B57\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
837
+ lower: { description: "\u5C0F\u6587\u5B57\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
838
+ capitalize: { description: "\u5148\u982D\u6587\u5B57\u3092\u5927\u6587\u5B57\u306B", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
804
839
  trim: { description: "\u524D\u5F8C\u306E\u7A7A\u767D\u3092\u524A\u9664", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
805
840
  slice: { description: "\u90E8\u5206\u6587\u5B57\u5217 (start[,end])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
806
841
  substr: { description: "\u90E8\u5206\u6587\u5B57\u5217 (pos,len)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
807
- pad: { description: "\u30D1\u30C7\u30A3\u30F3\u30B0 (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
808
- rep: { description: "\u7E70\u308A\u8FD4\u3057 (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
809
- rev: { description: "\u6587\u5B57\u9806\u3092\u53CD\u8EE2", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
842
+ padStart: { description: "\u5148\u982D\u3092\u57CB\u3081\u308B (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
843
+ padEnd: { description: "\u672B\u5C3E\u3092\u57CB\u3081\u308B (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
844
+ repeat: { description: "\u7E70\u308A\u8FD4\u3057 (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
845
+ reverse: { description: "\u6587\u5B57\u9806\u3092\u53CD\u8EE2", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
810
846
  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"] },
811
847
  join: { description: "\u914D\u5217\u3092\u9023\u7D50 ([separator])", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
812
848
  // 数値パース・丸め
@@ -829,10 +865,11 @@ var builtinFilterMeta = {
829
865
  falsy: { description: "\u507D\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
830
866
  truthy: { description: "\u771F\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
831
867
  defaults: { description: "\u507D\u5024\u306E\u5834\u5408\u30C7\u30D5\u30A9\u30EB\u30C8\u5024", hasArgs: true, resultType: "passthrough", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
868
+ coalesce: { description: "null / undefined \u306E\u5834\u5408\u30C7\u30D5\u30A9\u30EB\u30C8\u5024", hasArgs: true, resultType: "passthrough", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
832
869
  boolean: { description: "\u30D6\u30FC\u30EB\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
833
870
  number: { description: "\u6570\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
834
871
  string: { description: "\u6587\u5B57\u5217\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
835
- null: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
872
+ nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
836
873
  };
837
874
  var STRUCTURAL_BINDING_TYPE_SET = /* @__PURE__ */ new Set([
838
875
  "if",
@@ -877,7 +914,7 @@ for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
877
914
  Object.freeze(tmpIndexByIndexName);
878
915
  var STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
879
916
  var STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
880
- var STATE_UPDATED_CALLBACK_NAME = "$updatedCallback";
917
+ var STATE_UPDATED_CALLBACK_NAME = "$renderedCallback";
881
918
  var STATE_ERROR_CALLBACK_NAME = "$errorCallback";
882
919
  var WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
883
920
  var STATE_BINDABLES_NAME = "$bindables";
@@ -886,7 +923,7 @@ var STATE_COMMAND_TOKENS_NAME = "$commandTokens";
886
923
  var STATE_COMMAND_NAMESPACE_NAME = "$command";
887
924
  var STATE_EVENT_TOKENS_NAME = "$eventTokens";
888
925
  var STATE_ON_NAME = "$on";
889
- var STATE_STREAMS_NAME = "$streams";
926
+ var STATE_STREAMS_NAME = "$stream";
890
927
  var STATE_WATCH_NAME = "$watch";
891
928
  var STATE_SCAN_NAME = "$scan";
892
929
  var STATE_RECURSION_NAME = "$recursion";
@@ -923,6 +960,7 @@ function getWcsManifest() {
923
960
  elseKeyword: ELSE_KEYWORD,
924
961
  spread: SPREAD_PROP,
925
962
  eventPropertyPrefix: EVENT_PROP_PREFIX,
963
+ explicitPropertyPrefix: DELIMITER,
926
964
  propNamespaces: {
927
965
  eventToken: EVENT_TOKEN_NAMESPACE,
928
966
  command: COMMAND_NAMESPACE,
@@ -935,6 +973,7 @@ function getWcsManifest() {
935
973
  // 実装(Record のキー)から自動導出。手リストを持たない=ドリフトの構造的排除。
936
974
  filters: Object.keys(outputBuiltinFilters),
937
975
  filterMeta: builtinFilterMeta,
976
+ filterAliases: builtinFilterAliases,
938
977
  reservedLifecycle: [
939
978
  STATE_CONNECTED_CALLBACK_NAME,
940
979
  STATE_DISCONNECTED_CALLBACK_NAME,
@@ -961,6 +1000,9 @@ function getWcsManifest() {
961
1000
  }
962
1001
 
963
1002
  // src/service/completionData.ts
1003
+ function canonicalFilterName(name) {
1004
+ return Object.prototype.hasOwnProperty.call(builtinFilterAliases, name) ? builtinFilterAliases[name] : name;
1005
+ }
964
1006
  var BUILTIN_FILTERS = Object.entries(builtinFilterMeta).map(
965
1007
  ([name, meta]) => ({ name, ...meta })
966
1008
  );
@@ -1136,7 +1178,7 @@ function coversSuffix(spec, familySuffix, suffix) {
1136
1178
  }
1137
1179
 
1138
1180
  // src/service/stateAnalyzer.ts
1139
- var RESERVED_STREAMS_KEY = "$streams";
1181
+ var RESERVED_STREAMS_KEYS = /* @__PURE__ */ new Set(["$stream", "$streams"]);
1140
1182
  var RESERVED_COMMAND_TOKENS_KEY = "$commandTokens";
1141
1183
  var RESERVED_EVENT_TOKENS_KEY = "$eventTokens";
1142
1184
  var RESERVED_LIST_KEYS_KEY = "$listKeys";
@@ -1508,7 +1550,7 @@ function findNonObjectDeclaration(scriptContent, key, rejectArray = false) {
1508
1550
  return span;
1509
1551
  }
1510
1552
  function collectReservedKeyPaths(prop, paths, pendingStreamValues, pendingListKeys) {
1511
- if (prop.name === RESERVED_STREAMS_KEY && prop.kind === "data" && prop.value && isObjectLiteral(prop.value)) {
1553
+ if (RESERVED_STREAMS_KEYS.has(prop.name) && prop.kind === "data" && prop.value && isObjectLiteral(prop.value)) {
1512
1554
  const entries = parseTopLevelProperties(extractObjectContent(prop.value));
1513
1555
  for (const entry of entries) {
1514
1556
  if (entry.kind !== "data" || entry.name.startsWith("$")) continue;
@@ -2637,6 +2679,8 @@ var ja = {
2637
2679
  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`,
2638
2680
  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`,
2639
2681
  tagMemberUnknown: (prop, tag) => `"${prop}" \u306F <${tag}> \u306E wcBindable \u30E1\u30F3\u30D0\u30FC\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u672A\u77E5\u30E1\u30F3\u30D0\u30FC\u3078\u306E\u30D0\u30A4\u30F3\u30C9\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059\uFF09`,
2682
+ nameAlias: (written, canonical) => `"${written}" \u306F "${canonical}" \u306E\u65E7\u540D\u3067\u3059\u30023.x \u306E\u9593\u306F\u52D5\u304D\u307E\u3059\u304C 4.0 \u3067\u5916\u308C\u308B\u306E\u3067\u3001"${canonical}" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.2\uFF09`,
2683
+ onPrefixedMember: (member, tag) => `"${member}" \u306F <${tag}> \u306E\u30E1\u30F3\u30D0\u30FC\u3067\u3059\u304C\u3001"on" \u3067\u59CB\u307E\u308B\u540D\u524D\u306F\u30A4\u30D9\u30F3\u30C8\u675F\u7E1B\u306B\u306A\u308A\uFF08"${member.slice(2)}" \u30A4\u30D9\u30F3\u30C8\u3092\u5F85\u3064\uFF09\u3001\u5024\u306F\u5C4A\u304D\u307E\u305B\u3093\u3002\u30D7\u30ED\u30D1\u30C6\u30A3\u3068\u3057\u3066\u675F\u7E1B\u3059\u308B\u306B\u306F ".${member}:" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.1\uFF09`,
2640
2684
  tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
2641
2685
  spreadNoBindable: (tag) => `'...'\uFF08spread\uFF09\u306F <${tag}> \u306B\u6709\u52B9\u306A wcBindable \u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059 \u2014 \u3053\u306E\u30BF\u30B0\u306F\u5BA3\u8A00\u3092\u6301\u305F\u306A\u3044\u305F\u3081\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u30A8\u30E9\u30FC\u3092\u9001\u51FA\u3057\u307E\u3059`,
2642
2686
  tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken \u306E\u30AD\u30FC "${name}" \u306F <${tag}> \u306E wcBindable \u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u751F DOM \u30A4\u30D9\u30F3\u30C8\u540D\u306F\u767A\u706B\u3057\u307E\u305B\u3093 \u2014 \u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
@@ -2826,6 +2870,8 @@ var en = {
2826
2870
  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}).`,
2827
2871
  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.`,
2828
2872
  tagMemberUnknown: (prop, tag) => `"${prop}" is not a wcBindable member of <${tag}> (bindings to unknown members are silently ignored)`,
2873
+ nameAlias: (written, canonical) => `"${written}" is the old name of "${canonical}". It works through 3.x and goes in 4.0 \u2014 write "${canonical}" (@wcstack/state 3.2)`,
2874
+ onPrefixedMember: (member, tag) => `"${member}" is a member of <${tag}>, but a name starting with "on" makes an event binding (it listens for a "${member.slice(2)}" event) and the value never arrives. Write ".${member}:" to bind the property (@wcstack/state 3.1)`,
2829
2875
  tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
2830
2876
  spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
2831
2877
  tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken key "${name}" is not a wcBindable property of <${tag}>. Raw DOM event names never fire \u2014 use the property name (declared: ${declared})`,
@@ -3150,7 +3196,6 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
3150
3196
  structuralTemplates ??= collectStructuralTemplates(html, attrName);
3151
3197
  return structuralTemplates;
3152
3198
  };
3153
- const filterNameSet = new Set(BUILTIN_FILTERS.map((f) => f.name));
3154
3199
  for (const attr of attrs) {
3155
3200
  const bindings = splitBindingExpressions(attr.value);
3156
3201
  const nonEmptyCount = bindings.filter((b) => b.trim().length > 0).length;
@@ -3484,7 +3529,17 @@ function parseFilterSegments(expr, segments, searchStart) {
3484
3529
  }
3485
3530
  function validateFilterUsage(filter, bindingStart, msgs) {
3486
3531
  const diagnostics = [];
3487
- const info = filterMap.get(filter.name);
3532
+ const canonical = canonicalFilterName(filter.name);
3533
+ const info = filterMap.get(canonical);
3534
+ if (info && canonical !== filter.name) {
3535
+ diagnostics.push({
3536
+ code: WcsDiagnosticCode.NameAlias,
3537
+ start: bindingStart + filter.offset,
3538
+ end: bindingStart + filter.offset + filter.name.length,
3539
+ message: msgs.nameAlias(filter.name, canonical),
3540
+ severity: "info"
3541
+ });
3542
+ }
3488
3543
  if (!info) {
3489
3544
  diagnostics.push({
3490
3545
  code: WcsDiagnosticCode.FilterUnknown,
@@ -4114,8 +4169,17 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
4114
4169
  }
4115
4170
  for (let i = 1; i < parts.length; i++) {
4116
4171
  const filterName = parts[i].trim().replace(/\(.*$/, "");
4117
- if (filterName && !filterNameSet.has(filterName)) {
4118
- const filterOffset = item.expression.indexOf(parts[i]);
4172
+ const filterOffset = item.expression.indexOf(parts[i]) + (parts[i].length - parts[i].trimStart().length);
4173
+ const canonical = canonicalFilterName(filterName);
4174
+ if (filterName && canonical !== filterName && filterNameSet.has(canonical)) {
4175
+ diagnostics.push({
4176
+ code: WcsDiagnosticCode.NameAlias,
4177
+ start: item.exprStart + filterOffset,
4178
+ end: item.exprStart + filterOffset + filterName.length,
4179
+ message: msgs.nameAlias(filterName, canonical),
4180
+ severity: "info"
4181
+ });
4182
+ } else if (filterName && !filterNameSet.has(filterName)) {
4119
4183
  diagnostics.push({
4120
4184
  code: WcsDiagnosticCode.FilterUnknown,
4121
4185
  start: item.exprStart + filterOffset,
@@ -4150,6 +4214,10 @@ var ELSE_KEYWORD2 = "else";
4150
4214
  var SPREAD_PROP2 = "...";
4151
4215
  var EVENT_PROP_PREFIX2 = "on";
4152
4216
  var EVENT_TOKEN_NAMESPACE2 = "eventToken";
4217
+ var COMMAND_NAMESPACE2 = "command";
4218
+ var CLASS_NAMESPACE2 = "class";
4219
+ var ATTR_NAMESPACE2 = "attr";
4220
+ var STYLE_NAMESPACE2 = "style";
4153
4221
  var INDEX_PARAM_PREFIX2 = "$";
4154
4222
  var tmpIndexByIndexName2 = {};
4155
4223
  for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
@@ -4474,6 +4542,13 @@ function parseStatePart(statePart) {
4474
4542
  };
4475
4543
  }
4476
4544
  var KEYWORDS_WITHOUT_MODIFIERS = /* @__PURE__ */ new Set([ELSE_KEYWORD2, "if", "elseif", "for", SPREAD_PROP2]);
4545
+ var EXPLICIT_PROPERTY_REJECTED_HEADS = /* @__PURE__ */ new Set([
4546
+ CLASS_NAMESPACE2,
4547
+ ATTR_NAMESPACE2,
4548
+ STYLE_NAMESPACE2,
4549
+ COMMAND_NAMESPACE2,
4550
+ EVENT_TOKEN_NAMESPACE2
4551
+ ]);
4477
4552
  function splitBindTexts(bindText) {
4478
4553
  return splitOutsideQuotes(bindText, BINDING_SEPARATOR2);
4479
4554
  }
@@ -4542,6 +4617,19 @@ function parseBindTextsForElement(bindText) {
4542
4617
  } else {
4543
4618
  const stateResult = parseStatePart(statePart);
4544
4619
  const propResult = parsePropPart(propPart);
4620
+ if (propResult.propSegments[0] === "" && propResult.propSegments.length > 1) {
4621
+ const propSegments = propResult.propSegments.slice(1);
4622
+ if (propSegments.includes("") || EXPLICIT_PROPERTY_REJECTED_HEADS.has(propSegments[0])) {
4623
+ raiseError2(`[wcs/binding-syntax] "${propPart}": a leading "." binds an element property by name \u2014 write a non-empty property that is not a namespace (${[...EXPLICIT_PROPERTY_REJECTED_HEADS].join(", ")}).${LINT_HINT}`);
4624
+ }
4625
+ return {
4626
+ ...propResult,
4627
+ propName: propSegments.join(DELIMITER2),
4628
+ propSegments,
4629
+ ...stateResult,
4630
+ bindingType: "prop"
4631
+ };
4632
+ }
4545
4633
  if (propResult.propSegments[0] === EVENT_TOKEN_NAMESPACE2) {
4546
4634
  return {
4547
4635
  ...propResult,
@@ -6057,10 +6145,28 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
6057
6145
  const hashIndex = property.indexOf("#");
6058
6146
  const modifiers = hashIndex === -1 ? "" : property.slice(hashIndex + 1);
6059
6147
  property = hashIndex === -1 ? property : property.slice(0, hashIndex);
6148
+ const explicit = property.startsWith(".") && property !== "...";
6149
+ if (explicit) {
6150
+ property = property.slice(1);
6151
+ if (/^(class|style|attr|command|eventToken)(\.|$)/.test(property)) return;
6152
+ }
6060
6153
  if (property === "...") return;
6061
6154
  if (STRUCTURAL_DIRECTIVES2.has(property)) return;
6062
6155
  if (/^(class|style|attr)\./.test(property)) return;
6063
- if (/^on\w/.test(property)) return;
6156
+ if (!explicit && /^on\w/.test(property)) {
6157
+ if (contract.properties.includes(property) || property in contract.inputs) {
6158
+ diagnostics.push({
6159
+ code: WcsDiagnosticCode.OnPrefixedMember,
6160
+ start,
6161
+ end,
6162
+ severity: "warning",
6163
+ tag: tagName,
6164
+ member: property,
6165
+ message: msgs.onPrefixedMember(property, tagName)
6166
+ });
6167
+ }
6168
+ return;
6169
+ }
6064
6170
  const inputNames = Object.keys(contract.inputs);
6065
6171
  if (property.startsWith("command.")) {
6066
6172
  const name = property.slice("command.".length);
@@ -6831,7 +6937,8 @@ var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "trackDepende
6831
6937
  var UNSUPPORTED_API_SITE = {
6832
6938
  $resolve: "resolve",
6833
6939
  $postUpdate: "postUpdate",
6834
- $trackDependency: "trackDependency"
6940
+ $trackDependency: "trackDependency",
6941
+ $dependOn: "trackDependency"
6835
6942
  };
6836
6943
  var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
6837
6944
  var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
@@ -9791,16 +9898,16 @@ pp$5.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, forInit)
9791
9898
  if (prec != null && (!forInit || this.type !== types$1._in)) {
9792
9899
  if (prec > minPrec) {
9793
9900
  var logical = this.type === types$1.logicalOR || this.type === types$1.logicalAND;
9794
- var coalesce = this.type === types$1.coalesce;
9795
- if (coalesce) {
9901
+ var coalesce2 = this.type === types$1.coalesce;
9902
+ if (coalesce2) {
9796
9903
  prec = types$1.logicalAND.binop;
9797
9904
  }
9798
9905
  var op = this.value;
9799
9906
  this.next();
9800
9907
  var startPos = this.start, startLoc = this.startLoc;
9801
9908
  var right = this.parseExprOp(this.parseMaybeUnary(null, false, false, forInit), startPos, startLoc, prec, forInit);
9802
- var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce);
9803
- if (logical && this.type === types$1.coalesce || coalesce && (this.type === types$1.logicalOR || this.type === types$1.logicalAND)) {
9909
+ var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce2);
9910
+ if (logical && this.type === types$1.coalesce || coalesce2 && (this.type === types$1.logicalOR || this.type === types$1.logicalAND)) {
9804
9911
  this.raiseRecoverable(this.start, "Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses");
9805
9912
  }
9806
9913
  return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, forInit);
@@ -13051,8 +13158,9 @@ function parse3(input, options) {
13051
13158
  // src/service/scriptAst.ts
13052
13159
  var PREFIX = "(async function* () {\n";
13053
13160
  var SUFFIX = "\n})";
13054
- var PATH_ARG_APIS = /* @__PURE__ */ new Set(["$getAll", "$resolve", "$trackDependency"]);
13055
- var UNTRACK_API = "$untrackDependency";
13161
+ var PATH_ARG_APIS = /* @__PURE__ */ new Set(["$getAll", "$resolve", "$dependOn", "$trackDependency"]);
13162
+ var TRACK_APIS = /* @__PURE__ */ new Set(["$dependOn", "$trackDependency"]);
13163
+ var UNTRACK_APIS = /* @__PURE__ */ new Set(["$untracked", "$untrackDependency"]);
13056
13164
  function collectGetterReads(body) {
13057
13165
  let program;
13058
13166
  try {
@@ -13277,7 +13385,7 @@ function visitCall(node, scope, out) {
13277
13385
  const { segments, base } = resolveChain(callee);
13278
13386
  if (isThisRoot(base, scope) && segments.length === 1 && segments[0].text !== null) {
13279
13387
  const api = segments[0].text;
13280
- if (api === UNTRACK_API) return;
13388
+ if (UNTRACK_APIS.has(api)) return;
13281
13389
  if (PATH_ARG_APIS.has(api)) {
13282
13390
  const first = node.arguments[0];
13283
13391
  const path = first !== void 0 && first.type !== "SpreadElement" ? literalString2(first) : null;
@@ -13285,7 +13393,7 @@ function visitCall(node, scope, out) {
13285
13393
  out.push({
13286
13394
  path,
13287
13395
  chain: null,
13288
- form: api === "$trackDependency" ? "track" : "api",
13396
+ form: TRACK_APIS.has(api) ? "track" : "api",
13289
13397
  callee: false,
13290
13398
  written: false,
13291
13399
  start: node.start - PREFIX.length,
@@ -13468,7 +13576,35 @@ function buildReferenceIndex(html, options = {}) {
13468
13576
  }
13469
13577
 
13470
13578
  // src/service/semanticValidator.ts
13471
- var STATE_UPDATED_CALLBACK = "$updatedCallback";
13579
+ var STATE_UPDATED_CALLBACKS = /* @__PURE__ */ new Set(["$renderedCallback", "$updatedCallback"]);
13580
+ var OLD_API_NAMES = { $trackDependency: "$dependOn", $untrackDependency: "$untracked" };
13581
+ var OLD_DECLARATION_KEYS = { $streams: "$stream", $updatedCallback: "$renderedCallback" };
13582
+ var OLD_API_CALL = /\.\s*(\$trackDependency|\$untrackDependency)\b/g;
13583
+ var OLD_DECLARATION_KEY = /(^|[{,\s])(\$streams|\$updatedCallback)(?=\s*[:(])/g;
13584
+ function validateNameAliases(script, scriptStart, locale2) {
13585
+ const msgs = getMessages(locale2);
13586
+ const scan = blankComments(script);
13587
+ const out = [];
13588
+ const push2 = (name, canonical, offset2) => {
13589
+ out.push({
13590
+ code: WcsDiagnosticCode.NameAlias,
13591
+ start: scriptStart + offset2,
13592
+ end: scriptStart + offset2 + name.length,
13593
+ message: msgs.nameAlias(name, canonical),
13594
+ severity: "info"
13595
+ });
13596
+ };
13597
+ OLD_API_CALL.lastIndex = 0;
13598
+ let match;
13599
+ while ((match = OLD_API_CALL.exec(scan)) !== null) {
13600
+ push2(match[1], OLD_API_NAMES[match[1]], match.index + match[0].length - match[1].length);
13601
+ }
13602
+ OLD_DECLARATION_KEY.lastIndex = 0;
13603
+ while ((match = OLD_DECLARATION_KEY.exec(scan)) !== null) {
13604
+ push2(match[2], OLD_DECLARATION_KEYS[match[2]], match.index + match[1].length);
13605
+ }
13606
+ return out;
13607
+ }
13472
13608
  var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
13473
13609
  function validateIndexArity(script, scriptStart, locale2) {
13474
13610
  const msgs = getMessages(locale2);
@@ -13650,13 +13786,13 @@ var PATH_TEST_LITERAL = /(?:\.\s*(?:includes|indexOf)\s*\(\s*|[!=]==\s*)(["'])((
13650
13786
  function validateUpdatedCallbackDemand(html, stateTagName, bindAttrName, locale2) {
13651
13787
  const blocks = parseWcsScriptBlocks(html, stateTagName);
13652
13788
  if (blocks.length === 0) return [];
13653
- const hasCallback = blocks.some((block) => block.content.includes(STATE_UPDATED_CALLBACK));
13789
+ const hasCallback = blocks.some((block) => [...STATE_UPDATED_CALLBACKS].some((name) => block.content.includes(name)));
13654
13790
  if (!hasCallback) return [];
13655
13791
  const msgs = getMessages(locale2);
13656
13792
  const boundPaths = collectBoundPaths(html, stateTagName, bindAttrName);
13657
13793
  const out = [];
13658
13794
  for (const block of blocks) {
13659
- const callback = analyzeCallableBodies(block.content).find((entry) => entry.name === STATE_UPDATED_CALLBACK && entry.kind === "method");
13795
+ const callback = analyzeCallableBodies(block.content).find((entry) => STATE_UPDATED_CALLBACKS.has(entry.name) && entry.kind === "method");
13660
13796
  if (callback === void 0) continue;
13661
13797
  if (block.mountPath !== null) continue;
13662
13798
  const declared = new Set(analyzeStatePaths(block.content).map((p) => p.path));
@@ -13703,6 +13839,7 @@ function validateSemantics(html, stateTagName = "wcs-state", locale2, bindAttrNa
13703
13839
  const getNestedWriteRoots = () => nestedWriteRoots ??= collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks);
13704
13840
  for (const block of blocks) {
13705
13841
  out.push(...validateIndexArity(block.content, block.contentStart, locale2));
13842
+ out.push(...validateNameAliases(block.content, block.contentStart, locale2));
13706
13843
  out.push(...validateGetterCycles(block.content, block.contentStart, locale2));
13707
13844
  out.push(...validateGetterUntrackedReads(block.content, block.contentStart, getNestedWriteRoots, locale2));
13708
13845
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/lint",
3
- "version": "3.0.0",
3
+ "version": "3.2.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": {