tutuca 0.9.48 → 0.9.49

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.
@@ -3344,11 +3344,12 @@ class ComponentDocs {
3344
3344
  }
3345
3345
 
3346
3346
  class LintFinding {
3347
- constructor({ id, level, info, context = {} }) {
3347
+ constructor({ id, level, info, context = {}, suggestion = null }) {
3348
3348
  this.id = id;
3349
3349
  this.level = level;
3350
3350
  this.info = info;
3351
3351
  this.context = context;
3352
+ this.suggestion = suggestion;
3352
3353
  }
3353
3354
  }
3354
3355
 
@@ -6633,10 +6634,7 @@ class LinterCtx {
6633
6634
  a.quote = quote;
6634
6635
  const dup = this.currentAttrs.find((x) => x.name === a.name);
6635
6636
  if (dup) {
6636
- this.report(HTML_DUPLICATE_ATTRIBUTE, LEVEL_WARN, a.nameStart, {
6637
- name: a.name,
6638
- firstAt: dup.nameStart
6639
- });
6637
+ this.report(HTML_DUPLICATE_ATTRIBUTE, LEVEL_WARN, a.nameStart, { name: a.name, firstAt: dup.nameStart }, { kind: "remove", what: `the duplicate '${a.name}' attribute` });
6640
6638
  } else {
6641
6639
  this.currentAttrs.push(a);
6642
6640
  }
@@ -6686,12 +6684,12 @@ class LinterCtx {
6686
6684
  }
6687
6685
  }
6688
6686
  if (firstNonWs < 0) {
6689
- this.report(HTML_SELF_CLOSING_END_TAG, LEVEL_WARN, start, { tag: name });
6687
+ this.report(HTML_SELF_CLOSING_END_TAG, LEVEL_WARN, start, { tag: name }, { kind: "rewrite", from: `</${name}/>`, to: `</${name}>` });
6690
6688
  } else {
6691
- this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name });
6689
+ this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name }, { kind: "remove", what: `attributes from </${name}>` });
6692
6690
  }
6693
6691
  } else {
6694
- this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name });
6692
+ this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name }, { kind: "remove", what: `attributes from </${name}>` });
6695
6693
  }
6696
6694
  }
6697
6695
  this.handleEndTag(name, start);
@@ -6724,14 +6722,15 @@ class LinterCtx {
6724
6722
  return this.currentNamespace() !== NS.html;
6725
6723
  }
6726
6724
  onend() {}
6727
- report(id, level, offset, info) {
6725
+ report(id, level, offset, info, suggestion = null) {
6728
6726
  this.findingCount++;
6729
6727
  const { line, column } = offsetToLineCol(this.lineStarts, offset);
6730
6728
  this.onFinding({
6731
6729
  id,
6732
6730
  level,
6733
6731
  info,
6734
- location: { start: offset, end: offset + (info.tag?.length ?? 0), line, column }
6732
+ location: { start: offset, end: offset + (info.tag?.length ?? 0), line, column },
6733
+ suggestion
6735
6734
  });
6736
6735
  }
6737
6736
  currentNode() {
@@ -6840,17 +6839,11 @@ class LinterCtx {
6840
6839
  const ns = this.currentNamespace();
6841
6840
  if (ns === NS.html) {
6842
6841
  if (raw !== name) {
6843
- this.report(HTML_TAG_NAME_HAS_UPPERCASE, LEVEL_ERROR, start, {
6844
- raw,
6845
- lowercased: name
6846
- });
6842
+ this.report(HTML_TAG_NAME_HAS_UPPERCASE, LEVEL_ERROR, start, { raw, lowercased: name }, { kind: "rewrite", from: `<${raw}>`, to: `<${name}>` });
6847
6843
  }
6848
6844
  } else if (ns === NS.svg) {
6849
6845
  if (raw !== raw.toLowerCase() && !this.svgCamelElements.has(raw)) {
6850
- this.report(HTML_SVG_TAG_WILL_LOWERCASE, LEVEL_ERROR, start, {
6851
- raw,
6852
- lowercased: name
6853
- });
6846
+ this.report(HTML_SVG_TAG_WILL_LOWERCASE, LEVEL_ERROR, start, { raw, lowercased: name }, { kind: "rewrite", from: `<${raw}>`, to: `<${name}>` });
6854
6847
  }
6855
6848
  }
6856
6849
  const targetNs = ns !== NS.html ? ns : name === "svg" ? NS.svg : name === "math" ? NS.math : NS.html;
@@ -6858,20 +6851,14 @@ class LinterCtx {
6858
6851
  for (const a of this.currentAttrs) {
6859
6852
  const canonical = SVG_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
6860
6853
  if (canonical && a.rawName !== canonical) {
6861
- this.report(HTML_SVG_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
6862
- raw: a.rawName,
6863
- canonical
6864
- });
6854
+ this.report(HTML_SVG_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, { raw: a.rawName, canonical }, { kind: "rewrite", from: a.rawName, to: canonical });
6865
6855
  }
6866
6856
  }
6867
6857
  } else if (targetNs === NS.math) {
6868
6858
  for (const a of this.currentAttrs) {
6869
6859
  const canonical = MATHML_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
6870
6860
  if (canonical && a.rawName !== canonical) {
6871
- this.report(HTML_MATHML_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
6872
- raw: a.rawName,
6873
- canonical
6874
- });
6861
+ this.report(HTML_MATHML_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, { raw: a.rawName, canonical }, { kind: "rewrite", from: a.rawName, to: canonical });
6875
6862
  }
6876
6863
  }
6877
6864
  }
@@ -6986,10 +6973,7 @@ class LinterCtx {
6986
6973
  }
6987
6974
  if (name === "form") {
6988
6975
  if (this.openElementsHas("form") && !this.openElementsHas("template")) {
6989
- this.report(HTML_DUPLICATE_FORM, LEVEL_ERROR, start, {
6990
- tag: raw,
6991
- mode: this.insertionMode
6992
- });
6976
+ this.report(HTML_DUPLICATE_FORM, LEVEL_ERROR, start, { tag: raw, mode: this.insertionMode }, { kind: "remove", what: "the inner <form>" });
6993
6977
  return;
6994
6978
  }
6995
6979
  if (this.hasInButtonScope("p"))
@@ -7551,10 +7535,7 @@ class LinterCtx {
7551
7535
  }
7552
7536
  }
7553
7537
  if (VOID_ELEMENTS.has(name)) {
7554
- this.report(HTML_VOID_ELEMENT_HAS_CLOSE_TAG, LEVEL_WARN, start, {
7555
- tag: name,
7556
- mode: this.insertionMode
7557
- });
7538
+ this.report(HTML_VOID_ELEMENT_HAS_CLOSE_TAG, LEVEL_WARN, start, { tag: name, mode: this.insertionMode }, { kind: "remove", what: `the </${name}>` });
7558
7539
  return;
7559
7540
  }
7560
7541
  if (FORMATTING_ELEMENTS.has(name)) {
@@ -7691,7 +7672,89 @@ var init_htmllinter = __esm(() => {
7691
7672
  ]);
7692
7673
  });
7693
7674
 
7675
+ // tools/core/util/closest-name.js
7676
+ function editDistance(a, b) {
7677
+ if (a === b)
7678
+ return 0;
7679
+ const la = a.length;
7680
+ const lb = b.length;
7681
+ if (la === 0)
7682
+ return lb;
7683
+ if (lb === 0)
7684
+ return la;
7685
+ const prev2 = new Array(lb + 1);
7686
+ const prev1 = new Array(lb + 1);
7687
+ const curr = new Array(lb + 1);
7688
+ for (let j = 0;j <= lb; j++)
7689
+ prev1[j] = j;
7690
+ for (let i = 1;i <= la; i++) {
7691
+ curr[0] = i;
7692
+ const ca = a.charCodeAt(i - 1);
7693
+ for (let j = 1;j <= lb; j++) {
7694
+ const cb = b.charCodeAt(j - 1);
7695
+ const cost = ca === cb ? 0 : 1;
7696
+ let v = Math.min(curr[j - 1] + 1, prev1[j] + 1, prev1[j - 1] + cost);
7697
+ if (i > 1 && j > 1 && ca === b.charCodeAt(j - 2) && a.charCodeAt(i - 2) === cb) {
7698
+ v = Math.min(v, prev2[j - 2] + 1);
7699
+ }
7700
+ curr[j] = v;
7701
+ }
7702
+ for (let j = 0;j <= lb; j++) {
7703
+ prev2[j] = prev1[j];
7704
+ prev1[j] = curr[j];
7705
+ }
7706
+ }
7707
+ return prev1[lb];
7708
+ }
7709
+ function closestName(name, candidates, maxDistance = 2) {
7710
+ if (!name)
7711
+ return null;
7712
+ const lower = name.toLowerCase();
7713
+ let best = null;
7714
+ let bestDist = maxDistance + 1;
7715
+ for (const cand of candidates) {
7716
+ if (cand === name)
7717
+ return null;
7718
+ if (cand.toLowerCase() === lower)
7719
+ return cand;
7720
+ const d = editDistance(name, cand);
7721
+ if (d < bestDist) {
7722
+ best = cand;
7723
+ bestDist = d;
7724
+ }
7725
+ }
7726
+ return bestDist <= maxDistance ? best : null;
7727
+ }
7728
+
7694
7729
  // tools/core/lint-check.js
7730
+ function collectProtoMethodNames(proto) {
7731
+ const out = [];
7732
+ let cursor = proto;
7733
+ while (cursor && cursor !== Object.prototype) {
7734
+ for (const key of Object.getOwnPropertyNames(cursor)) {
7735
+ if (key === "constructor")
7736
+ continue;
7737
+ out.push(key);
7738
+ }
7739
+ cursor = Object.getPrototypeOf(cursor);
7740
+ }
7741
+ return out;
7742
+ }
7743
+ function scopeKeysAlong(scope, mapKey) {
7744
+ const out = [];
7745
+ for (let cursor = scope;cursor; cursor = cursor.parent) {
7746
+ const map = cursor[mapKey];
7747
+ if (!map)
7748
+ continue;
7749
+ for (const key of Object.keys(map))
7750
+ out.push(key);
7751
+ }
7752
+ return out;
7753
+ }
7754
+ function replaceNameSuggestion(name, candidates) {
7755
+ const close = closestName(name, candidates);
7756
+ return close ? { kind: "replace-name", from: name, to: close } : null;
7757
+ }
7695
7758
  function checkComponent(Comp, lx = new LintContext) {
7696
7759
  return lx.push({ componentName: Comp.name }, () => {
7697
7760
  const referencedAlters = new Set;
@@ -7717,7 +7780,7 @@ function checkView(lx, view, Comp, referencedAlters) {
7717
7780
  function checkHtmlStructure(lx, view) {
7718
7781
  if (typeof view.rawView !== "string" || !view.rawView)
7719
7782
  return;
7720
- lintHtml(view.rawView, (f) => lx.report(f.id, { ...f.info, location: f.location }, f.level), HTML_LINT_OPTS);
7783
+ lintHtml(view.rawView, (f) => lx.report(f.id, { ...f.info, location: f.location }, f.level, f.suggestion ?? null), HTML_LINT_OPTS);
7721
7784
  }
7722
7785
  function checkParseIssues(lx, view) {
7723
7786
  const issues = view.ctx.parseIssues;
@@ -7727,12 +7790,19 @@ function checkParseIssues(lx, view) {
7727
7790
  const id = PARSE_ISSUE_KIND_TO_LINT_ID[kind];
7728
7791
  if (!id)
7729
7792
  continue;
7730
- lx.error(id, info);
7731
- const known = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
7732
- if (known && info.name?.startsWith("@")) {
7733
- const suggestion = info.name.slice(1);
7734
- if (known.has(suggestion))
7735
- lx.hint(MAYBE_DROP_AT_PREFIX, { ...info, suggestion });
7793
+ const atPrefixKnown = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
7794
+ const isAtPrefixedTypo = atPrefixKnown && info.name?.startsWith("@") && atPrefixKnown.has(info.name.slice(1));
7795
+ let suggestion = null;
7796
+ if (isAtPrefixedTypo) {
7797
+ suggestion = { kind: "drop-prefix", from: info.name, to: info.name.slice(1) };
7798
+ } else {
7799
+ const candidates = PARSE_ISSUE_KIND_TO_KNOWN_NAMES[kind];
7800
+ if (candidates)
7801
+ suggestion = replaceNameSuggestion(info.name, candidates);
7802
+ }
7803
+ lx.error(id, info, suggestion);
7804
+ if (isAtPrefixedTypo) {
7805
+ lx.hint(MAYBE_DROP_AT_PREFIX, { ...info, suggestion: info.name.slice(1) }, { kind: "drop-prefix", from: info.name, to: info.name.slice(1) });
7736
7806
  }
7737
7807
  }
7738
7808
  }
@@ -7745,11 +7815,7 @@ function checkMacroCallArgs(lx, view, Comp) {
7745
7815
  const { defaults } = macro;
7746
7816
  for (const argName in macroNode.attrs) {
7747
7817
  if (!(argName in defaults)) {
7748
- lx.error(UNKNOWN_MACRO_ARG, {
7749
- name: argName,
7750
- macroName: macroNode.name,
7751
- tag: `x:${macroNode.name}`
7752
- });
7818
+ lx.error(UNKNOWN_MACRO_ARG, { name: argName, macroName: macroNode.name, tag: `x:${macroNode.name}` }, replaceNameSuggestion(argName, Object.keys(defaults)));
7753
7819
  }
7754
7820
  }
7755
7821
  }
@@ -7771,8 +7837,9 @@ function walkForRenderIt(lx, node, loopDepth) {
7771
7837
  return;
7772
7838
  switch (node.constructor.name) {
7773
7839
  case "RenderItNode":
7774
- if (loopDepth === 0)
7775
- lx.error(RENDER_IT_OUTSIDE_OF_LOOP, { node });
7840
+ if (loopDepth === 0) {
7841
+ lx.error(RENDER_IT_OUTSIDE_OF_LOOP, { node }, { kind: "wrap", from: "<x render-it>", to: "<x render-each>" });
7842
+ }
7776
7843
  return;
7777
7844
  case "EachNode":
7778
7845
  walkForRenderIt(lx, node.node, loopDepth + 1);
@@ -7802,13 +7869,14 @@ function checkEventModifiers(lx, view) {
7802
7869
  const modWrappers = MOD_WRAPPERS_BY_EVENT[name] ?? NO_WRAPPERS;
7803
7870
  for (const modifier of modifiers) {
7804
7871
  if (modWrappers[modifier] === undefined) {
7872
+ const close = closestName(modifier, Object.keys(modWrappers));
7805
7873
  lx.error(UNKNOWN_EVENT_MODIFIER, {
7806
7874
  name,
7807
7875
  modifier,
7808
7876
  handler,
7809
7877
  event,
7810
7878
  originAttr: `@on.${name}+${modifiers.join("+")}`
7811
- });
7879
+ }, close ? { kind: "replace-name", from: `+${modifier}`, to: `+${close}` } : null);
7812
7880
  }
7813
7881
  }
7814
7882
  }
@@ -7854,42 +7922,22 @@ function checkEventHandlersHaveImpls(lx, Comp, referencedInputs) {
7854
7922
  const originAttr = `@on.${eventName}`;
7855
7923
  if (hvName === "InputHandlerNameVal") {
7856
7924
  referencedInputs?.add(handlerVal.name);
7857
- if (input[handlerVal.name] === undefined) {
7858
- lx.error(INPUT_HANDLER_NOT_IMPLEMENTED, {
7859
- name: handlerVal.name,
7860
- handler,
7861
- event,
7862
- eventName,
7863
- originAttr
7864
- });
7865
- if (proto[handlerVal.name] !== undefined) {
7866
- lx.hint(INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER, {
7867
- name: handlerVal.name,
7868
- handler,
7869
- event,
7870
- eventName,
7871
- originAttr
7872
- });
7925
+ const { name } = handlerVal;
7926
+ if (input[name] === undefined) {
7927
+ const isMethodFix = proto[name] !== undefined;
7928
+ lx.error(INPUT_HANDLER_NOT_IMPLEMENTED, { name, handler, event, eventName, originAttr }, isMethodFix ? { kind: "add-prefix", from: name, to: `.${name}` } : replaceNameSuggestion(name, Object.keys(input)));
7929
+ if (isMethodFix) {
7930
+ lx.hint(INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER, { name, handler, event, eventName, originAttr }, { kind: "add-prefix", from: name, to: `.${name}` });
7873
7931
  }
7874
7932
  }
7875
7933
  } else if (hvName === "RawFieldVal") {
7876
7934
  referencedInputs?.add(handlerVal.name);
7877
- if (proto[handlerVal.name] === undefined) {
7878
- lx.error(INPUT_HANDLER_METHOD_NOT_IMPLEMENTED, {
7879
- name: handlerVal.name,
7880
- handler,
7881
- event,
7882
- eventName,
7883
- originAttr
7884
- });
7885
- if (input[handlerVal.name] !== undefined) {
7886
- lx.hint(INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD, {
7887
- name: handlerVal.name,
7888
- handler,
7889
- event,
7890
- eventName,
7891
- originAttr
7892
- });
7935
+ const { name } = handlerVal;
7936
+ if (proto[name] === undefined) {
7937
+ const isInputFix = input[name] !== undefined;
7938
+ lx.error(INPUT_HANDLER_METHOD_NOT_IMPLEMENTED, { name, handler, event, eventName, originAttr }, isInputFix ? { kind: "drop-prefix", from: `.${name}`, to: name } : replaceNameSuggestion(name, collectProtoMethodNames(proto)));
7939
+ if (isInputFix) {
7940
+ lx.hint(INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD, { name, handler, event, eventName, originAttr }, { kind: "drop-prefix", from: `.${name}`, to: name });
7893
7941
  }
7894
7942
  }
7895
7943
  }
@@ -7903,22 +7951,23 @@ function checkConsistentAttrVal(lx, val, fields, proto, scope, alter, referenced
7903
7951
  if (valName === "FieldVal" || valName === "RawFieldVal") {
7904
7952
  const { name } = val;
7905
7953
  if (fields[name] === undefined && proto[name] === undefined) {
7906
- lx.error(FIELD_VAL_NOT_DEFINED, { ...errCtx, val, name });
7954
+ const candidates = [...Object.keys(fields), ...collectProtoMethodNames(proto)];
7955
+ lx.error(FIELD_VAL_NOT_DEFINED, { ...errCtx, val, name }, replaceNameSuggestion(name, candidates));
7907
7956
  }
7908
7957
  } else if (valName === "SeqAccessVal") {
7909
7958
  checkConsistentAttrVal(lx, val.seqVal, fields, proto, scope, alter, referencedAlters, skipNameVal, errCtx);
7910
7959
  checkConsistentAttrVal(lx, val.keyVal, fields, proto, scope, alter, referencedAlters, skipNameVal, errCtx);
7911
7960
  } else if (valName === "RequestVal") {
7912
7961
  if (scope.lookupRequest(val.name) === null) {
7913
- lx.error(UNKNOWN_REQUEST_NAME, { ...errCtx, name: val.name });
7962
+ lx.error(UNKNOWN_REQUEST_NAME, { ...errCtx, name: val.name }, replaceNameSuggestion(val.name, scopeKeysAlong(scope, "reqsByName")));
7914
7963
  }
7915
7964
  } else if (valName === "TypeVal") {
7916
7965
  if (scope.lookupComponent(val.name) === null) {
7917
- lx.error(UNKNOWN_COMPONENT_NAME, { ...errCtx, name: val.name });
7966
+ lx.error(UNKNOWN_COMPONENT_NAME, { ...errCtx, name: val.name }, replaceNameSuggestion(val.name, scopeKeysAlong(scope, "byName")));
7918
7967
  }
7919
7968
  } else if (valName === "NameVal") {
7920
7969
  if (!skipNameVal && !isKnownHandlerName(val.name)) {
7921
- lx.error(UNKNOWN_HANDLER_ARG_NAME, { ...errCtx, name: val.name });
7970
+ lx.error(UNKNOWN_HANDLER_ARG_NAME, { ...errCtx, name: val.name }, replaceNameSuggestion(val.name, KNOWN_HANDLER_NAMES));
7922
7971
  }
7923
7972
  } else if (valName === "StrTplVal") {
7924
7973
  for (const subVal of val.vals) {
@@ -7927,7 +7976,7 @@ function checkConsistentAttrVal(lx, val, fields, proto, scope, alter, referenced
7927
7976
  } else if (valName === "AlterHandlerNameVal") {
7928
7977
  referencedAlters?.add(val.name);
7929
7978
  if (alter[val.name] === undefined) {
7930
- lx.error(ALT_HANDLER_NOT_DEFINED, { ...errCtx, name: val.name });
7979
+ lx.error(ALT_HANDLER_NOT_DEFINED, { ...errCtx, name: val.name }, replaceNameSuggestion(val.name, Object.keys(alter)));
7931
7980
  }
7932
7981
  } else if (valName !== "ConstVal" && valName !== "BindVal" && valName !== "DynVal") {
7933
7982
  console.log(val);
@@ -8064,23 +8113,37 @@ class LintContext {
8064
8113
  this.frame = prev;
8065
8114
  }
8066
8115
  }
8067
- error(id, info) {
8068
- this.report(id, info, LEVEL_ERROR2);
8116
+ error(id, info, suggestion = null) {
8117
+ this.report(id, info, LEVEL_ERROR2, suggestion);
8069
8118
  }
8070
- warn(id, info) {
8071
- this.report(id, info, LEVEL_WARN2);
8119
+ warn(id, info, suggestion = null) {
8120
+ this.report(id, info, LEVEL_WARN2, suggestion);
8072
8121
  }
8073
- hint(id, info) {
8074
- this.report(id, info, LEVEL_HINT);
8122
+ hint(id, info, suggestion = null) {
8123
+ this.report(id, info, LEVEL_HINT, suggestion);
8075
8124
  }
8076
- report(id, info = {}, level = LEVEL_ERROR2) {
8077
- this.reports.push({ id, info, level, context: { ...this.frame } });
8125
+ report(id, info = {}, level = LEVEL_ERROR2, suggestion = null) {
8126
+ this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
8078
8127
  }
8079
8128
  }
8080
- var ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", BAD_VALUE = "BAD_VALUE", PARSE_ISSUE_KIND_TO_LINT_ID, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, AT_PREFIX_HINT_KNOWN_BY_KIND, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, NODE_KIND_TO_CTX, LintParseContext;
8129
+ var KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", BAD_VALUE = "BAD_VALUE", PARSE_ISSUE_KIND_TO_LINT_ID, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, AT_PREFIX_HINT_KNOWN_BY_KIND, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUE_KIND_TO_KNOWN_NAMES, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, NODE_KIND_TO_CTX, LintParseContext;
8081
8130
  var init_lint_check = __esm(() => {
8082
8131
  init_anode();
8083
8132
  init_htmllinter();
8133
+ KNOWN_DIRECTIVE_NAMES = new Set([
8134
+ "dangerouslysetinnerhtml",
8135
+ "slot",
8136
+ "push-view",
8137
+ "text",
8138
+ "show",
8139
+ "hide",
8140
+ "each",
8141
+ "enrich-with",
8142
+ "when",
8143
+ "loop-with",
8144
+ "then",
8145
+ "else"
8146
+ ]);
8084
8147
  PARSE_ISSUE_KIND_TO_LINT_ID = {
8085
8148
  "unknown-directive": UNKNOWN_DIRECTIVE,
8086
8149
  "unknown-x-op": UNKNOWN_X_OP,
@@ -8101,6 +8164,11 @@ var init_lint_check = __esm(() => {
8101
8164
  "unknown-x-op": X_KNOWN_OP_NAMES,
8102
8165
  "unknown-x-attr": X_KNOWN_ATTR_NAMES
8103
8166
  };
8167
+ PARSE_ISSUE_KIND_TO_KNOWN_NAMES = {
8168
+ "unknown-directive": KNOWN_DIRECTIVE_NAMES,
8169
+ "unknown-x-op": X_KNOWN_OP_NAMES,
8170
+ "unknown-x-attr": X_KNOWN_ATTR_NAMES
8171
+ };
8104
8172
  HTML_LINT_OPTS = {
8105
8173
  fragmentContext: "template",
8106
8174
  transparentTagPrefixes: ["x"]
@@ -14456,7 +14524,7 @@ function fmtEventSuffix(info) {
14456
14524
  function lintIdToMessage(id, info) {
14457
14525
  switch (id) {
14458
14526
  case "RENDER_IT_OUTSIDE_OF_LOOP":
14459
- return "render-it used outside of a loop";
14527
+ return "<x render-it> used outside of a loop";
14460
14528
  case "UNKNOWN_EVENT_MODIFIER": {
14461
14529
  const mods = info.handler?.modifiers ?? [info.modifier];
14462
14530
  const written = `@on.${info.name}+${mods.join("+")}`;
@@ -14467,13 +14535,13 @@ function lintIdToMessage(id, info) {
14467
14535
  case "INPUT_HANDLER_NOT_IMPLEMENTED":
14468
14536
  return `Input handler '${info.name}' is not implemented${fmtEventSuffix(info)}`;
14469
14537
  case "INPUT_HANDLER_NOT_REFERENCED":
14470
- return `Input handler '${info.name}' is defined but not referenced`;
14538
+ return `Input handler '${info.name}' is defined but never used — remove it or wire it to an @on.* event`;
14471
14539
  case "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED":
14472
14540
  return `Method '.${info.name}' is not implemented${fmtEventSuffix(info)}`;
14473
14541
  case "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD":
14474
- return `'${info.name}' exists as input handler use without '.' prefix${fmtEventSuffix(info)}`;
14542
+ return `'.${info.name}' is a method reference, but '${info.name}' is defined as an input handler${fmtEventSuffix(info)}`;
14475
14543
  case "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER":
14476
- return `'${info.name}' exists as method use with '.' prefix${fmtEventSuffix(info)}`;
14544
+ return `'${info.name}' is an input handler reference, but '${info.name}' is defined as a method${fmtEventSuffix(info)}`;
14477
14545
  case "FIELD_VAL_NOT_DEFINED":
14478
14546
  return `Field '.${info.name}' is not defined${fmtOriginSuffix(info)}`;
14479
14547
  case "DUPLICATE_ATTR_DEFINITION": {
@@ -14482,7 +14550,7 @@ function lintIdToMessage(id, info) {
14482
14550
  return `Attribute '${info.name}' is set ${info.sources?.length ?? "multiple"} times${sources}${tag}`;
14483
14551
  }
14484
14552
  case "IF_NO_BRANCH_SET":
14485
- return `'@if.${info.attr}' has no '@then' or '@else' branch — at least one must be set${fmtTagSuffix(info)}`;
14553
+ return `'@if.${info.attr}' has no '@then' or '@else' branch — add '@then="…"' or '@else="…"' (or both)${fmtTagSuffix(info)}`;
14486
14554
  case "UNKNOWN_REQUEST_NAME":
14487
14555
  return `Unknown request '!${info.name}'${fmtOriginSuffix(info)}`;
14488
14556
  case "UNKNOWN_COMPONENT_NAME":
@@ -14490,7 +14558,7 @@ function lintIdToMessage(id, info) {
14490
14558
  case "ALT_HANDLER_NOT_DEFINED":
14491
14559
  return `Alter handler '${info.name}' is not defined${fmtOriginSuffix(info)}`;
14492
14560
  case "ALT_HANDLER_NOT_REFERENCED":
14493
- return `Alter handler '${info.name}' is defined but not referenced`;
14561
+ return `Alter handler '${info.name}' is defined but never used — remove it or reference it from @when, @enrich-with, or @loop-with`;
14494
14562
  case "UNKNOWN_MACRO_ARG":
14495
14563
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
14496
14564
  case "UNKNOWN_DIRECTIVE":
@@ -14501,7 +14569,7 @@ function lintIdToMessage(id, info) {
14501
14569
  return `Unknown attribute '${info.name}=${JSON.stringify(info.value)}' on <x ${info.op}>${fmtTagSuffix(info)}`;
14502
14570
  case "MAYBE_DROP_AT_PREFIX": {
14503
14571
  const written = info.value !== undefined ? `${info.name}=${JSON.stringify(info.value)}` : info.name;
14504
- return `Did you mean '${info.suggestion}'? Drop the '@' from '${written}' on <x>.`;
14572
+ return `'${written}' on <x> looks like a directive but is actually an x op/attr written with a leading '@'`;
14505
14573
  }
14506
14574
  case "BAD_VALUE":
14507
14575
  return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
@@ -14510,7 +14578,7 @@ function lintIdToMessage(id, info) {
14510
14578
  case "HTML_SVG_TAG_WILL_LOWERCASE":
14511
14579
  return `SVG tag <${info.raw}> is not in the WHATWG case-correction list — will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
14512
14580
  case "HTML_TAG_NOT_ALLOWED_IN_PARENT":
14513
- return `<${info.tag}> not allowed under <${info.parent ?? "?"}> in ${info.mode} (action: ${info.action})${fmtLocationSuffix(info)}`;
14581
+ return `<${info.tag}> not allowed under <${info.parent ?? "?"}> in ${info.mode} ${htmlActionPhrase(info.action, info.tag, info.parent)}${fmtLocationSuffix(info)}`;
14514
14582
  case "HTML_TEXT_NOT_ALLOWED_IN_PARENT":
14515
14583
  return `Non-whitespace text not allowed in ${info.mode}: ${JSON.stringify(info.snippet)}${fmtLocationSuffix(info)}`;
14516
14584
  case "HTML_VOID_ELEMENT_HAS_CLOSE_TAG":
@@ -14547,11 +14615,47 @@ function lintIdToMessage(id, info) {
14547
14615
  return id;
14548
14616
  }
14549
14617
  }
14618
+ function suggestionToMessage(suggestion) {
14619
+ if (!suggestion)
14620
+ return null;
14621
+ switch (suggestion.kind) {
14622
+ case "replace-name":
14623
+ return `did you mean '${suggestion.to}'?`;
14624
+ case "drop-prefix":
14625
+ return `did you mean '${suggestion.to}'? (drop the leading '${suggestion.from.slice(0, suggestion.from.length - suggestion.to.length)}')`;
14626
+ case "add-prefix":
14627
+ return `did you mean '${suggestion.to}'? (add the leading '${suggestion.to.slice(0, suggestion.to.length - suggestion.from.length)}')`;
14628
+ case "remove":
14629
+ return `remove ${suggestion.what}`;
14630
+ case "rewrite":
14631
+ return `use '${suggestion.to}' instead of '${suggestion.from}'`;
14632
+ case "wrap":
14633
+ return `wrap it in ${suggestion.to}`;
14634
+ default:
14635
+ return null;
14636
+ }
14637
+ }
14550
14638
  function fmtLocationSuffix(info) {
14551
14639
  const loc = info?.location;
14552
14640
  if (!loc)
14553
14641
  return "";
14554
- return ` at L${loc.line}:C${loc.column}`;
14642
+ return ` at line ${loc.line}, col ${loc.column}`;
14643
+ }
14644
+ function htmlActionPhrase(action, tag, parent) {
14645
+ switch (action) {
14646
+ case "ignored":
14647
+ return `the parser will drop this <${tag}>`;
14648
+ case "drop":
14649
+ return `the parser will drop this <${tag}>`;
14650
+ case "auto-close-implicit":
14651
+ return `the parser will close <${parent ?? "?"}> first, then place <${tag}> as a sibling`;
14652
+ case "foster-parent":
14653
+ return `the parser will move <${tag}> outside <${parent ?? "?"}> (foster-parenting)`;
14654
+ case "foreign-breakout":
14655
+ return `the parser will exit foreign content and re-process <${tag}> in HTML mode`;
14656
+ default:
14657
+ return `parser action: ${action}`;
14658
+ }
14555
14659
  }
14556
14660
 
14557
14661
  // tools/format/cli.js
@@ -14636,7 +14740,9 @@ function fmtLintReport(rep) {
14636
14740
  for (const f of c.findings) {
14637
14741
  const tag = f.level.toUpperCase();
14638
14742
  const view = f.context?.viewName ? ` [view=${f.context.viewName}]` : "";
14639
- lines.push(` [${tag}]${view} ${lintIdToMessage(f.id, f.info)}`);
14743
+ const tail = suggestionToMessage(f.suggestion);
14744
+ const suffix = tail ? ` — ${tail}` : "";
14745
+ lines.push(` [${tag}]${view} ${lintIdToMessage(f.id, f.info)}${suffix}`);
14640
14746
  }
14641
14747
  }
14642
14748
  lines.push("");
@@ -14817,7 +14923,9 @@ function fmtLintReport2(rep) {
14817
14923
  }
14818
14924
  for (const f of c.findings) {
14819
14925
  const view = f.context?.viewName ? ` _(view: \`${f.context.viewName}\`)_` : "";
14820
- lines.push(`- **${f.level.toUpperCase()}** ${lintIdToMessage(f.id, f.info)}${view}`);
14926
+ const tail = suggestionToMessage(f.suggestion);
14927
+ const suffix = tail ? ` — ${tail}` : "";
14928
+ lines.push(`- **${f.level.toUpperCase()}** ${lintIdToMessage(f.id, f.info)}${suffix}${view}`);
14821
14929
  }
14822
14930
  lines.push("");
14823
14931
  }