tutuca 0.9.92 → 0.9.93
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 +80 -1
- package/dist/tutuca-dev.ext.js +72 -0
- package/dist/tutuca-dev.js +72 -0
- package/dist/tutuca-dev.min.js +3 -2
- package/package.json +1 -1
- package/skill/tutuca/core.md +17 -0
package/dist/tutuca-cli.js
CHANGED
|
@@ -12963,6 +12963,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
|
|
|
12963
12963
|
checkProvidesAreAddressable(lx, Comp);
|
|
12964
12964
|
checkLookupShapes(lx, Comp);
|
|
12965
12965
|
checkHandlersNotAsync(lx, Comp);
|
|
12966
|
+
checkScopedStyleTopLevel(lx, Comp);
|
|
12966
12967
|
const referencedAlters = new Set;
|
|
12967
12968
|
const referencedInputs = new Set;
|
|
12968
12969
|
const referencedDynamics = new Set;
|
|
@@ -13376,6 +13377,64 @@ function checkHandlersNotAsync(lx, Comp) {
|
|
|
13376
13377
|
}
|
|
13377
13378
|
}
|
|
13378
13379
|
}
|
|
13380
|
+
function blankCssNoise(css) {
|
|
13381
|
+
return css.replace(/\/\*[\s\S]*?\*\//g, blankRun).replace(/"(?:[^"\\\n]|\\.)*"/g, blankRun).replace(/'(?:[^'\\\n]|\\.)*'/g, blankRun);
|
|
13382
|
+
}
|
|
13383
|
+
function offsetToLineCol2(str, index) {
|
|
13384
|
+
let line = 1;
|
|
13385
|
+
let lineStart = 0;
|
|
13386
|
+
for (let i = 0;i < index; i++) {
|
|
13387
|
+
if (str[i] === `
|
|
13388
|
+
`) {
|
|
13389
|
+
line++;
|
|
13390
|
+
lineStart = i + 1;
|
|
13391
|
+
}
|
|
13392
|
+
}
|
|
13393
|
+
return { line, column: index - lineStart + 1 };
|
|
13394
|
+
}
|
|
13395
|
+
function suppressedLines(css) {
|
|
13396
|
+
const lines = new Set;
|
|
13397
|
+
IGNORE_DIRECTIVE.lastIndex = 0;
|
|
13398
|
+
for (let m = IGNORE_DIRECTIVE.exec(css);m !== null; m = IGNORE_DIRECTIVE.exec(css)) {
|
|
13399
|
+
lines.add(offsetToLineCol2(css, m.index).line);
|
|
13400
|
+
}
|
|
13401
|
+
return lines;
|
|
13402
|
+
}
|
|
13403
|
+
function scanScopedCss(lx, css, key) {
|
|
13404
|
+
if (!css)
|
|
13405
|
+
return;
|
|
13406
|
+
const ignore = suppressedLines(css);
|
|
13407
|
+
const clean = blankCssNoise(css);
|
|
13408
|
+
const report = (id, info) => {
|
|
13409
|
+
if (ignore.has(info.location.line))
|
|
13410
|
+
return;
|
|
13411
|
+
lx.error(id, { key, ...info }, { kind: "rephrase", text: STYLE_TO_GLOBAL_HELP });
|
|
13412
|
+
};
|
|
13413
|
+
NON_NESTABLE_AT_RULE.lastIndex = 0;
|
|
13414
|
+
for (let m = NON_NESTABLE_AT_RULE.exec(clean);m !== null; m = NON_NESTABLE_AT_RULE.exec(clean)) {
|
|
13415
|
+
const at = m.index + m[0].indexOf("@");
|
|
13416
|
+
report(TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE, {
|
|
13417
|
+
atRule: m[1].toLowerCase(),
|
|
13418
|
+
location: offsetToLineCol2(clean, at)
|
|
13419
|
+
});
|
|
13420
|
+
}
|
|
13421
|
+
GLOBAL_LEADING_SELECTOR.lastIndex = 0;
|
|
13422
|
+
for (let m = GLOBAL_LEADING_SELECTOR.exec(clean);m !== null; m = GLOBAL_LEADING_SELECTOR.exec(clean)) {
|
|
13423
|
+
const at = m.index + m[0].indexOf(m[1]);
|
|
13424
|
+
report(GLOBAL_SELECTOR_IN_SCOPED_STYLE, {
|
|
13425
|
+
selector: m[1].toLowerCase(),
|
|
13426
|
+
location: offsetToLineCol2(clean, at)
|
|
13427
|
+
});
|
|
13428
|
+
}
|
|
13429
|
+
}
|
|
13430
|
+
function checkScopedStyleTopLevel(lx, Comp) {
|
|
13431
|
+
scanScopedCss(lx, Comp.commonStyle, "commonStyle");
|
|
13432
|
+
for (const name in Comp.views) {
|
|
13433
|
+
const { style } = Comp.views[name];
|
|
13434
|
+
if (style)
|
|
13435
|
+
lx.push({ viewName: name }, () => scanScopedCss(lx, style, "style"));
|
|
13436
|
+
}
|
|
13437
|
+
}
|
|
13379
13438
|
function checkProvidesAreAddressable(lx, Comp) {
|
|
13380
13439
|
for (const name in Comp._rawProvide) {
|
|
13381
13440
|
if (Comp.provide[name] === undefined) {
|
|
@@ -13452,7 +13511,7 @@ class LintContext {
|
|
|
13452
13511
|
this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
|
|
13453
13512
|
}
|
|
13454
13513
|
}
|
|
13455
|
-
var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", 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", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", 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", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
|
|
13514
|
+
var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", 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", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", 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", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE", GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun = (m) => m.replace(/[^\n]/g, " "), STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
|
|
13456
13515
|
var init_lint_check = __esm(() => {
|
|
13457
13516
|
init_anode();
|
|
13458
13517
|
init_htmllinter();
|
|
@@ -13603,6 +13662,10 @@ var init_lint_check = __esm(() => {
|
|
|
13603
13662
|
};
|
|
13604
13663
|
HANDLER_CHANNELS = ["input", "receive", "bubble", "response", "alter"];
|
|
13605
13664
|
ASYNC_HANDLER_HELP = "Move the async work into a request handler and trigger it with " + "ctx.request('name', args), then handle the result in a synchronous response " + "handler. To coordinate other components, keep the handler synchronous and use " + "ctx.send to deliver a message or ctx.bubble to raise an event.";
|
|
13665
|
+
NON_NESTABLE_AT_RULE = /(?:^|[\s;{}])@(?:-[a-z]+-)?(charset|import|namespace|font-face|keyframes|page|property|counter-style|font-feature-values|font-palette-values|view-transition)\b/gi;
|
|
13666
|
+
GLOBAL_LEADING_SELECTOR = /(?:^|[{}])\s*(html|body|:root)\b[^{};]*\{/gi;
|
|
13667
|
+
IGNORE_DIRECTIVE = /\/\*\s*tutuca-lint-ignore\s*\*\//g;
|
|
13668
|
+
STYLE_TO_GLOBAL_HELP = "Move it to the component's 'globalStyle' key, which is injected without the " + "component-scoping wrapper. To suppress a false positive, put a " + "/* tutuca-lint-ignore */ comment on the same line.";
|
|
13606
13669
|
KNOWN_LOOKUP_KEYS = new Set(["for", "default"]);
|
|
13607
13670
|
LintParseContext = class LintParseContext extends ParseContext {
|
|
13608
13671
|
constructor(document2, Text, Comment) {
|
|
@@ -15103,6 +15166,18 @@ var init_lint_rules = __esm(() => {
|
|
|
15103
15166
|
group: "Handler effects",
|
|
15104
15167
|
summary: "A handler in `input`/`receive`/`bubble`/`response`/`alter` is an async " + "function — handlers must be synchronous; use a request handler, `ctx.send`, " + "or `ctx.bubble` instead."
|
|
15105
15168
|
},
|
|
15169
|
+
{
|
|
15170
|
+
code: TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE,
|
|
15171
|
+
level: "error",
|
|
15172
|
+
group: "Component styles",
|
|
15173
|
+
summary: "A top-level-only at-rule (`@import`, `@keyframes`, `@font-face`, …) appears " + "in `style`/`commonStyle`, which is wrapped in a component-scoped selector " + "where it is invalid and dropped — move it to `globalStyle`."
|
|
15174
|
+
},
|
|
15175
|
+
{
|
|
15176
|
+
code: GLOBAL_SELECTOR_IN_SCOPED_STYLE,
|
|
15177
|
+
level: "error",
|
|
15178
|
+
group: "Component styles",
|
|
15179
|
+
summary: "A rule whose leading selector is `html`/`body`/`:root` appears in " + "`style`/`commonStyle`; once scoped it becomes a descendant selector that " + "never matches — move it to `globalStyle`."
|
|
15180
|
+
},
|
|
15106
15181
|
{
|
|
15107
15182
|
code: COMP_FIELD_BAD_SHAPE,
|
|
15108
15183
|
level: "error",
|
|
@@ -15298,6 +15373,10 @@ function lintIdToMessage(id, info) {
|
|
|
15298
15373
|
return `MathML attribute '${info.raw}' will be rewritten to '${info.canonical}'${fmtLocationSuffix(info)}`;
|
|
15299
15374
|
case "ASYNC_HANDLER":
|
|
15300
15375
|
return `Handler '${info.name}' in '${info.channel}' is an async function — handlers must be synchronous and return the updated state (an async function returns a Promise the framework won't await)`;
|
|
15376
|
+
case "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE":
|
|
15377
|
+
return `'@${info.atRule}' is a top-level-only at-rule, but '${info.key}' is wrapped in a component-scoped selector ([data-cid=…]{…}) where it is invalid and silently dropped — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
|
|
15378
|
+
case "GLOBAL_SELECTOR_IN_SCOPED_STYLE":
|
|
15379
|
+
return `Selector '${info.selector}' targets the document root, but '${info.key}' is wrapped in a component-scoped selector, so it becomes a descendant selector that never matches — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
|
|
15301
15380
|
case "LINT_ERROR":
|
|
15302
15381
|
return info.message;
|
|
15303
15382
|
default:
|
package/dist/tutuca-dev.ext.js
CHANGED
|
@@ -5033,6 +5033,8 @@ var PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING";
|
|
|
5033
5033
|
var UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY";
|
|
5034
5034
|
var COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE";
|
|
5035
5035
|
var ASYNC_HANDLER = "ASYNC_HANDLER";
|
|
5036
|
+
var TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE";
|
|
5037
|
+
var GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE";
|
|
5036
5038
|
var X_KNOWN_OP_NAMES = new Set([
|
|
5037
5039
|
"slot",
|
|
5038
5040
|
"text",
|
|
@@ -5124,6 +5126,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
|
|
|
5124
5126
|
checkProvidesAreAddressable(lx, Comp);
|
|
5125
5127
|
checkLookupShapes(lx, Comp);
|
|
5126
5128
|
checkHandlersNotAsync(lx, Comp);
|
|
5129
|
+
checkScopedStyleTopLevel(lx, Comp);
|
|
5127
5130
|
const referencedAlters = new Set;
|
|
5128
5131
|
const referencedInputs = new Set;
|
|
5129
5132
|
const referencedDynamics = new Set;
|
|
@@ -5641,6 +5644,69 @@ function checkHandlersNotAsync(lx, Comp) {
|
|
|
5641
5644
|
}
|
|
5642
5645
|
}
|
|
5643
5646
|
}
|
|
5647
|
+
var NON_NESTABLE_AT_RULE = /(?:^|[\s;{}])@(?:-[a-z]+-)?(charset|import|namespace|font-face|keyframes|page|property|counter-style|font-feature-values|font-palette-values|view-transition)\b/gi;
|
|
5648
|
+
var GLOBAL_LEADING_SELECTOR = /(?:^|[{}])\s*(html|body|:root)\b[^{};]*\{/gi;
|
|
5649
|
+
var IGNORE_DIRECTIVE = /\/\*\s*tutuca-lint-ignore\s*\*\//g;
|
|
5650
|
+
var blankRun = (m) => m.replace(/[^\n]/g, " ");
|
|
5651
|
+
function blankCssNoise(css) {
|
|
5652
|
+
return css.replace(/\/\*[\s\S]*?\*\//g, blankRun).replace(/"(?:[^"\\\n]|\\.)*"/g, blankRun).replace(/'(?:[^'\\\n]|\\.)*'/g, blankRun);
|
|
5653
|
+
}
|
|
5654
|
+
function offsetToLineCol2(str, index) {
|
|
5655
|
+
let line = 1;
|
|
5656
|
+
let lineStart = 0;
|
|
5657
|
+
for (let i = 0;i < index; i++) {
|
|
5658
|
+
if (str[i] === `
|
|
5659
|
+
`) {
|
|
5660
|
+
line++;
|
|
5661
|
+
lineStart = i + 1;
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
return { line, column: index - lineStart + 1 };
|
|
5665
|
+
}
|
|
5666
|
+
function suppressedLines(css) {
|
|
5667
|
+
const lines = new Set;
|
|
5668
|
+
IGNORE_DIRECTIVE.lastIndex = 0;
|
|
5669
|
+
for (let m = IGNORE_DIRECTIVE.exec(css);m !== null; m = IGNORE_DIRECTIVE.exec(css)) {
|
|
5670
|
+
lines.add(offsetToLineCol2(css, m.index).line);
|
|
5671
|
+
}
|
|
5672
|
+
return lines;
|
|
5673
|
+
}
|
|
5674
|
+
var STYLE_TO_GLOBAL_HELP = "Move it to the component's 'globalStyle' key, which is injected without the " + "component-scoping wrapper. To suppress a false positive, put a " + "/* tutuca-lint-ignore */ comment on the same line.";
|
|
5675
|
+
function scanScopedCss(lx, css, key) {
|
|
5676
|
+
if (!css)
|
|
5677
|
+
return;
|
|
5678
|
+
const ignore = suppressedLines(css);
|
|
5679
|
+
const clean = blankCssNoise(css);
|
|
5680
|
+
const report = (id, info) => {
|
|
5681
|
+
if (ignore.has(info.location.line))
|
|
5682
|
+
return;
|
|
5683
|
+
lx.error(id, { key, ...info }, { kind: "rephrase", text: STYLE_TO_GLOBAL_HELP });
|
|
5684
|
+
};
|
|
5685
|
+
NON_NESTABLE_AT_RULE.lastIndex = 0;
|
|
5686
|
+
for (let m = NON_NESTABLE_AT_RULE.exec(clean);m !== null; m = NON_NESTABLE_AT_RULE.exec(clean)) {
|
|
5687
|
+
const at = m.index + m[0].indexOf("@");
|
|
5688
|
+
report(TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE, {
|
|
5689
|
+
atRule: m[1].toLowerCase(),
|
|
5690
|
+
location: offsetToLineCol2(clean, at)
|
|
5691
|
+
});
|
|
5692
|
+
}
|
|
5693
|
+
GLOBAL_LEADING_SELECTOR.lastIndex = 0;
|
|
5694
|
+
for (let m = GLOBAL_LEADING_SELECTOR.exec(clean);m !== null; m = GLOBAL_LEADING_SELECTOR.exec(clean)) {
|
|
5695
|
+
const at = m.index + m[0].indexOf(m[1]);
|
|
5696
|
+
report(GLOBAL_SELECTOR_IN_SCOPED_STYLE, {
|
|
5697
|
+
selector: m[1].toLowerCase(),
|
|
5698
|
+
location: offsetToLineCol2(clean, at)
|
|
5699
|
+
});
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
function checkScopedStyleTopLevel(lx, Comp) {
|
|
5703
|
+
scanScopedCss(lx, Comp.commonStyle, "commonStyle");
|
|
5704
|
+
for (const name in Comp.views) {
|
|
5705
|
+
const { style } = Comp.views[name];
|
|
5706
|
+
if (style)
|
|
5707
|
+
lx.push({ viewName: name }, () => scanScopedCss(lx, style, "style"));
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5644
5710
|
function checkProvidesAreAddressable(lx, Comp) {
|
|
5645
5711
|
for (const name in Comp._rawProvide) {
|
|
5646
5712
|
if (Comp.provide[name] === undefined) {
|
|
@@ -6357,6 +6423,10 @@ function lintIdToMessage(id, info) {
|
|
|
6357
6423
|
return `MathML attribute '${info.raw}' will be rewritten to '${info.canonical}'${fmtLocationSuffix(info)}`;
|
|
6358
6424
|
case "ASYNC_HANDLER":
|
|
6359
6425
|
return `Handler '${info.name}' in '${info.channel}' is an async function — handlers must be synchronous and return the updated state (an async function returns a Promise the framework won't await)`;
|
|
6426
|
+
case "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE":
|
|
6427
|
+
return `'@${info.atRule}' is a top-level-only at-rule, but '${info.key}' is wrapped in a component-scoped selector ([data-cid=…]{…}) where it is invalid and silently dropped — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
|
|
6428
|
+
case "GLOBAL_SELECTOR_IN_SCOPED_STYLE":
|
|
6429
|
+
return `Selector '${info.selector}' targets the document root, but '${info.key}' is wrapped in a component-scoped selector, so it becomes a descendant selector that never matches — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
|
|
6360
6430
|
case "LINT_ERROR":
|
|
6361
6431
|
return info.message;
|
|
6362
6432
|
default:
|
|
@@ -8198,6 +8268,7 @@ export {
|
|
|
8198
8268
|
TestReport,
|
|
8199
8269
|
TestIndex,
|
|
8200
8270
|
Test,
|
|
8271
|
+
TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE,
|
|
8201
8272
|
Stack2 as Stack,
|
|
8202
8273
|
Set2 as Set,
|
|
8203
8274
|
Seq,
|
|
@@ -8242,6 +8313,7 @@ export {
|
|
|
8242
8313
|
INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD,
|
|
8243
8314
|
Map3 as IMap,
|
|
8244
8315
|
IF_NO_BRANCH_SET,
|
|
8316
|
+
GLOBAL_SELECTOR_IN_SCOPED_STYLE,
|
|
8245
8317
|
FIELD_VAL_NOT_DEFINED,
|
|
8246
8318
|
FIELD_VAL_IS_METHOD,
|
|
8247
8319
|
FIELD_CLASS,
|
package/dist/tutuca-dev.js
CHANGED
|
@@ -12684,6 +12684,8 @@ var PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING";
|
|
|
12684
12684
|
var UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY";
|
|
12685
12685
|
var COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE";
|
|
12686
12686
|
var ASYNC_HANDLER = "ASYNC_HANDLER";
|
|
12687
|
+
var TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE";
|
|
12688
|
+
var GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE";
|
|
12687
12689
|
var X_KNOWN_OP_NAMES = new Set([
|
|
12688
12690
|
"slot",
|
|
12689
12691
|
"text",
|
|
@@ -12775,6 +12777,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
|
|
|
12775
12777
|
checkProvidesAreAddressable(lx, Comp);
|
|
12776
12778
|
checkLookupShapes(lx, Comp);
|
|
12777
12779
|
checkHandlersNotAsync(lx, Comp);
|
|
12780
|
+
checkScopedStyleTopLevel(lx, Comp);
|
|
12778
12781
|
const referencedAlters = new Set;
|
|
12779
12782
|
const referencedInputs = new Set;
|
|
12780
12783
|
const referencedDynamics = new Set;
|
|
@@ -13292,6 +13295,69 @@ function checkHandlersNotAsync(lx, Comp) {
|
|
|
13292
13295
|
}
|
|
13293
13296
|
}
|
|
13294
13297
|
}
|
|
13298
|
+
var NON_NESTABLE_AT_RULE = /(?:^|[\s;{}])@(?:-[a-z]+-)?(charset|import|namespace|font-face|keyframes|page|property|counter-style|font-feature-values|font-palette-values|view-transition)\b/gi;
|
|
13299
|
+
var GLOBAL_LEADING_SELECTOR = /(?:^|[{}])\s*(html|body|:root)\b[^{};]*\{/gi;
|
|
13300
|
+
var IGNORE_DIRECTIVE = /\/\*\s*tutuca-lint-ignore\s*\*\//g;
|
|
13301
|
+
var blankRun = (m) => m.replace(/[^\n]/g, " ");
|
|
13302
|
+
function blankCssNoise(css) {
|
|
13303
|
+
return css.replace(/\/\*[\s\S]*?\*\//g, blankRun).replace(/"(?:[^"\\\n]|\\.)*"/g, blankRun).replace(/'(?:[^'\\\n]|\\.)*'/g, blankRun);
|
|
13304
|
+
}
|
|
13305
|
+
function offsetToLineCol2(str, index) {
|
|
13306
|
+
let line = 1;
|
|
13307
|
+
let lineStart = 0;
|
|
13308
|
+
for (let i = 0;i < index; i++) {
|
|
13309
|
+
if (str[i] === `
|
|
13310
|
+
`) {
|
|
13311
|
+
line++;
|
|
13312
|
+
lineStart = i + 1;
|
|
13313
|
+
}
|
|
13314
|
+
}
|
|
13315
|
+
return { line, column: index - lineStart + 1 };
|
|
13316
|
+
}
|
|
13317
|
+
function suppressedLines(css) {
|
|
13318
|
+
const lines = new Set;
|
|
13319
|
+
IGNORE_DIRECTIVE.lastIndex = 0;
|
|
13320
|
+
for (let m = IGNORE_DIRECTIVE.exec(css);m !== null; m = IGNORE_DIRECTIVE.exec(css)) {
|
|
13321
|
+
lines.add(offsetToLineCol2(css, m.index).line);
|
|
13322
|
+
}
|
|
13323
|
+
return lines;
|
|
13324
|
+
}
|
|
13325
|
+
var STYLE_TO_GLOBAL_HELP = "Move it to the component's 'globalStyle' key, which is injected without the " + "component-scoping wrapper. To suppress a false positive, put a " + "/* tutuca-lint-ignore */ comment on the same line.";
|
|
13326
|
+
function scanScopedCss(lx, css, key) {
|
|
13327
|
+
if (!css)
|
|
13328
|
+
return;
|
|
13329
|
+
const ignore = suppressedLines(css);
|
|
13330
|
+
const clean = blankCssNoise(css);
|
|
13331
|
+
const report = (id, info) => {
|
|
13332
|
+
if (ignore.has(info.location.line))
|
|
13333
|
+
return;
|
|
13334
|
+
lx.error(id, { key, ...info }, { kind: "rephrase", text: STYLE_TO_GLOBAL_HELP });
|
|
13335
|
+
};
|
|
13336
|
+
NON_NESTABLE_AT_RULE.lastIndex = 0;
|
|
13337
|
+
for (let m = NON_NESTABLE_AT_RULE.exec(clean);m !== null; m = NON_NESTABLE_AT_RULE.exec(clean)) {
|
|
13338
|
+
const at = m.index + m[0].indexOf("@");
|
|
13339
|
+
report(TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE, {
|
|
13340
|
+
atRule: m[1].toLowerCase(),
|
|
13341
|
+
location: offsetToLineCol2(clean, at)
|
|
13342
|
+
});
|
|
13343
|
+
}
|
|
13344
|
+
GLOBAL_LEADING_SELECTOR.lastIndex = 0;
|
|
13345
|
+
for (let m = GLOBAL_LEADING_SELECTOR.exec(clean);m !== null; m = GLOBAL_LEADING_SELECTOR.exec(clean)) {
|
|
13346
|
+
const at = m.index + m[0].indexOf(m[1]);
|
|
13347
|
+
report(GLOBAL_SELECTOR_IN_SCOPED_STYLE, {
|
|
13348
|
+
selector: m[1].toLowerCase(),
|
|
13349
|
+
location: offsetToLineCol2(clean, at)
|
|
13350
|
+
});
|
|
13351
|
+
}
|
|
13352
|
+
}
|
|
13353
|
+
function checkScopedStyleTopLevel(lx, Comp) {
|
|
13354
|
+
scanScopedCss(lx, Comp.commonStyle, "commonStyle");
|
|
13355
|
+
for (const name in Comp.views) {
|
|
13356
|
+
const { style } = Comp.views[name];
|
|
13357
|
+
if (style)
|
|
13358
|
+
lx.push({ viewName: name }, () => scanScopedCss(lx, style, "style"));
|
|
13359
|
+
}
|
|
13360
|
+
}
|
|
13295
13361
|
function checkProvidesAreAddressable(lx, Comp) {
|
|
13296
13362
|
for (const name in Comp._rawProvide) {
|
|
13297
13363
|
if (Comp.provide[name] === undefined) {
|
|
@@ -14008,6 +14074,10 @@ function lintIdToMessage(id, info) {
|
|
|
14008
14074
|
return `MathML attribute '${info.raw}' will be rewritten to '${info.canonical}'${fmtLocationSuffix(info)}`;
|
|
14009
14075
|
case "ASYNC_HANDLER":
|
|
14010
14076
|
return `Handler '${info.name}' in '${info.channel}' is an async function — handlers must be synchronous and return the updated state (an async function returns a Promise the framework won't await)`;
|
|
14077
|
+
case "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE":
|
|
14078
|
+
return `'@${info.atRule}' is a top-level-only at-rule, but '${info.key}' is wrapped in a component-scoped selector ([data-cid=…]{…}) where it is invalid and silently dropped — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
|
|
14079
|
+
case "GLOBAL_SELECTOR_IN_SCOPED_STYLE":
|
|
14080
|
+
return `Selector '${info.selector}' targets the document root, but '${info.key}' is wrapped in a component-scoped selector, so it becomes a descendant selector that never matches — move it to 'globalStyle'${fmtLocationSuffix(info)}`;
|
|
14011
14081
|
case "LINT_ERROR":
|
|
14012
14082
|
return info.message;
|
|
14013
14083
|
default:
|
|
@@ -15792,6 +15862,7 @@ export {
|
|
|
15792
15862
|
TestReport,
|
|
15793
15863
|
TestIndex,
|
|
15794
15864
|
Test,
|
|
15865
|
+
TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE,
|
|
15795
15866
|
Stack,
|
|
15796
15867
|
Set2 as Set,
|
|
15797
15868
|
Seq,
|
|
@@ -15836,6 +15907,7 @@ export {
|
|
|
15836
15907
|
INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD,
|
|
15837
15908
|
Map2 as IMap,
|
|
15838
15909
|
IF_NO_BRANCH_SET,
|
|
15910
|
+
GLOBAL_SELECTOR_IN_SCOPED_STYLE,
|
|
15839
15911
|
FIELD_VAL_NOT_DEFINED,
|
|
15840
15912
|
FIELD_VAL_IS_METHOD,
|
|
15841
15913
|
FIELD_CLASS,
|