tutuca 0.9.47 → 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
 
@@ -5427,7 +5428,8 @@ class ComponentStack {
5427
5428
  enter() {
5428
5429
  return new ComponentStack(this.comps, this);
5429
5430
  }
5430
- registerComponents(comps, aliases2 = {}) {
5431
+ registerComponents(comps, opts) {
5432
+ const { aliases: aliases2 = {} } = opts ?? {};
5431
5433
  for (let i = 0;i < comps.length; i++) {
5432
5434
  const comp = comps[i];
5433
5435
  comp.scope = this.enter();
@@ -6632,10 +6634,7 @@ class LinterCtx {
6632
6634
  a.quote = quote;
6633
6635
  const dup = this.currentAttrs.find((x) => x.name === a.name);
6634
6636
  if (dup) {
6635
- this.report(HTML_DUPLICATE_ATTRIBUTE, LEVEL_WARN, a.nameStart, {
6636
- name: a.name,
6637
- firstAt: dup.nameStart
6638
- });
6637
+ this.report(HTML_DUPLICATE_ATTRIBUTE, LEVEL_WARN, a.nameStart, { name: a.name, firstAt: dup.nameStart }, { kind: "remove", what: `the duplicate '${a.name}' attribute` });
6639
6638
  } else {
6640
6639
  this.currentAttrs.push(a);
6641
6640
  }
@@ -6685,12 +6684,12 @@ class LinterCtx {
6685
6684
  }
6686
6685
  }
6687
6686
  if (firstNonWs < 0) {
6688
- 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}>` });
6689
6688
  } else {
6690
- 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}>` });
6691
6690
  }
6692
6691
  } else {
6693
- 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}>` });
6694
6693
  }
6695
6694
  }
6696
6695
  this.handleEndTag(name, start);
@@ -6723,14 +6722,15 @@ class LinterCtx {
6723
6722
  return this.currentNamespace() !== NS.html;
6724
6723
  }
6725
6724
  onend() {}
6726
- report(id, level, offset, info) {
6725
+ report(id, level, offset, info, suggestion = null) {
6727
6726
  this.findingCount++;
6728
6727
  const { line, column } = offsetToLineCol(this.lineStarts, offset);
6729
6728
  this.onFinding({
6730
6729
  id,
6731
6730
  level,
6732
6731
  info,
6733
- 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
6734
6734
  });
6735
6735
  }
6736
6736
  currentNode() {
@@ -6839,17 +6839,11 @@ class LinterCtx {
6839
6839
  const ns = this.currentNamespace();
6840
6840
  if (ns === NS.html) {
6841
6841
  if (raw !== name) {
6842
- this.report(HTML_TAG_NAME_HAS_UPPERCASE, LEVEL_ERROR, start, {
6843
- raw,
6844
- lowercased: name
6845
- });
6842
+ this.report(HTML_TAG_NAME_HAS_UPPERCASE, LEVEL_ERROR, start, { raw, lowercased: name }, { kind: "rewrite", from: `<${raw}>`, to: `<${name}>` });
6846
6843
  }
6847
6844
  } else if (ns === NS.svg) {
6848
6845
  if (raw !== raw.toLowerCase() && !this.svgCamelElements.has(raw)) {
6849
- this.report(HTML_SVG_TAG_WILL_LOWERCASE, LEVEL_ERROR, start, {
6850
- raw,
6851
- lowercased: name
6852
- });
6846
+ this.report(HTML_SVG_TAG_WILL_LOWERCASE, LEVEL_ERROR, start, { raw, lowercased: name }, { kind: "rewrite", from: `<${raw}>`, to: `<${name}>` });
6853
6847
  }
6854
6848
  }
6855
6849
  const targetNs = ns !== NS.html ? ns : name === "svg" ? NS.svg : name === "math" ? NS.math : NS.html;
@@ -6857,20 +6851,14 @@ class LinterCtx {
6857
6851
  for (const a of this.currentAttrs) {
6858
6852
  const canonical = SVG_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
6859
6853
  if (canonical && a.rawName !== canonical) {
6860
- this.report(HTML_SVG_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
6861
- raw: a.rawName,
6862
- canonical
6863
- });
6854
+ this.report(HTML_SVG_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, { raw: a.rawName, canonical }, { kind: "rewrite", from: a.rawName, to: canonical });
6864
6855
  }
6865
6856
  }
6866
6857
  } else if (targetNs === NS.math) {
6867
6858
  for (const a of this.currentAttrs) {
6868
6859
  const canonical = MATHML_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
6869
6860
  if (canonical && a.rawName !== canonical) {
6870
- this.report(HTML_MATHML_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
6871
- raw: a.rawName,
6872
- canonical
6873
- });
6861
+ this.report(HTML_MATHML_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, { raw: a.rawName, canonical }, { kind: "rewrite", from: a.rawName, to: canonical });
6874
6862
  }
6875
6863
  }
6876
6864
  }
@@ -6985,10 +6973,7 @@ class LinterCtx {
6985
6973
  }
6986
6974
  if (name === "form") {
6987
6975
  if (this.openElementsHas("form") && !this.openElementsHas("template")) {
6988
- this.report(HTML_DUPLICATE_FORM, LEVEL_ERROR, start, {
6989
- tag: raw,
6990
- mode: this.insertionMode
6991
- });
6976
+ this.report(HTML_DUPLICATE_FORM, LEVEL_ERROR, start, { tag: raw, mode: this.insertionMode }, { kind: "remove", what: "the inner <form>" });
6992
6977
  return;
6993
6978
  }
6994
6979
  if (this.hasInButtonScope("p"))
@@ -7550,10 +7535,7 @@ class LinterCtx {
7550
7535
  }
7551
7536
  }
7552
7537
  if (VOID_ELEMENTS.has(name)) {
7553
- this.report(HTML_VOID_ELEMENT_HAS_CLOSE_TAG, LEVEL_WARN, start, {
7554
- tag: name,
7555
- mode: this.insertionMode
7556
- });
7538
+ this.report(HTML_VOID_ELEMENT_HAS_CLOSE_TAG, LEVEL_WARN, start, { tag: name, mode: this.insertionMode }, { kind: "remove", what: `the </${name}>` });
7557
7539
  return;
7558
7540
  }
7559
7541
  if (FORMATTING_ELEMENTS.has(name)) {
@@ -7690,7 +7672,89 @@ var init_htmllinter = __esm(() => {
7690
7672
  ]);
7691
7673
  });
7692
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
+
7693
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
+ }
7694
7758
  function checkComponent(Comp, lx = new LintContext) {
7695
7759
  return lx.push({ componentName: Comp.name }, () => {
7696
7760
  const referencedAlters = new Set;
@@ -7716,7 +7780,7 @@ function checkView(lx, view, Comp, referencedAlters) {
7716
7780
  function checkHtmlStructure(lx, view) {
7717
7781
  if (typeof view.rawView !== "string" || !view.rawView)
7718
7782
  return;
7719
- 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);
7720
7784
  }
7721
7785
  function checkParseIssues(lx, view) {
7722
7786
  const issues = view.ctx.parseIssues;
@@ -7726,12 +7790,19 @@ function checkParseIssues(lx, view) {
7726
7790
  const id = PARSE_ISSUE_KIND_TO_LINT_ID[kind];
7727
7791
  if (!id)
7728
7792
  continue;
7729
- lx.error(id, info);
7730
- const known = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
7731
- if (known && info.name?.startsWith("@")) {
7732
- const suggestion = info.name.slice(1);
7733
- if (known.has(suggestion))
7734
- 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) });
7735
7806
  }
7736
7807
  }
7737
7808
  }
@@ -7744,11 +7815,7 @@ function checkMacroCallArgs(lx, view, Comp) {
7744
7815
  const { defaults } = macro;
7745
7816
  for (const argName in macroNode.attrs) {
7746
7817
  if (!(argName in defaults)) {
7747
- lx.error(UNKNOWN_MACRO_ARG, {
7748
- name: argName,
7749
- macroName: macroNode.name,
7750
- tag: `x:${macroNode.name}`
7751
- });
7818
+ lx.error(UNKNOWN_MACRO_ARG, { name: argName, macroName: macroNode.name, tag: `x:${macroNode.name}` }, replaceNameSuggestion(argName, Object.keys(defaults)));
7752
7819
  }
7753
7820
  }
7754
7821
  }
@@ -7770,8 +7837,9 @@ function walkForRenderIt(lx, node, loopDepth) {
7770
7837
  return;
7771
7838
  switch (node.constructor.name) {
7772
7839
  case "RenderItNode":
7773
- if (loopDepth === 0)
7774
- 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
+ }
7775
7843
  return;
7776
7844
  case "EachNode":
7777
7845
  walkForRenderIt(lx, node.node, loopDepth + 1);
@@ -7801,13 +7869,14 @@ function checkEventModifiers(lx, view) {
7801
7869
  const modWrappers = MOD_WRAPPERS_BY_EVENT[name] ?? NO_WRAPPERS;
7802
7870
  for (const modifier of modifiers) {
7803
7871
  if (modWrappers[modifier] === undefined) {
7872
+ const close = closestName(modifier, Object.keys(modWrappers));
7804
7873
  lx.error(UNKNOWN_EVENT_MODIFIER, {
7805
7874
  name,
7806
7875
  modifier,
7807
7876
  handler,
7808
7877
  event,
7809
7878
  originAttr: `@on.${name}+${modifiers.join("+")}`
7810
- });
7879
+ }, close ? { kind: "replace-name", from: `+${modifier}`, to: `+${close}` } : null);
7811
7880
  }
7812
7881
  }
7813
7882
  }
@@ -7853,42 +7922,22 @@ function checkEventHandlersHaveImpls(lx, Comp, referencedInputs) {
7853
7922
  const originAttr = `@on.${eventName}`;
7854
7923
  if (hvName === "InputHandlerNameVal") {
7855
7924
  referencedInputs?.add(handlerVal.name);
7856
- if (input[handlerVal.name] === undefined) {
7857
- lx.error(INPUT_HANDLER_NOT_IMPLEMENTED, {
7858
- name: handlerVal.name,
7859
- handler,
7860
- event,
7861
- eventName,
7862
- originAttr
7863
- });
7864
- if (proto[handlerVal.name] !== undefined) {
7865
- lx.hint(INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER, {
7866
- name: handlerVal.name,
7867
- handler,
7868
- event,
7869
- eventName,
7870
- originAttr
7871
- });
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}` });
7872
7931
  }
7873
7932
  }
7874
7933
  } else if (hvName === "RawFieldVal") {
7875
7934
  referencedInputs?.add(handlerVal.name);
7876
- if (proto[handlerVal.name] === undefined) {
7877
- lx.error(INPUT_HANDLER_METHOD_NOT_IMPLEMENTED, {
7878
- name: handlerVal.name,
7879
- handler,
7880
- event,
7881
- eventName,
7882
- originAttr
7883
- });
7884
- if (input[handlerVal.name] !== undefined) {
7885
- lx.hint(INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD, {
7886
- name: handlerVal.name,
7887
- handler,
7888
- event,
7889
- eventName,
7890
- originAttr
7891
- });
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 });
7892
7941
  }
7893
7942
  }
7894
7943
  }
@@ -7902,22 +7951,23 @@ function checkConsistentAttrVal(lx, val, fields, proto, scope, alter, referenced
7902
7951
  if (valName === "FieldVal" || valName === "RawFieldVal") {
7903
7952
  const { name } = val;
7904
7953
  if (fields[name] === undefined && proto[name] === undefined) {
7905
- 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));
7906
7956
  }
7907
7957
  } else if (valName === "SeqAccessVal") {
7908
7958
  checkConsistentAttrVal(lx, val.seqVal, fields, proto, scope, alter, referencedAlters, skipNameVal, errCtx);
7909
7959
  checkConsistentAttrVal(lx, val.keyVal, fields, proto, scope, alter, referencedAlters, skipNameVal, errCtx);
7910
7960
  } else if (valName === "RequestVal") {
7911
7961
  if (scope.lookupRequest(val.name) === null) {
7912
- 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")));
7913
7963
  }
7914
7964
  } else if (valName === "TypeVal") {
7915
7965
  if (scope.lookupComponent(val.name) === null) {
7916
- 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")));
7917
7967
  }
7918
7968
  } else if (valName === "NameVal") {
7919
7969
  if (!skipNameVal && !isKnownHandlerName(val.name)) {
7920
- 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));
7921
7971
  }
7922
7972
  } else if (valName === "StrTplVal") {
7923
7973
  for (const subVal of val.vals) {
@@ -7926,7 +7976,7 @@ function checkConsistentAttrVal(lx, val, fields, proto, scope, alter, referenced
7926
7976
  } else if (valName === "AlterHandlerNameVal") {
7927
7977
  referencedAlters?.add(val.name);
7928
7978
  if (alter[val.name] === undefined) {
7929
- 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)));
7930
7980
  }
7931
7981
  } else if (valName !== "ConstVal" && valName !== "BindVal" && valName !== "DynVal") {
7932
7982
  console.log(val);
@@ -8063,23 +8113,37 @@ class LintContext {
8063
8113
  this.frame = prev;
8064
8114
  }
8065
8115
  }
8066
- error(id, info) {
8067
- this.report(id, info, LEVEL_ERROR2);
8116
+ error(id, info, suggestion = null) {
8117
+ this.report(id, info, LEVEL_ERROR2, suggestion);
8068
8118
  }
8069
- warn(id, info) {
8070
- this.report(id, info, LEVEL_WARN2);
8119
+ warn(id, info, suggestion = null) {
8120
+ this.report(id, info, LEVEL_WARN2, suggestion);
8071
8121
  }
8072
- hint(id, info) {
8073
- this.report(id, info, LEVEL_HINT);
8122
+ hint(id, info, suggestion = null) {
8123
+ this.report(id, info, LEVEL_HINT, suggestion);
8074
8124
  }
8075
- report(id, info = {}, level = LEVEL_ERROR2) {
8076
- 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 });
8077
8127
  }
8078
8128
  }
8079
- 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;
8080
8130
  var init_lint_check = __esm(() => {
8081
8131
  init_anode();
8082
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
+ ]);
8083
8147
  PARSE_ISSUE_KIND_TO_LINT_ID = {
8084
8148
  "unknown-directive": UNKNOWN_DIRECTIVE,
8085
8149
  "unknown-x-op": UNKNOWN_X_OP,
@@ -8100,6 +8164,11 @@ var init_lint_check = __esm(() => {
8100
8164
  "unknown-x-op": X_KNOWN_OP_NAMES,
8101
8165
  "unknown-x-attr": X_KNOWN_ATTR_NAMES
8102
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
+ };
8103
8172
  HTML_LINT_OPTS = {
8104
8173
  fragmentContext: "template",
8105
8174
  transparentTagPrefixes: ["x"]
@@ -9119,9 +9188,9 @@ class App {
9119
9188
  sendAtRoot(name, args, opts) {
9120
9189
  this.transactor.pushSend(new Path([]), name, args, opts);
9121
9190
  }
9122
- registerComponents(comps, aliases2) {
9191
+ registerComponents(comps, opts) {
9123
9192
  const scope = this.compStack.enter();
9124
- scope.registerComponents(comps, aliases2);
9193
+ scope.registerComponents(comps, opts);
9125
9194
  return scope;
9126
9195
  }
9127
9196
  _transactNextBatch(maxRunTimeMs = 10) {
@@ -14455,7 +14524,7 @@ function fmtEventSuffix(info) {
14455
14524
  function lintIdToMessage(id, info) {
14456
14525
  switch (id) {
14457
14526
  case "RENDER_IT_OUTSIDE_OF_LOOP":
14458
- return "render-it used outside of a loop";
14527
+ return "<x render-it> used outside of a loop";
14459
14528
  case "UNKNOWN_EVENT_MODIFIER": {
14460
14529
  const mods = info.handler?.modifiers ?? [info.modifier];
14461
14530
  const written = `@on.${info.name}+${mods.join("+")}`;
@@ -14466,13 +14535,13 @@ function lintIdToMessage(id, info) {
14466
14535
  case "INPUT_HANDLER_NOT_IMPLEMENTED":
14467
14536
  return `Input handler '${info.name}' is not implemented${fmtEventSuffix(info)}`;
14468
14537
  case "INPUT_HANDLER_NOT_REFERENCED":
14469
- 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`;
14470
14539
  case "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED":
14471
14540
  return `Method '.${info.name}' is not implemented${fmtEventSuffix(info)}`;
14472
14541
  case "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD":
14473
- 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)}`;
14474
14543
  case "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER":
14475
- 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)}`;
14476
14545
  case "FIELD_VAL_NOT_DEFINED":
14477
14546
  return `Field '.${info.name}' is not defined${fmtOriginSuffix(info)}`;
14478
14547
  case "DUPLICATE_ATTR_DEFINITION": {
@@ -14481,7 +14550,7 @@ function lintIdToMessage(id, info) {
14481
14550
  return `Attribute '${info.name}' is set ${info.sources?.length ?? "multiple"} times${sources}${tag}`;
14482
14551
  }
14483
14552
  case "IF_NO_BRANCH_SET":
14484
- 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)}`;
14485
14554
  case "UNKNOWN_REQUEST_NAME":
14486
14555
  return `Unknown request '!${info.name}'${fmtOriginSuffix(info)}`;
14487
14556
  case "UNKNOWN_COMPONENT_NAME":
@@ -14489,7 +14558,7 @@ function lintIdToMessage(id, info) {
14489
14558
  case "ALT_HANDLER_NOT_DEFINED":
14490
14559
  return `Alter handler '${info.name}' is not defined${fmtOriginSuffix(info)}`;
14491
14560
  case "ALT_HANDLER_NOT_REFERENCED":
14492
- 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`;
14493
14562
  case "UNKNOWN_MACRO_ARG":
14494
14563
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
14495
14564
  case "UNKNOWN_DIRECTIVE":
@@ -14500,7 +14569,7 @@ function lintIdToMessage(id, info) {
14500
14569
  return `Unknown attribute '${info.name}=${JSON.stringify(info.value)}' on <x ${info.op}>${fmtTagSuffix(info)}`;
14501
14570
  case "MAYBE_DROP_AT_PREFIX": {
14502
14571
  const written = info.value !== undefined ? `${info.name}=${JSON.stringify(info.value)}` : info.name;
14503
- 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 '@'`;
14504
14573
  }
14505
14574
  case "BAD_VALUE":
14506
14575
  return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
@@ -14509,7 +14578,7 @@ function lintIdToMessage(id, info) {
14509
14578
  case "HTML_SVG_TAG_WILL_LOWERCASE":
14510
14579
  return `SVG tag <${info.raw}> is not in the WHATWG case-correction list — will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
14511
14580
  case "HTML_TAG_NOT_ALLOWED_IN_PARENT":
14512
- 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)}`;
14513
14582
  case "HTML_TEXT_NOT_ALLOWED_IN_PARENT":
14514
14583
  return `Non-whitespace text not allowed in ${info.mode}: ${JSON.stringify(info.snippet)}${fmtLocationSuffix(info)}`;
14515
14584
  case "HTML_VOID_ELEMENT_HAS_CLOSE_TAG":
@@ -14546,11 +14615,47 @@ function lintIdToMessage(id, info) {
14546
14615
  return id;
14547
14616
  }
14548
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
+ }
14549
14638
  function fmtLocationSuffix(info) {
14550
14639
  const loc = info?.location;
14551
14640
  if (!loc)
14552
14641
  return "";
14553
- 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
+ }
14554
14659
  }
14555
14660
 
14556
14661
  // tools/format/cli.js
@@ -14635,7 +14740,9 @@ function fmtLintReport(rep) {
14635
14740
  for (const f of c.findings) {
14636
14741
  const tag = f.level.toUpperCase();
14637
14742
  const view = f.context?.viewName ? ` [view=${f.context.viewName}]` : "";
14638
- 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}`);
14639
14746
  }
14640
14747
  }
14641
14748
  lines.push("");
@@ -14816,7 +14923,9 @@ function fmtLintReport2(rep) {
14816
14923
  }
14817
14924
  for (const f of c.findings) {
14818
14925
  const view = f.context?.viewName ? ` _(view: \`${f.context.viewName}\`)_` : "";
14819
- 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}`);
14820
14929
  }
14821
14930
  lines.push("");
14822
14931
  }