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.
- package/dist/tutuca-cli.js +218 -109
- package/dist/tutuca-dev.js +270 -162
- package/dist/tutuca-dev.min.js +3 -3
- package/dist/tutuca-extra.js +4 -3
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca.js +4 -3
- package/dist/tutuca.min.js +2 -2
- package/package.json +1 -1
- package/skill/tutuca/core.md +15 -0
package/dist/tutuca-cli.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
7730
|
-
const
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
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
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
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
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
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
|
-
|
|
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,
|
|
9191
|
+
registerComponents(comps, opts) {
|
|
9123
9192
|
const scope = this.compStack.enter();
|
|
9124
|
-
scope.registerComponents(comps,
|
|
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
|
|
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 `'
|
|
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}'
|
|
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 —
|
|
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
|
|
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 `
|
|
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}
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|