tutuca 0.9.49 → 0.9.51
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/README.md +1 -3
- package/dist/tutuca-cli.js +56 -5
- package/dist/tutuca-dev.js +57 -0
- package/dist/tutuca-dev.min.js +2 -2
- package/package.json +3 -11
package/README.md
CHANGED
|
@@ -73,11 +73,9 @@ in the storybook shape `{ title, description?, items: [{ title, description?, va
|
|
|
73
73
|
|
|
74
74
|
```sh
|
|
75
75
|
npm install --save-dev tutuca
|
|
76
|
-
# prettier is optional, only needed for --pretty
|
|
77
|
-
npm install --save-dev prettier
|
|
78
76
|
```
|
|
79
77
|
|
|
80
|
-
The package exposes `tutuca` via `bin`, so `npx tutuca` (or a global `npm i -g tutuca`) just works. `jsdom`
|
|
78
|
+
The package exposes `tutuca` via `bin`, so `npx tutuca` (or a global `npm i -g tutuca`) just works. `jsdom` (needed by `render` and `lint`) and `prettier` (used by `--pretty`) ship as regular dependencies and are installed automatically.
|
|
81
79
|
|
|
82
80
|
### Commands
|
|
83
81
|
|
package/dist/tutuca-cli.js
CHANGED
|
@@ -7755,6 +7755,22 @@ function replaceNameSuggestion(name, candidates) {
|
|
|
7755
7755
|
const close = closestName(name, candidates);
|
|
7756
7756
|
return close ? { kind: "replace-name", from: name, to: close } : null;
|
|
7757
7757
|
}
|
|
7758
|
+
function classifyBadValue(value) {
|
|
7759
|
+
if (typeof value !== "string")
|
|
7760
|
+
return null;
|
|
7761
|
+
const s = value.trim();
|
|
7762
|
+
if (s === "")
|
|
7763
|
+
return null;
|
|
7764
|
+
if (/\s\?\s.+\s:\s/.test(s))
|
|
7765
|
+
return "ternary";
|
|
7766
|
+
if (/===|!==|==|!=|<=|>=|\s<\s|\s>\s/.test(s))
|
|
7767
|
+
return "comparison";
|
|
7768
|
+
if (/&&|\|\|/.test(s))
|
|
7769
|
+
return "logical";
|
|
7770
|
+
if (/^\.[A-Za-z_]\w*\s+\S/.test(s))
|
|
7771
|
+
return "call-with-args";
|
|
7772
|
+
return null;
|
|
7773
|
+
}
|
|
7758
7774
|
function checkComponent(Comp, lx = new LintContext) {
|
|
7759
7775
|
return lx.push({ componentName: Comp.name }, () => {
|
|
7760
7776
|
const referencedAlters = new Set;
|
|
@@ -7790,6 +7806,13 @@ function checkParseIssues(lx, view) {
|
|
|
7790
7806
|
const id = PARSE_ISSUE_KIND_TO_LINT_ID[kind];
|
|
7791
7807
|
if (!id)
|
|
7792
7808
|
continue;
|
|
7809
|
+
if (kind === "bad-value") {
|
|
7810
|
+
const detected = classifyBadValue(info.value);
|
|
7811
|
+
if (detected) {
|
|
7812
|
+
lx.error(UNSUPPORTED_EXPR_SYNTAX, { ...info, detected }, { kind: "rephrase", from: info.value, text: UNSUPPORTED_EXPR_GUIDANCE[detected] });
|
|
7813
|
+
continue;
|
|
7814
|
+
}
|
|
7815
|
+
}
|
|
7793
7816
|
const atPrefixKnown = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
|
|
7794
7817
|
const isAtPrefixedTypo = atPrefixKnown && info.name?.startsWith("@") && atPrefixKnown.has(info.name.slice(1));
|
|
7795
7818
|
let suggestion = null;
|
|
@@ -8126,7 +8149,7 @@ class LintContext {
|
|
|
8126
8149
|
this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
|
|
8127
8150
|
}
|
|
8128
8151
|
}
|
|
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;
|
|
8152
|
+
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", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", 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
8153
|
var init_lint_check = __esm(() => {
|
|
8131
8154
|
init_anode();
|
|
8132
8155
|
init_htmllinter();
|
|
@@ -8169,6 +8192,12 @@ var init_lint_check = __esm(() => {
|
|
|
8169
8192
|
"unknown-x-op": X_KNOWN_OP_NAMES,
|
|
8170
8193
|
"unknown-x-attr": X_KNOWN_ATTR_NAMES
|
|
8171
8194
|
};
|
|
8195
|
+
UNSUPPORTED_EXPR_GUIDANCE = {
|
|
8196
|
+
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'.",
|
|
8197
|
+
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like '.isFooSelected' that returns the boolean, then reference it as '.isFooSelected'.",
|
|
8198
|
+
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '.methodName'.",
|
|
8199
|
+
"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."
|
|
8200
|
+
};
|
|
8172
8201
|
HTML_LINT_OPTS = {
|
|
8173
8202
|
fragmentContext: "template",
|
|
8174
8203
|
transparentTagPrefixes: ["x"]
|
|
@@ -14336,7 +14365,6 @@ GLOBAL FLAGS
|
|
|
14336
14365
|
-o, --output <file> Write to <file> instead of stdout.
|
|
14337
14366
|
--pretty Pretty-print HTML (md/html formats) via
|
|
14338
14367
|
prettier; JSON formatter uses indent 2.
|
|
14339
|
-
Requires \`prettier\` to be installed.
|
|
14340
14368
|
-h, --help Show this help.
|
|
14341
14369
|
--module <path> Alternative to first-positional module path.
|
|
14342
14370
|
|
|
@@ -14347,9 +14375,6 @@ EXIT CODES
|
|
|
14347
14375
|
3 render crash
|
|
14348
14376
|
4 test failures
|
|
14349
14377
|
|
|
14350
|
-
ENVIRONMENT
|
|
14351
|
-
\`prettier\` is an optional peer dep, only used by --pretty.
|
|
14352
|
-
|
|
14353
14378
|
EXAMPLES
|
|
14354
14379
|
# Inspect a module
|
|
14355
14380
|
tutuca ./src/components.js info
|
|
@@ -14470,6 +14495,28 @@ function makeFormatter(name, table) {
|
|
|
14470
14495
|
}
|
|
14471
14496
|
|
|
14472
14497
|
// tools/format/lint.js
|
|
14498
|
+
var UNSUPPORTED_EXPR_LABEL = {
|
|
14499
|
+
ternary: "ternary expression",
|
|
14500
|
+
comparison: "comparison",
|
|
14501
|
+
logical: "logical expression",
|
|
14502
|
+
"call-with-args": "method call with arguments"
|
|
14503
|
+
};
|
|
14504
|
+
function unsupportedExprMessage(info) {
|
|
14505
|
+
const v = JSON.stringify(info.value);
|
|
14506
|
+
const label = UNSUPPORTED_EXPR_LABEL[info.detected] ?? "expression";
|
|
14507
|
+
switch (info.role) {
|
|
14508
|
+
case "attr":
|
|
14509
|
+
return `Unsupported ${label} ${v} in dynamic attribute ':${info.attr}'`;
|
|
14510
|
+
case "directive":
|
|
14511
|
+
return `Unsupported ${label} ${v} in directive '@${info.directive}'`;
|
|
14512
|
+
case "if":
|
|
14513
|
+
return `Unsupported ${label} ${v} in '@if.${info.attr}' condition`;
|
|
14514
|
+
case "x-op":
|
|
14515
|
+
return `Unsupported ${label} ${v} in <x ${info.op}>`;
|
|
14516
|
+
default:
|
|
14517
|
+
return `Unsupported ${label} ${v}`;
|
|
14518
|
+
}
|
|
14519
|
+
}
|
|
14473
14520
|
function badValueMessage(info) {
|
|
14474
14521
|
const v = JSON.stringify(info.value);
|
|
14475
14522
|
switch (info.role) {
|
|
@@ -14573,6 +14620,8 @@ function lintIdToMessage(id, info) {
|
|
|
14573
14620
|
}
|
|
14574
14621
|
case "BAD_VALUE":
|
|
14575
14622
|
return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
|
|
14623
|
+
case "UNSUPPORTED_EXPR_SYNTAX":
|
|
14624
|
+
return `${unsupportedExprMessage(info)}${fmtTagSuffix(info)}`;
|
|
14576
14625
|
case "HTML_TAG_NAME_HAS_UPPERCASE":
|
|
14577
14626
|
return `Tag <${info.raw}> will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
|
|
14578
14627
|
case "HTML_SVG_TAG_WILL_LOWERCASE":
|
|
@@ -14631,6 +14680,8 @@ function suggestionToMessage(suggestion) {
|
|
|
14631
14680
|
return `use '${suggestion.to}' instead of '${suggestion.from}'`;
|
|
14632
14681
|
case "wrap":
|
|
14633
14682
|
return `wrap it in ${suggestion.to}`;
|
|
14683
|
+
case "rephrase":
|
|
14684
|
+
return suggestion.text ?? null;
|
|
14634
14685
|
default:
|
|
14635
14686
|
return null;
|
|
14636
14687
|
}
|
package/dist/tutuca-dev.js
CHANGED
|
@@ -7310,6 +7310,7 @@ var UNKNOWN_X_OP = "UNKNOWN_X_OP";
|
|
|
7310
7310
|
var UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR";
|
|
7311
7311
|
var MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX";
|
|
7312
7312
|
var BAD_VALUE = "BAD_VALUE";
|
|
7313
|
+
var UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX";
|
|
7313
7314
|
var PARSE_ISSUE_KIND_TO_LINT_ID = {
|
|
7314
7315
|
"unknown-directive": UNKNOWN_DIRECTIVE,
|
|
7315
7316
|
"unknown-x-op": UNKNOWN_X_OP,
|
|
@@ -7366,6 +7367,28 @@ function replaceNameSuggestion(name, candidates) {
|
|
|
7366
7367
|
const close = closestName(name, candidates);
|
|
7367
7368
|
return close ? { kind: "replace-name", from: name, to: close } : null;
|
|
7368
7369
|
}
|
|
7370
|
+
function classifyBadValue(value) {
|
|
7371
|
+
if (typeof value !== "string")
|
|
7372
|
+
return null;
|
|
7373
|
+
const s = value.trim();
|
|
7374
|
+
if (s === "")
|
|
7375
|
+
return null;
|
|
7376
|
+
if (/\s\?\s.+\s:\s/.test(s))
|
|
7377
|
+
return "ternary";
|
|
7378
|
+
if (/===|!==|==|!=|<=|>=|\s<\s|\s>\s/.test(s))
|
|
7379
|
+
return "comparison";
|
|
7380
|
+
if (/&&|\|\|/.test(s))
|
|
7381
|
+
return "logical";
|
|
7382
|
+
if (/^\.[A-Za-z_]\w*\s+\S/.test(s))
|
|
7383
|
+
return "call-with-args";
|
|
7384
|
+
return null;
|
|
7385
|
+
}
|
|
7386
|
+
var UNSUPPORTED_EXPR_GUIDANCE = {
|
|
7387
|
+
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'.",
|
|
7388
|
+
comparison: "Comparisons aren't supported in dynamic attributes. Define a method like '.isFooSelected' that returns the boolean, then reference it as '.isFooSelected'.",
|
|
7389
|
+
logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '.methodName'.",
|
|
7390
|
+
"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."
|
|
7391
|
+
};
|
|
7369
7392
|
function checkComponent(Comp, lx = new LintContext) {
|
|
7370
7393
|
return lx.push({ componentName: Comp.name }, () => {
|
|
7371
7394
|
const referencedAlters = new Set;
|
|
@@ -7405,6 +7428,13 @@ function checkParseIssues(lx, view) {
|
|
|
7405
7428
|
const id = PARSE_ISSUE_KIND_TO_LINT_ID[kind];
|
|
7406
7429
|
if (!id)
|
|
7407
7430
|
continue;
|
|
7431
|
+
if (kind === "bad-value") {
|
|
7432
|
+
const detected = classifyBadValue(info.value);
|
|
7433
|
+
if (detected) {
|
|
7434
|
+
lx.error(UNSUPPORTED_EXPR_SYNTAX, { ...info, detected }, { kind: "rephrase", from: info.value, text: UNSUPPORTED_EXPR_GUIDANCE[detected] });
|
|
7435
|
+
continue;
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7408
7438
|
const atPrefixKnown = AT_PREFIX_HINT_KNOWN_BY_KIND[kind];
|
|
7409
7439
|
const isAtPrefixedTypo = atPrefixKnown && info.name?.startsWith("@") && atPrefixKnown.has(info.name.slice(1));
|
|
7410
7440
|
let suggestion = null;
|
|
@@ -8215,6 +8245,28 @@ function reportTestReportToConsole(report) {
|
|
|
8215
8245
|
}
|
|
8216
8246
|
|
|
8217
8247
|
// tools/format/lint.js
|
|
8248
|
+
var UNSUPPORTED_EXPR_LABEL = {
|
|
8249
|
+
ternary: "ternary expression",
|
|
8250
|
+
comparison: "comparison",
|
|
8251
|
+
logical: "logical expression",
|
|
8252
|
+
"call-with-args": "method call with arguments"
|
|
8253
|
+
};
|
|
8254
|
+
function unsupportedExprMessage(info) {
|
|
8255
|
+
const v = JSON.stringify(info.value);
|
|
8256
|
+
const label = UNSUPPORTED_EXPR_LABEL[info.detected] ?? "expression";
|
|
8257
|
+
switch (info.role) {
|
|
8258
|
+
case "attr":
|
|
8259
|
+
return `Unsupported ${label} ${v} in dynamic attribute ':${info.attr}'`;
|
|
8260
|
+
case "directive":
|
|
8261
|
+
return `Unsupported ${label} ${v} in directive '@${info.directive}'`;
|
|
8262
|
+
case "if":
|
|
8263
|
+
return `Unsupported ${label} ${v} in '@if.${info.attr}' condition`;
|
|
8264
|
+
case "x-op":
|
|
8265
|
+
return `Unsupported ${label} ${v} in <x ${info.op}>`;
|
|
8266
|
+
default:
|
|
8267
|
+
return `Unsupported ${label} ${v}`;
|
|
8268
|
+
}
|
|
8269
|
+
}
|
|
8218
8270
|
function badValueMessage(info) {
|
|
8219
8271
|
const v = JSON.stringify(info.value);
|
|
8220
8272
|
switch (info.role) {
|
|
@@ -8318,6 +8370,8 @@ function lintIdToMessage(id, info) {
|
|
|
8318
8370
|
}
|
|
8319
8371
|
case "BAD_VALUE":
|
|
8320
8372
|
return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
|
|
8373
|
+
case "UNSUPPORTED_EXPR_SYNTAX":
|
|
8374
|
+
return `${unsupportedExprMessage(info)}${fmtTagSuffix(info)}`;
|
|
8321
8375
|
case "HTML_TAG_NAME_HAS_UPPERCASE":
|
|
8322
8376
|
return `Tag <${info.raw}> will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
|
|
8323
8377
|
case "HTML_SVG_TAG_WILL_LOWERCASE":
|
|
@@ -8376,6 +8430,8 @@ function suggestionToMessage(suggestion) {
|
|
|
8376
8430
|
return `use '${suggestion.to}' instead of '${suggestion.from}'`;
|
|
8377
8431
|
case "wrap":
|
|
8378
8432
|
return `wrap it in ${suggestion.to}`;
|
|
8433
|
+
case "rephrase":
|
|
8434
|
+
return suggestion.text ?? null;
|
|
8379
8435
|
default:
|
|
8380
8436
|
return null;
|
|
8381
8437
|
}
|
|
@@ -15062,6 +15118,7 @@ export {
|
|
|
15062
15118
|
compileClassesToStyle,
|
|
15063
15119
|
checkComponent,
|
|
15064
15120
|
check,
|
|
15121
|
+
UNSUPPORTED_EXPR_SYNTAX,
|
|
15065
15122
|
UNKNOWN_X_OP,
|
|
15066
15123
|
UNKNOWN_X_ATTR,
|
|
15067
15124
|
UNKNOWN_REQUEST_NAME,
|