tutuca 0.9.50 → 0.9.52
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 +96 -7
- package/dist/tutuca-dev.js +107 -8
- package/dist/tutuca-dev.min.js +3 -3
- package/dist/tutuca-extra.js +15 -1
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca.js +15 -1
- package/dist/tutuca.min.js +2 -2
- package/package.json +1 -1
package/dist/tutuca-cli.js
CHANGED
|
@@ -4317,7 +4317,15 @@ var init_value = __esm(() => {
|
|
|
4317
4317
|
vals[i] = val;
|
|
4318
4318
|
allConsts &&= val instanceof ConstVal;
|
|
4319
4319
|
}
|
|
4320
|
-
|
|
4320
|
+
if (allConsts)
|
|
4321
|
+
return new ConstVal(vals.map((v) => v.val).join(""));
|
|
4322
|
+
let lo = 0;
|
|
4323
|
+
let hi = vals.length;
|
|
4324
|
+
while (lo < hi && vals[lo] instanceof ConstVal && vals[lo].val === "")
|
|
4325
|
+
lo++;
|
|
4326
|
+
while (hi > lo && vals[hi - 1] instanceof ConstVal && vals[hi - 1].val === "")
|
|
4327
|
+
hi--;
|
|
4328
|
+
return new StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
|
|
4321
4329
|
}
|
|
4322
4330
|
};
|
|
4323
4331
|
NameVal = class NameVal extends VarVal {
|
|
@@ -5472,8 +5480,10 @@ class ComponentStack {
|
|
|
5472
5480
|
function defaultOnStackEnter() {
|
|
5473
5481
|
return null;
|
|
5474
5482
|
}
|
|
5483
|
+
var _rawSpecKeys = "name view style commonStyle globalStyle input receive bubble response alter on views dynamic fields methods statics", KNOWN_SPEC_KEYS;
|
|
5475
5484
|
var init_components = __esm(() => {
|
|
5476
5485
|
init_attribute();
|
|
5486
|
+
KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
|
|
5477
5487
|
});
|
|
5478
5488
|
|
|
5479
5489
|
// tools/core/html-tokenizer.js
|
|
@@ -7755,8 +7765,25 @@ function replaceNameSuggestion(name, candidates) {
|
|
|
7755
7765
|
const close = closestName(name, candidates);
|
|
7756
7766
|
return close ? { kind: "replace-name", from: name, to: close } : null;
|
|
7757
7767
|
}
|
|
7758
|
-
function
|
|
7768
|
+
function classifyBadValue(value) {
|
|
7769
|
+
if (typeof value !== "string")
|
|
7770
|
+
return null;
|
|
7771
|
+
const s = value.trim();
|
|
7772
|
+
if (s === "")
|
|
7773
|
+
return null;
|
|
7774
|
+
if (/\s\?\s.+\s:\s/.test(s))
|
|
7775
|
+
return "ternary";
|
|
7776
|
+
if (/===|!==|==|!=|<=|>=|\s<\s|\s>\s/.test(s))
|
|
7777
|
+
return "comparison";
|
|
7778
|
+
if (/&&|\|\|/.test(s))
|
|
7779
|
+
return "logical";
|
|
7780
|
+
if (/^\.[A-Za-z_]\w*\s+\S/.test(s))
|
|
7781
|
+
return "call-with-args";
|
|
7782
|
+
return null;
|
|
7783
|
+
}
|
|
7784
|
+
function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SET } = {}) {
|
|
7759
7785
|
return lx.push({ componentName: Comp.name }, () => {
|
|
7786
|
+
checkUnknownSpecKeys(lx, Comp, wellKnownExtras);
|
|
7760
7787
|
const referencedAlters = new Set;
|
|
7761
7788
|
const referencedInputs = new Set;
|
|
7762
7789
|
checkEventHandlersHaveImpls(lx, Comp, referencedInputs);
|
|
@@ -7790,6 +7817,13 @@ function checkParseIssues(lx, view) {
|
|
|
7790
7817
|
const id = PARSE_ISSUE_KIND_TO_LINT_ID[kind];
|
|
7791
7818
|
if (!id)
|
|
7792
7819
|
continue;
|
|
7820
|
+
if (kind === "bad-value") {
|
|
7821
|
+
const detected = classifyBadValue(info.value);
|
|
7822
|
+
if (detected) {
|
|
7823
|
+
lx.error(UNSUPPORTED_EXPR_SYNTAX, { ...info, detected }, { kind: "rephrase", from: info.value, text: UNSUPPORTED_EXPR_GUIDANCE[detected] });
|
|
7824
|
+
continue;
|
|
7825
|
+
}
|
|
7826
|
+
}
|
|
7793
7827
|
const atPrefixKnown = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
|
|
7794
7828
|
const isAtPrefixedTypo = atPrefixKnown && info.name?.startsWith("@") && atPrefixKnown.has(info.name.slice(1));
|
|
7795
7829
|
let suggestion = null;
|
|
@@ -7970,7 +8004,12 @@ function checkConsistentAttrVal(lx, val, fields, proto, scope, alter, referenced
|
|
|
7970
8004
|
lx.error(UNKNOWN_HANDLER_ARG_NAME, { ...errCtx, name: val.name }, replaceNameSuggestion(val.name, KNOWN_HANDLER_NAMES));
|
|
7971
8005
|
}
|
|
7972
8006
|
} else if (valName === "StrTplVal") {
|
|
7973
|
-
|
|
8007
|
+
const vs = val.vals;
|
|
8008
|
+
if (vs.length === 1) {
|
|
8009
|
+
const simpler = String(vs[0]);
|
|
8010
|
+
lx.warn(REDUNDANT_TEMPLATE_STRING, { ...errCtx, simpler }, { kind: "rewrite", from: `{${simpler}}`, to: simpler });
|
|
8011
|
+
}
|
|
8012
|
+
for (const subVal of vs) {
|
|
7974
8013
|
checkConsistentAttrVal(lx, subVal, fields, proto, scope, alter, referencedAlters, skipNameVal, errCtx);
|
|
7975
8014
|
}
|
|
7976
8015
|
} else if (valName === "AlterHandlerNameVal") {
|
|
@@ -8084,6 +8123,18 @@ function checkConsistentAttrs(lx, Comp, referencedAlters) {
|
|
|
8084
8123
|
});
|
|
8085
8124
|
}
|
|
8086
8125
|
}
|
|
8126
|
+
function checkUnknownSpecKeys(lx, Comp, wellKnownExtras) {
|
|
8127
|
+
const extra = Comp.extra;
|
|
8128
|
+
if (!extra)
|
|
8129
|
+
return;
|
|
8130
|
+
let candidates = null;
|
|
8131
|
+
for (const key of Object.keys(extra)) {
|
|
8132
|
+
if (wellKnownExtras.has(key))
|
|
8133
|
+
continue;
|
|
8134
|
+
candidates ??= [...KNOWN_COMPONENT_SPEC_KEYS, ...wellKnownExtras];
|
|
8135
|
+
lx.warn(UNKNOWN_COMPONENT_SPEC_KEY, { key }, replaceNameSuggestion(key, candidates));
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8087
8138
|
function checkUnreferencedAlterHandlers(lx, Comp, referencedAlters) {
|
|
8088
8139
|
for (const name in Comp.alter) {
|
|
8089
8140
|
if (!referencedAlters.has(name)) {
|
|
@@ -8126,10 +8177,12 @@ class LintContext {
|
|
|
8126
8177
|
this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
|
|
8127
8178
|
}
|
|
8128
8179
|
}
|
|
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;
|
|
8180
|
+
var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET, 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", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", 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, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, NODE_KIND_TO_CTX, LintParseContext;
|
|
8130
8181
|
var init_lint_check = __esm(() => {
|
|
8131
8182
|
init_anode();
|
|
8132
8183
|
init_htmllinter();
|
|
8184
|
+
KNOWN_COMPONENT_SPEC_KEYS = new Set("name view style commonStyle globalStyle input receive bubble response alter on views dynamic fields methods statics".split(" "));
|
|
8185
|
+
EMPTY_SET = new Set;
|
|
8133
8186
|
KNOWN_DIRECTIVE_NAMES = new Set([
|
|
8134
8187
|
"dangerouslysetinnerhtml",
|
|
8135
8188
|
"slot",
|
|
@@ -8169,6 +8222,12 @@ var init_lint_check = __esm(() => {
|
|
|
8169
8222
|
"unknown-x-op": X_KNOWN_OP_NAMES,
|
|
8170
8223
|
"unknown-x-attr": X_KNOWN_ATTR_NAMES
|
|
8171
8224
|
};
|
|
8225
|
+
UNSUPPORTED_EXPR_GUIDANCE = {
|
|
8226
|
+
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed field on the component that returns the value, then reference it as '.methodName'.",
|
|
8227
|
+
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like '.isFooSelected' that returns the boolean, then reference it as '.isFooSelected'.",
|
|
8228
|
+
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '.methodName'.",
|
|
8229
|
+
"call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('.methodName') and read what you need from component state, or split into per-case methods."
|
|
8230
|
+
};
|
|
8172
8231
|
HTML_LINT_OPTS = {
|
|
8173
8232
|
fragmentContext: "template",
|
|
8174
8233
|
transparentTagPrefixes: ["x"]
|
|
@@ -8220,7 +8279,7 @@ var init_lint_check = __esm(() => {
|
|
|
8220
8279
|
});
|
|
8221
8280
|
|
|
8222
8281
|
// tools/core/lint.js
|
|
8223
|
-
function lintComponents(normalized, { name = null, LintParseContextClass }) {
|
|
8282
|
+
function lintComponents(normalized, { name = null, LintParseContextClass, wellKnownExtras = new Set }) {
|
|
8224
8283
|
const comps = normalized.components;
|
|
8225
8284
|
const picked = name === null ? comps : comps.filter((c) => c.name === name);
|
|
8226
8285
|
const stack = new ComponentStack;
|
|
@@ -8232,7 +8291,7 @@ function lintComponents(normalized, { name = null, LintParseContextClass }) {
|
|
|
8232
8291
|
const results = [];
|
|
8233
8292
|
for (const comp of picked) {
|
|
8234
8293
|
comp.compile(LintParseContextClass);
|
|
8235
|
-
const lx = checkComponent(comp);
|
|
8294
|
+
const lx = checkComponent(comp, undefined, { wellKnownExtras });
|
|
8236
8295
|
results.push(new LintComponentResult({
|
|
8237
8296
|
componentName: comp.name,
|
|
8238
8297
|
findings: lx.reports.map((r) => new LintFinding(r))
|
|
@@ -11148,7 +11207,7 @@ var keyMapper = (v, k) => k, entryMapper = (v, k) => [k, v], not = (predicate) =
|
|
|
11148
11207
|
const iter = SetCollection(value);
|
|
11149
11208
|
assertNotInfinite(iter.size);
|
|
11150
11209
|
iter.forEach((v) => set3.add(v));
|
|
11151
|
-
}), SetImpl, makeSet = (map, ownerID) => new SetImpl(map, ownerID),
|
|
11210
|
+
}), SetImpl, makeSet = (map, ownerID) => new SetImpl(map, ownerID), EMPTY_SET2, emptySet = () => EMPTY_SET2 || (EMPTY_SET2 = makeSet(emptyMap())), OrderedSet = (value) => value === undefined || value === null ? emptyOrderedSet() : isOrderedSet(value) ? value : emptyOrderedSet().withMutations((set3) => {
|
|
11152
11211
|
const iter = SetCollection(value);
|
|
11153
11212
|
assertNotInfinite(iter.size);
|
|
11154
11213
|
iter.forEach((v) => set3.add(v));
|
|
@@ -14466,6 +14525,28 @@ function makeFormatter(name, table) {
|
|
|
14466
14525
|
}
|
|
14467
14526
|
|
|
14468
14527
|
// tools/format/lint.js
|
|
14528
|
+
var UNSUPPORTED_EXPR_LABEL = {
|
|
14529
|
+
ternary: "ternary expression",
|
|
14530
|
+
comparison: "comparison",
|
|
14531
|
+
logical: "logical expression",
|
|
14532
|
+
"call-with-args": "method call with arguments"
|
|
14533
|
+
};
|
|
14534
|
+
function unsupportedExprMessage(info) {
|
|
14535
|
+
const v = JSON.stringify(info.value);
|
|
14536
|
+
const label = UNSUPPORTED_EXPR_LABEL[info.detected] ?? "expression";
|
|
14537
|
+
switch (info.role) {
|
|
14538
|
+
case "attr":
|
|
14539
|
+
return `Unsupported ${label} ${v} in dynamic attribute ':${info.attr}'`;
|
|
14540
|
+
case "directive":
|
|
14541
|
+
return `Unsupported ${label} ${v} in directive '@${info.directive}'`;
|
|
14542
|
+
case "if":
|
|
14543
|
+
return `Unsupported ${label} ${v} in '@if.${info.attr}' condition`;
|
|
14544
|
+
case "x-op":
|
|
14545
|
+
return `Unsupported ${label} ${v} in <x ${info.op}>`;
|
|
14546
|
+
default:
|
|
14547
|
+
return `Unsupported ${label} ${v}`;
|
|
14548
|
+
}
|
|
14549
|
+
}
|
|
14469
14550
|
function badValueMessage(info) {
|
|
14470
14551
|
const v = JSON.stringify(info.value);
|
|
14471
14552
|
switch (info.role) {
|
|
@@ -14569,6 +14650,12 @@ function lintIdToMessage(id, info) {
|
|
|
14569
14650
|
}
|
|
14570
14651
|
case "BAD_VALUE":
|
|
14571
14652
|
return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
|
|
14653
|
+
case "UNSUPPORTED_EXPR_SYNTAX":
|
|
14654
|
+
return `${unsupportedExprMessage(info)}${fmtTagSuffix(info)}`;
|
|
14655
|
+
case "REDUNDANT_TEMPLATE_STRING":
|
|
14656
|
+
return `Redundant template string — '{${info.simpler}}' should be just '${info.simpler}'${fmtOriginSuffix(info)}`;
|
|
14657
|
+
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
14658
|
+
return `Unknown component spec key '${info.key}' — value will be ignored at runtime`;
|
|
14572
14659
|
case "HTML_TAG_NAME_HAS_UPPERCASE":
|
|
14573
14660
|
return `Tag <${info.raw}> will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
|
|
14574
14661
|
case "HTML_SVG_TAG_WILL_LOWERCASE":
|
|
@@ -14627,6 +14714,8 @@ function suggestionToMessage(suggestion) {
|
|
|
14627
14714
|
return `use '${suggestion.to}' instead of '${suggestion.from}'`;
|
|
14628
14715
|
case "wrap":
|
|
14629
14716
|
return `wrap it in ${suggestion.to}`;
|
|
14717
|
+
case "rephrase":
|
|
14718
|
+
return suggestion.text ?? null;
|
|
14630
14719
|
default:
|
|
14631
14720
|
return null;
|
|
14632
14721
|
}
|
package/dist/tutuca-dev.js
CHANGED
|
@@ -3792,7 +3792,15 @@ class StrTplVal extends VarVal {
|
|
|
3792
3792
|
vals[i] = val;
|
|
3793
3793
|
allConsts &&= val instanceof ConstVal;
|
|
3794
3794
|
}
|
|
3795
|
-
|
|
3795
|
+
if (allConsts)
|
|
3796
|
+
return new ConstVal(vals.map((v) => v.val).join(""));
|
|
3797
|
+
let lo = 0;
|
|
3798
|
+
let hi = vals.length;
|
|
3799
|
+
while (lo < hi && vals[lo] instanceof ConstVal && vals[lo].val === "")
|
|
3800
|
+
lo++;
|
|
3801
|
+
while (hi > lo && vals[hi - 1] instanceof ConstVal && vals[hi - 1].val === "")
|
|
3802
|
+
hi--;
|
|
3803
|
+
return new StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
|
|
3796
3804
|
}
|
|
3797
3805
|
}
|
|
3798
3806
|
|
|
@@ -7275,6 +7283,8 @@ function closestName(name, candidates, maxDistance = 2) {
|
|
|
7275
7283
|
}
|
|
7276
7284
|
|
|
7277
7285
|
// tools/core/lint-check.js
|
|
7286
|
+
var KNOWN_COMPONENT_SPEC_KEYS = new Set("name view style commonStyle globalStyle input receive bubble response alter on views dynamic fields methods statics".split(" "));
|
|
7287
|
+
var EMPTY_SET = new Set;
|
|
7278
7288
|
var KNOWN_DIRECTIVE_NAMES = new Set([
|
|
7279
7289
|
"dangerouslysetinnerhtml",
|
|
7280
7290
|
"slot",
|
|
@@ -7310,6 +7320,9 @@ var UNKNOWN_X_OP = "UNKNOWN_X_OP";
|
|
|
7310
7320
|
var UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR";
|
|
7311
7321
|
var MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX";
|
|
7312
7322
|
var BAD_VALUE = "BAD_VALUE";
|
|
7323
|
+
var UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX";
|
|
7324
|
+
var REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING";
|
|
7325
|
+
var UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY";
|
|
7313
7326
|
var PARSE_ISSUE_KIND_TO_LINT_ID = {
|
|
7314
7327
|
"unknown-directive": UNKNOWN_DIRECTIVE,
|
|
7315
7328
|
"unknown-x-op": UNKNOWN_X_OP,
|
|
@@ -7366,8 +7379,31 @@ function replaceNameSuggestion(name, candidates) {
|
|
|
7366
7379
|
const close = closestName(name, candidates);
|
|
7367
7380
|
return close ? { kind: "replace-name", from: name, to: close } : null;
|
|
7368
7381
|
}
|
|
7369
|
-
function
|
|
7382
|
+
function classifyBadValue(value) {
|
|
7383
|
+
if (typeof value !== "string")
|
|
7384
|
+
return null;
|
|
7385
|
+
const s = value.trim();
|
|
7386
|
+
if (s === "")
|
|
7387
|
+
return null;
|
|
7388
|
+
if (/\s\?\s.+\s:\s/.test(s))
|
|
7389
|
+
return "ternary";
|
|
7390
|
+
if (/===|!==|==|!=|<=|>=|\s<\s|\s>\s/.test(s))
|
|
7391
|
+
return "comparison";
|
|
7392
|
+
if (/&&|\|\|/.test(s))
|
|
7393
|
+
return "logical";
|
|
7394
|
+
if (/^\.[A-Za-z_]\w*\s+\S/.test(s))
|
|
7395
|
+
return "call-with-args";
|
|
7396
|
+
return null;
|
|
7397
|
+
}
|
|
7398
|
+
var UNSUPPORTED_EXPR_GUIDANCE = {
|
|
7399
|
+
ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed field on the component that returns the value, then reference it as '.methodName'.",
|
|
7400
|
+
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like '.isFooSelected' that returns the boolean, then reference it as '.isFooSelected'.",
|
|
7401
|
+
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '.methodName'.",
|
|
7402
|
+
"call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('.methodName') and read what you need from component state, or split into per-case methods."
|
|
7403
|
+
};
|
|
7404
|
+
function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SET } = {}) {
|
|
7370
7405
|
return lx.push({ componentName: Comp.name }, () => {
|
|
7406
|
+
checkUnknownSpecKeys(lx, Comp, wellKnownExtras);
|
|
7371
7407
|
const referencedAlters = new Set;
|
|
7372
7408
|
const referencedInputs = new Set;
|
|
7373
7409
|
checkEventHandlersHaveImpls(lx, Comp, referencedInputs);
|
|
@@ -7405,6 +7441,13 @@ function checkParseIssues(lx, view) {
|
|
|
7405
7441
|
const id = PARSE_ISSUE_KIND_TO_LINT_ID[kind];
|
|
7406
7442
|
if (!id)
|
|
7407
7443
|
continue;
|
|
7444
|
+
if (kind === "bad-value") {
|
|
7445
|
+
const detected = classifyBadValue(info.value);
|
|
7446
|
+
if (detected) {
|
|
7447
|
+
lx.error(UNSUPPORTED_EXPR_SYNTAX, { ...info, detected }, { kind: "rephrase", from: info.value, text: UNSUPPORTED_EXPR_GUIDANCE[detected] });
|
|
7448
|
+
continue;
|
|
7449
|
+
}
|
|
7450
|
+
}
|
|
7408
7451
|
const atPrefixKnown = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
|
|
7409
7452
|
const isAtPrefixedTypo = atPrefixKnown && info.name?.startsWith("@") && atPrefixKnown.has(info.name.slice(1));
|
|
7410
7453
|
let suggestion = null;
|
|
@@ -7606,7 +7649,12 @@ function checkConsistentAttrVal(lx, val, fields, proto, scope, alter, referenced
|
|
|
7606
7649
|
lx.error(UNKNOWN_HANDLER_ARG_NAME, { ...errCtx, name: val.name }, replaceNameSuggestion(val.name, KNOWN_HANDLER_NAMES));
|
|
7607
7650
|
}
|
|
7608
7651
|
} else if (valName === "StrTplVal") {
|
|
7609
|
-
|
|
7652
|
+
const vs = val.vals;
|
|
7653
|
+
if (vs.length === 1) {
|
|
7654
|
+
const simpler = String(vs[0]);
|
|
7655
|
+
lx.warn(REDUNDANT_TEMPLATE_STRING, { ...errCtx, simpler }, { kind: "rewrite", from: `{${simpler}}`, to: simpler });
|
|
7656
|
+
}
|
|
7657
|
+
for (const subVal of vs) {
|
|
7610
7658
|
checkConsistentAttrVal(lx, subVal, fields, proto, scope, alter, referencedAlters, skipNameVal, errCtx);
|
|
7611
7659
|
}
|
|
7612
7660
|
} else if (valName === "AlterHandlerNameVal") {
|
|
@@ -7729,6 +7777,18 @@ function checkConsistentAttrs(lx, Comp, referencedAlters) {
|
|
|
7729
7777
|
});
|
|
7730
7778
|
}
|
|
7731
7779
|
}
|
|
7780
|
+
function checkUnknownSpecKeys(lx, Comp, wellKnownExtras) {
|
|
7781
|
+
const extra = Comp.extra;
|
|
7782
|
+
if (!extra)
|
|
7783
|
+
return;
|
|
7784
|
+
let candidates = null;
|
|
7785
|
+
for (const key of Object.keys(extra)) {
|
|
7786
|
+
if (wellKnownExtras.has(key))
|
|
7787
|
+
continue;
|
|
7788
|
+
candidates ??= [...KNOWN_COMPONENT_SPEC_KEYS, ...wellKnownExtras];
|
|
7789
|
+
lx.warn(UNKNOWN_COMPONENT_SPEC_KEY, { key }, replaceNameSuggestion(key, candidates));
|
|
7790
|
+
}
|
|
7791
|
+
}
|
|
7732
7792
|
function checkUnreferencedAlterHandlers(lx, Comp, referencedAlters) {
|
|
7733
7793
|
for (const name in Comp.alter) {
|
|
7734
7794
|
if (!referencedAlters.has(name)) {
|
|
@@ -8215,6 +8275,28 @@ function reportTestReportToConsole(report) {
|
|
|
8215
8275
|
}
|
|
8216
8276
|
|
|
8217
8277
|
// tools/format/lint.js
|
|
8278
|
+
var UNSUPPORTED_EXPR_LABEL = {
|
|
8279
|
+
ternary: "ternary expression",
|
|
8280
|
+
comparison: "comparison",
|
|
8281
|
+
logical: "logical expression",
|
|
8282
|
+
"call-with-args": "method call with arguments"
|
|
8283
|
+
};
|
|
8284
|
+
function unsupportedExprMessage(info) {
|
|
8285
|
+
const v = JSON.stringify(info.value);
|
|
8286
|
+
const label = UNSUPPORTED_EXPR_LABEL[info.detected] ?? "expression";
|
|
8287
|
+
switch (info.role) {
|
|
8288
|
+
case "attr":
|
|
8289
|
+
return `Unsupported ${label} ${v} in dynamic attribute ':${info.attr}'`;
|
|
8290
|
+
case "directive":
|
|
8291
|
+
return `Unsupported ${label} ${v} in directive '@${info.directive}'`;
|
|
8292
|
+
case "if":
|
|
8293
|
+
return `Unsupported ${label} ${v} in '@if.${info.attr}' condition`;
|
|
8294
|
+
case "x-op":
|
|
8295
|
+
return `Unsupported ${label} ${v} in <x ${info.op}>`;
|
|
8296
|
+
default:
|
|
8297
|
+
return `Unsupported ${label} ${v}`;
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
8218
8300
|
function badValueMessage(info) {
|
|
8219
8301
|
const v = JSON.stringify(info.value);
|
|
8220
8302
|
switch (info.role) {
|
|
@@ -8318,6 +8400,12 @@ function lintIdToMessage(id, info) {
|
|
|
8318
8400
|
}
|
|
8319
8401
|
case "BAD_VALUE":
|
|
8320
8402
|
return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
|
|
8403
|
+
case "UNSUPPORTED_EXPR_SYNTAX":
|
|
8404
|
+
return `${unsupportedExprMessage(info)}${fmtTagSuffix(info)}`;
|
|
8405
|
+
case "REDUNDANT_TEMPLATE_STRING":
|
|
8406
|
+
return `Redundant template string — '{${info.simpler}}' should be just '${info.simpler}'${fmtOriginSuffix(info)}`;
|
|
8407
|
+
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
8408
|
+
return `Unknown component spec key '${info.key}' — value will be ignored at runtime`;
|
|
8321
8409
|
case "HTML_TAG_NAME_HAS_UPPERCASE":
|
|
8322
8410
|
return `Tag <${info.raw}> will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
|
|
8323
8411
|
case "HTML_SVG_TAG_WILL_LOWERCASE":
|
|
@@ -8376,6 +8464,8 @@ function suggestionToMessage(suggestion) {
|
|
|
8376
8464
|
return `use '${suggestion.to}' instead of '${suggestion.from}'`;
|
|
8377
8465
|
case "wrap":
|
|
8378
8466
|
return `wrap it in ${suggestion.to}`;
|
|
8467
|
+
case "rephrase":
|
|
8468
|
+
return suggestion.text ?? null;
|
|
8379
8469
|
default:
|
|
8380
8470
|
return null;
|
|
8381
8471
|
}
|
|
@@ -8522,6 +8612,8 @@ class DynamicAlias extends Dynamic {
|
|
|
8522
8612
|
}
|
|
8523
8613
|
}
|
|
8524
8614
|
var isString = (v) => typeof v === "string";
|
|
8615
|
+
var _rawSpecKeys = "name view style commonStyle globalStyle input receive bubble response alter on views dynamic fields methods statics";
|
|
8616
|
+
var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
|
|
8525
8617
|
var _compId = 0;
|
|
8526
8618
|
|
|
8527
8619
|
class Component {
|
|
@@ -8546,6 +8638,10 @@ class Component {
|
|
|
8546
8638
|
this._rawDynamic = o.dynamic ?? {};
|
|
8547
8639
|
this.dynamic = {};
|
|
8548
8640
|
this.scope = null;
|
|
8641
|
+
this.extra = {};
|
|
8642
|
+
for (const key of Object.keys(o))
|
|
8643
|
+
if (!KNOWN_SPEC_KEYS.has(key))
|
|
8644
|
+
this.extra[key] = o[key];
|
|
8549
8645
|
}
|
|
8550
8646
|
compile(ParseContext2) {
|
|
8551
8647
|
for (const key in this._rawDynamic) {
|
|
@@ -13435,8 +13531,8 @@ class SetImpl extends SetCollectionImpl {
|
|
|
13435
13531
|
}
|
|
13436
13532
|
Set2.isSet = isSet;
|
|
13437
13533
|
var makeSet = (map, ownerID) => new SetImpl(map, ownerID);
|
|
13438
|
-
var
|
|
13439
|
-
var emptySet = () =>
|
|
13534
|
+
var EMPTY_SET2;
|
|
13535
|
+
var emptySet = () => EMPTY_SET2 || (EMPTY_SET2 = makeSet(emptyMap()));
|
|
13440
13536
|
function filterByIters(set3, iters, shouldRemove) {
|
|
13441
13537
|
if (iters.length === 0) {
|
|
13442
13538
|
return set3;
|
|
@@ -14283,16 +14379,16 @@ function extendProtoTruthy(proto, name, uname) {
|
|
|
14283
14379
|
return !this.get(name);
|
|
14284
14380
|
};
|
|
14285
14381
|
}
|
|
14286
|
-
var
|
|
14382
|
+
var EMPTY_SET3 = Set2();
|
|
14287
14383
|
var isetCoercer = (v) => Array.isArray(v) ? Set2(v) : v instanceof Set ? Set2(v) : null;
|
|
14288
14384
|
|
|
14289
14385
|
class FieldSet extends Field {
|
|
14290
|
-
constructor(name, defaultValue =
|
|
14386
|
+
constructor(name, defaultValue = EMPTY_SET3) {
|
|
14291
14387
|
super("set", name, CHECK_TYPE_SET, isetCoercer, defaultValue);
|
|
14292
14388
|
}
|
|
14293
14389
|
extendProtoForType(proto, uname) {
|
|
14294
14390
|
const { name } = this;
|
|
14295
|
-
extendProtoSized(proto, name, uname,
|
|
14391
|
+
extendProtoSized(proto, name, uname, EMPTY_SET3);
|
|
14296
14392
|
proto[`addIn${uname}`] = function(v) {
|
|
14297
14393
|
return this.set(name, this.get(name).add(v));
|
|
14298
14394
|
};
|
|
@@ -15062,6 +15158,7 @@ export {
|
|
|
15062
15158
|
compileClassesToStyle,
|
|
15063
15159
|
checkComponent,
|
|
15064
15160
|
check,
|
|
15161
|
+
UNSUPPORTED_EXPR_SYNTAX,
|
|
15065
15162
|
UNKNOWN_X_OP,
|
|
15066
15163
|
UNKNOWN_X_ATTR,
|
|
15067
15164
|
UNKNOWN_REQUEST_NAME,
|
|
@@ -15069,6 +15166,7 @@ export {
|
|
|
15069
15166
|
UNKNOWN_HANDLER_ARG_NAME,
|
|
15070
15167
|
UNKNOWN_EVENT_MODIFIER,
|
|
15071
15168
|
UNKNOWN_DIRECTIVE,
|
|
15169
|
+
UNKNOWN_COMPONENT_SPEC_KEY,
|
|
15072
15170
|
UNKNOWN_COMPONENT_NAME,
|
|
15073
15171
|
TestResult,
|
|
15074
15172
|
TestReport,
|
|
@@ -15085,6 +15183,7 @@ export {
|
|
|
15085
15183
|
Record,
|
|
15086
15184
|
Range,
|
|
15087
15185
|
RENDER_IT_OUTSIDE_OF_LOOP,
|
|
15186
|
+
REDUNDANT_TEMPLATE_STRING,
|
|
15088
15187
|
ParseContext,
|
|
15089
15188
|
PairSorting,
|
|
15090
15189
|
OrderedSet,
|