diff --git a/src/generators/Generator.ts b/src/generators/Generator.ts index aa3771fea5..595cc12edb 100644 --- a/src/generators/Generator.ts +++ b/src/generators/Generator.ts @@ -13,7 +13,8 @@ import annotateWithScopes from '../utils/annotateWithScopes'; import clone from '../utils/clone'; import DomBlock from './dom/Block'; import SsrBlock from './server-side-rendering/Block'; -import extractSelectors from './extractSelectors'; +import { walkRules } from '../utils/css'; +import Selector from './Selector'; import { Node, Parsed, CompileOptions } from '../interfaces'; const test = typeof global !== 'undefined' && global.__svelte_test; @@ -41,7 +42,7 @@ export default class Generator { cssId: string; usesRefs: boolean; - selectors: any[]; // TODO how to indicate it takes `(Node[]) => boolean` functions? + selectors: Selector[]; importedNames: Set; aliases: Map; @@ -74,12 +75,21 @@ export default class Generator { this.expectedProperties = new Set(); this.code = new MagicString(source); + this.usesRefs = false; + + // styles this.cascade = options.cascade !== false; // TODO remove this option in v2 this.css = parsed.css ? processCss(parsed, this.code, this.cascade) : null; this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; - this.usesRefs = false; + this.selectors = []; - this.selectors = extractSelectors(parsed.css); + if (parsed.css) { + walkRules(parsed.css.children, node => { + node.selector.children.forEach((child: Node) => { + this.selectors.push(new Selector(child)); + }); + }); + } // allow compiler to deconflict user's `import { get } from 'whatever'` and // Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`; diff --git a/src/generators/extractSelectors.ts b/src/generators/Selector.ts similarity index 78% rename from src/generators/extractSelectors.ts rename to src/generators/Selector.ts index 024542ddcc..4a1c566b69 100644 --- a/src/generators/extractSelectors.ts +++ b/src/generators/Selector.ts @@ -1,21 +1,19 @@ import { walkRules } from '../utils/css'; import { Node } from '../interfaces'; -export default function extractSelectors(css: Node) :Node[] { - if (!css) return []; +export default class Selector { + node: Node; + parts: Node[]; + used: boolean; - const selectors = []; + constructor(node: Node) { + this.node = node; - walkRules(css.children, node => { - node.selector.children.forEach(processSelector); - }); - - function processSelector(selector: Node) { // take trailing :global(...) selectors out of consideration - let i = selector.children.length; + let i = node.children.length; while (i > 2) { - const last = selector.children[i-1]; - const penultimate = selector.children[i-2]; + const last = node.children[i-1]; + const penultimate = node.children[i-2]; if (last.type === 'PseudoClassSelector' && last.name === 'global') { i -= 2; @@ -24,24 +22,21 @@ export default function extractSelectors(css: Node) :Node[] { } } - const parts = selector.children.slice(0, i); - - selectors.push({ - used: false, // TODO use this! warn on unused selectors - apply: (node: Node, stack: Node[]) => { - const applies = selectorAppliesTo(parts, node, stack.slice()); + this.parts = node.children.slice(0, i); - if (applies) { - // add svelte-123xyz attribute to outermost and innermost - // elements — no need to add it to intermediate elements - node._needsCssAttribute = true; - if (stack[0] && selector.children.find(isDescendantSelector)) stack[0]._needsCssAttribute = true; - } - } - }); + this.used = false; // TODO use this! warn on unused selectors } - return selectors; + apply(node: Node, stack: Node[]) { + const applies = selectorAppliesTo(this.parts, node, stack.slice()); + + if (applies) { + // add svelte-123xyz attribute to outermost and innermost + // elements — no need to add it to intermediate elements + node._needsCssAttribute = true; + if (stack[0] && this.node.children.find(isDescendantSelector)) stack[0]._needsCssAttribute = true; + } + } } function isDescendantSelector(selector: Node) {