chore: CSS tidy up (#10480)

* move files

* move types

* rename Selector -> ComplexSelector

* ditto

* rename block -> relative_selector

* tidy

* tidy

* unused file

* tweak

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/10487/head
Rich Harris 2 years ago committed by GitHub
parent cc273f7d53
commit 302c379e01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,6 +1,6 @@
import { get_possible_values } from './gather_possible_values.js'; import { get_possible_values } from './utils.js';
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../patterns.js'; import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../phases/patterns.js';
import { error } from '../../../errors.js'; import { error } from '../errors.js';
import { Stylesheet } from './Stylesheet.js'; import { Stylesheet } from './Stylesheet.js';
const NO_MATCH = 'NO_MATCH'; const NO_MATCH = 'NO_MATCH';
@ -19,41 +19,48 @@ const whitelist_attribute_selector = new Map([
['dialog', new Set(['open'])] ['dialog', new Set(['open'])]
]); ]);
export default class Selector { export class ComplexSelector {
/** @type {import('#compiler').Css.Selector} */ /** @type {import('#compiler').Css.ComplexSelector} */
node; node;
/** @type {import('./Stylesheet.js').Stylesheet} */ /** @type {import('./Stylesheet.js').Stylesheet} */
stylesheet; stylesheet;
/** @type {Block[]} */ /** @type {RelativeSelector[]} */
blocks; relative_selectors;
/** @type {Block[]} */ /**
local_blocks; * The `relative_selectors`, minus any trailing global selectors
* (which includes `:root` and `:host`) since we ignore these
* when determining if a selector is used.
* @type {RelativeSelector[]}
*/
local_relative_selectors;
used = false; used = false;
/** /**
* @param {import('#compiler').Css.Selector} node * @param {import('#compiler').Css.ComplexSelector} node
* @param {import('./Stylesheet.js').Stylesheet} stylesheet * @param {import('./Stylesheet.js').Stylesheet} stylesheet
*/ */
constructor(node, stylesheet) { constructor(node, stylesheet) {
this.node = node; this.node = node;
this.stylesheet = stylesheet; this.stylesheet = stylesheet;
this.blocks = group_selectors(node);
this.relative_selectors = group_selectors(node);
// take trailing :global(...) selectors out of consideration // take trailing :global(...) selectors out of consideration
const i = this.blocks.findLastIndex((block) => !block.can_ignore()); const i = this.relative_selectors.findLastIndex((s) => !s.can_ignore());
this.local_blocks = this.blocks.slice(0, i + 1); this.local_relative_selectors = this.relative_selectors.slice(0, i + 1);
// if we have a `:root {...}` or `:global(...) {...}` selector, we need to mark // if we have a `:root {...}` or `:global(...) {...}` selector, we need to mark
// this selector as `used` even if the component doesn't contain any nodes // this selector as `used` even if the component doesn't contain any nodes
this.used = this.local_blocks.length === 0; this.used = this.local_relative_selectors.length === 0;
} }
/** @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node */ /** @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node */
apply(node) { apply(node) {
if (apply_selector(this.local_blocks.slice(), node, this.stylesheet)) { if (apply_selector(this.local_relative_selectors.slice(), node, this.stylesheet)) {
this.used = true; this.used = true;
} }
} }
@ -71,19 +78,19 @@ export default class Selector {
} }
/** /**
* @param {Block} block * @param {RelativeSelector} relative_selector
* @param {string} modifier * @param {string} modifier
*/ */
function encapsulate_block(block, modifier) { function encapsulate_block(relative_selector, modifier) {
for (const selector of block.selectors) { for (const selector of relative_selector.selectors) {
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
remove_global_pseudo_class(selector); remove_global_pseudo_class(selector);
} }
} }
let i = block.selectors.length; let i = relative_selector.selectors.length;
while (i--) { while (i--) {
const selector = block.selectors[i]; const selector = relative_selector.selectors[i];
if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') { if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') {
if (selector.name !== 'root' && selector.name !== 'host') { if (selector.name !== 'root' && selector.name !== 'host') {
@ -103,22 +110,22 @@ export default class Selector {
} }
let first = true; let first = true;
for (const block of this.blocks) { for (const relative_selector of this.relative_selectors) {
if (block.global) { if (relative_selector.is_global) {
remove_global_pseudo_class(block.selectors[0]); remove_global_pseudo_class(relative_selector.selectors[0]);
} }
if (block.should_encapsulate) { if (relative_selector.should_encapsulate) {
// for the first occurrence, we use a classname selector, so that every // for the first occurrence, we use a classname selector, so that every
// encapsulated selector gets a +0-1-0 specificity bump. thereafter, // encapsulated selector gets a +0-1-0 specificity bump. thereafter,
// we use a `:where` selector, which does not affect specificity // we use a `:where` selector, which does not affect specificity
encapsulate_block(block, first ? modifier : `:where(${modifier})`); encapsulate_block(relative_selector, first ? modifier : `:where(${modifier})`);
first = false; first = false;
} }
} }
} }
/** @param {import('../../types.js').ComponentAnalysis} analysis */ /** @param {import('../phases/types.js').ComponentAnalysis} analysis */
validate(analysis) { validate(analysis) {
this.validate_global_placement(); this.validate_global_placement();
this.validate_global_with_multiple_selectors(); this.validate_global_with_multiple_selectors();
@ -128,27 +135,27 @@ export default class Selector {
validate_global_placement() { validate_global_placement() {
let start = 0; let start = 0;
let end = this.blocks.length; let end = this.relative_selectors.length;
for (; start < end; start += 1) { for (; start < end; start += 1) {
if (!this.blocks[start].global) break; if (!this.relative_selectors[start].is_global) break;
} }
for (; end > start; end -= 1) { for (; end > start; end -= 1) {
if (!this.blocks[end - 1].global) break; if (!this.relative_selectors[end - 1].is_global) break;
} }
for (let i = start; i < end; i += 1) { for (let i = start; i < end; i += 1) {
if (this.blocks[i].global) { if (this.relative_selectors[i].is_global) {
error(this.blocks[i].selectors[0], 'invalid-css-global-placement'); error(this.relative_selectors[i].selectors[0], 'invalid-css-global-placement');
} }
} }
} }
validate_global_with_multiple_selectors() { validate_global_with_multiple_selectors() {
if (this.blocks.length === 1 && this.blocks[0].selectors.length === 1) { if (this.relative_selectors.length === 1 && this.relative_selectors[0].selectors.length === 1) {
// standalone :global() with multiple selectors is OK // standalone :global() with multiple selectors is OK
return; return;
} }
for (const block of this.blocks) { for (const relative_selector of this.relative_selectors) {
for (const selector of block.selectors) { for (const selector of relative_selector.selectors) {
if ( if (
selector.type === 'PseudoClassSelector' && selector.type === 'PseudoClassSelector' &&
selector.name === 'global' && selector.name === 'global' &&
@ -161,22 +168,22 @@ export default class Selector {
} }
} }
/** @param {import('../../types.js').ComponentAnalysis} analysis */ /** @param {import('../phases/types.js').ComponentAnalysis} analysis */
validate_invalid_combinator_without_selector(analysis) { validate_invalid_combinator_without_selector(analysis) {
for (let i = 0; i < this.blocks.length; i++) { for (let i = 0; i < this.relative_selectors.length; i++) {
const block = this.blocks[i]; const relative_selector = this.relative_selectors[i];
if (block.selectors.length === 0) { if (relative_selector.selectors.length === 0) {
error(this.node, 'invalid-css-selector'); error(this.node, 'invalid-css-selector');
} }
} }
} }
validate_global_compound_selector() { validate_global_compound_selector() {
for (const block of this.blocks) { for (const relative_selector of this.relative_selectors) {
if (block.selectors.length === 1) continue; if (relative_selector.selectors.length === 1) continue;
for (let i = 0; i < block.selectors.length; i++) { for (let i = 0; i < relative_selector.selectors.length; i++) {
const selector = block.selectors[i]; const selector = relative_selector.selectors[i];
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
const child = selector.args?.children[0].children[0]; const child = selector.args?.children[0].children[0];
@ -184,7 +191,7 @@ export default class Selector {
child?.type === 'TypeSelector' && child?.type === 'TypeSelector' &&
!/[.:#]/.test(child.name[0]) && !/[.:#]/.test(child.name[0]) &&
(i !== 0 || (i !== 0 ||
block.selectors relative_selector.selectors
.slice(1) .slice(1)
.some( .some(
(s) => s.type !== 'PseudoElementSelector' && s.type !== 'PseudoClassSelector' (s) => s.type !== 'PseudoElementSelector' && s.type !== 'PseudoClassSelector'
@ -199,20 +206,22 @@ export default class Selector {
} }
/** /**
* @param {Block[]} blocks * @param {RelativeSelector[]} relative_selectors
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} node * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} node
* @param {Stylesheet} stylesheet * @param {Stylesheet} stylesheet
* @returns {boolean} * @returns {boolean}
*/ */
function apply_selector(blocks, node, stylesheet) { function apply_selector(relative_selectors, node, stylesheet) {
const block = blocks.pop(); const relative_selector = relative_selectors.pop();
if (!block) return false; if (!relative_selector) return false;
if (!node) { if (!node) {
return ( return (
(block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0) (relative_selector.is_global &&
relative_selectors.every((relative_selector) => relative_selector.is_global)) ||
(relative_selector.is_host && relative_selectors.length === 0)
); );
} }
const applies = block_might_apply_to_node(block, node); const applies = block_might_apply_to_node(relative_selector, node);
if (applies === NO_MATCH) { if (applies === NO_MATCH) {
return false; return false;
@ -221,27 +230,30 @@ function apply_selector(blocks, node, stylesheet) {
/** /**
* Mark both the compound selector and the node it selects as encapsulated, * Mark both the compound selector and the node it selects as encapsulated,
* for transformation in a later step * for transformation in a later step
* @param {Block} block * @param {RelativeSelector} relative_selector
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
*/ */
function mark(block, node) { function mark(relative_selector, node) {
block.should_encapsulate = true; relative_selector.should_encapsulate = true;
stylesheet.nodes_with_css_class.add(node); stylesheet.nodes_with_css_class.add(node);
return true; return true;
} }
if (applies === UNKNOWN_SELECTOR) { if (applies === UNKNOWN_SELECTOR) {
return mark(block, node); return mark(relative_selector, node);
} }
if (block.combinator) { if (relative_selector.combinator) {
if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') { if (
for (const ancestor_block of blocks) { relative_selector.combinator.type === 'Combinator' &&
if (ancestor_block.global) { relative_selector.combinator.name === ' '
) {
for (const ancestor_block of relative_selectors) {
if (ancestor_block.is_global) {
continue; continue;
} }
if (ancestor_block.host) { if (ancestor_block.is_host) {
return mark(block, node); return mark(relative_selector, node);
} }
/** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} */ /** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} */
let parent = node; let parent = node;
@ -253,35 +265,48 @@ function apply_selector(blocks, node, stylesheet) {
} }
} }
if (matched) { if (matched) {
return mark(block, node); return mark(relative_selector, node);
} }
} }
if (blocks.every((block) => block.global)) { if (relative_selectors.every((relative_selector) => relative_selector.is_global)) {
return mark(block, node); return mark(relative_selector, node);
} }
return false; return false;
} else if (block.combinator.name === '>') { } else if (relative_selector.combinator.name === '>') {
const has_global_parent = blocks.every((block) => block.global); const has_global_parent = relative_selectors.every(
if (has_global_parent || apply_selector(blocks, get_element_parent(node), stylesheet)) { (relative_selector) => relative_selector.is_global
return mark(block, node); );
if (
has_global_parent ||
apply_selector(relative_selectors, get_element_parent(node), stylesheet)
) {
return mark(relative_selector, node);
} }
return false; return false;
} else if (block.combinator.name === '+' || block.combinator.name === '~') { } else if (
const siblings = get_possible_element_siblings(node, block.combinator.name === '+'); relative_selector.combinator.name === '+' ||
relative_selector.combinator.name === '~'
) {
const siblings = get_possible_element_siblings(
node,
relative_selector.combinator.name === '+'
);
let has_match = false; let has_match = false;
// NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the // NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the
// css-tree limitation that does not parse the inner selector of :global // css-tree limitation that does not parse the inner selector of :global
// so unless we are sure there will be no sibling to match, we will consider it as matched // so unless we are sure there will be no sibling to match, we will consider it as matched
const has_global = blocks.some((block) => block.global); const has_global = relative_selectors.some(
(relative_selector) => relative_selector.is_global
);
if (has_global) { if (has_global) {
if (siblings.size === 0 && get_element_parent(node) !== null) { if (siblings.size === 0 && get_element_parent(node) !== null) {
return false; return false;
} }
return mark(block, node); return mark(relative_selector, node);
} }
for (const possible_sibling of siblings.keys()) { for (const possible_sibling of siblings.keys()) {
if (apply_selector(blocks.slice(), possible_sibling, stylesheet)) { if (apply_selector(relative_selectors.slice(), possible_sibling, stylesheet)) {
mark(block, node); mark(relative_selector, node);
has_match = true; has_match = true;
} }
} }
@ -289,25 +314,25 @@ function apply_selector(blocks, node, stylesheet) {
} }
// TODO other combinators // TODO other combinators
return mark(block, node); return mark(relative_selector, node);
} }
return mark(block, node); return mark(relative_selector, node);
} }
const regex_backslash_and_following_character = /\\(.)/g; const regex_backslash_and_following_character = /\\(.)/g;
/** /**
* @param {Block} block * @param {RelativeSelector} relative_selector
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
* @returns {NO_MATCH | POSSIBLE_MATCH | UNKNOWN_SELECTOR} * @returns {NO_MATCH | POSSIBLE_MATCH | UNKNOWN_SELECTOR}
*/ */
function block_might_apply_to_node(block, node) { function block_might_apply_to_node(relative_selector, node) {
if (block.host || block.root) return NO_MATCH; if (relative_selector.is_host || relative_selector.is_root) return NO_MATCH;
let i = block.selectors.length; let i = relative_selector.selectors.length;
while (i--) { while (i--) {
const selector = block.selectors[i]; const selector = relative_selector.selectors[i];
if (selector.type === 'Percentage' || selector.type === 'Nth') continue; if (selector.type === 'Percentage' || selector.type === 'Nth') continue;
@ -317,7 +342,7 @@ function block_might_apply_to_node(block, node) {
return NO_MATCH; return NO_MATCH;
} }
if ( if (
block.selectors.length === 1 && relative_selector.selectors.length === 1 &&
selector.type === 'PseudoClassSelector' && selector.type === 'PseudoClassSelector' &&
name === 'global' name === 'global'
) { ) {
@ -636,22 +661,22 @@ function get_possible_element_siblings(node, adjacent_only) {
} }
/** /**
* @param {import('#compiler').EachBlock | import('#compiler').IfBlock | import('#compiler').AwaitBlock} block * @param {import('#compiler').EachBlock | import('#compiler').IfBlock | import('#compiler').AwaitBlock} relative_selector
* @param {boolean} adjacent_only * @param {boolean} adjacent_only
* @returns {Map<import('#compiler').RegularElement, NodeExistsValue>} * @returns {Map<import('#compiler').RegularElement, NodeExistsValue>}
*/ */
function get_possible_last_child(block, adjacent_only) { function get_possible_last_child(relative_selector, adjacent_only) {
/** @typedef {Map<import('#compiler').RegularElement, NodeExistsValue>} NodeMap */ /** @typedef {Map<import('#compiler').RegularElement, NodeExistsValue>} NodeMap */
/** @type {NodeMap} */ /** @type {NodeMap} */
const result = new Map(); const result = new Map();
if (block.type === 'EachBlock') { if (relative_selector.type === 'EachBlock') {
/** @type {NodeMap} */ /** @type {NodeMap} */
const each_result = loop_child(block.body.nodes, adjacent_only); const each_result = loop_child(relative_selector.body.nodes, adjacent_only);
/** @type {NodeMap} */ /** @type {NodeMap} */
const else_result = block.fallback const else_result = relative_selector.fallback
? loop_child(block.fallback.nodes, adjacent_only) ? loop_child(relative_selector.fallback.nodes, adjacent_only)
: new Map(); : new Map();
const not_exhaustive = !has_definite_elements(else_result); const not_exhaustive = !has_definite_elements(else_result);
if (not_exhaustive) { if (not_exhaustive) {
@ -660,13 +685,13 @@ function get_possible_last_child(block, adjacent_only) {
} }
add_to_map(each_result, result); add_to_map(each_result, result);
add_to_map(else_result, result); add_to_map(else_result, result);
} else if (block.type === 'IfBlock') { } else if (relative_selector.type === 'IfBlock') {
/** @type {NodeMap} */ /** @type {NodeMap} */
const if_result = loop_child(block.consequent.nodes, adjacent_only); const if_result = loop_child(relative_selector.consequent.nodes, adjacent_only);
/** @type {NodeMap} */ /** @type {NodeMap} */
const else_result = block.alternate const else_result = relative_selector.alternate
? loop_child(block.alternate.nodes, adjacent_only) ? loop_child(relative_selector.alternate.nodes, adjacent_only)
: new Map(); : new Map();
const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result); const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result);
if (not_exhaustive) { if (not_exhaustive) {
@ -675,17 +700,21 @@ function get_possible_last_child(block, adjacent_only) {
} }
add_to_map(if_result, result); add_to_map(if_result, result);
add_to_map(else_result, result); add_to_map(else_result, result);
} else if (block.type === 'AwaitBlock') { } else if (relative_selector.type === 'AwaitBlock') {
/** @type {NodeMap} */ /** @type {NodeMap} */
const pending_result = block.pending const pending_result = relative_selector.pending
? loop_child(block.pending.nodes, adjacent_only) ? loop_child(relative_selector.pending.nodes, adjacent_only)
: new Map(); : new Map();
/** @type {NodeMap} */ /** @type {NodeMap} */
const then_result = block.then ? loop_child(block.then.nodes, adjacent_only) : new Map(); const then_result = relative_selector.then
? loop_child(relative_selector.then.nodes, adjacent_only)
: new Map();
/** @type {NodeMap} */ /** @type {NodeMap} */
const catch_result = block.catch ? loop_child(block.catch.nodes, adjacent_only) : new Map(); const catch_result = relative_selector.catch
? loop_child(relative_selector.catch.nodes, adjacent_only)
: new Map();
const not_exhaustive = const not_exhaustive =
!has_definite_elements(pending_result) || !has_definite_elements(pending_result) ||
!has_definite_elements(then_result) || !has_definite_elements(then_result) ||
@ -774,55 +803,52 @@ function loop_child(children, adjacent_only) {
return result; return result;
} }
class Block { /**
/** @type {boolean} */ * Represents a compound selector (aka an array of simple selectors) plus
host; * a preceding combinator (if not the first in the list). Given this...
*
/** @type {boolean} */ * ```css
root; * .a + .b.c {...}
* ```
*
* ...both `.a` and `+ .b.c` are relative selectors.
* Combined, they are a complex selector.
*/
class RelativeSelector {
/** @type {import('#compiler').Css.Combinator | null} */ /** @type {import('#compiler').Css.Combinator | null} */
combinator; combinator;
/** @type {import('#compiler').Css.SimpleSelector[]} */ /** @type {import('#compiler').Css.SimpleSelector[]} */
selectors; selectors = [];
/** @type {number} */
start;
/** @type {number} */
end;
/** @type {boolean} */ is_host = false;
should_encapsulate; is_root = false;
should_encapsulate = false;
start = -1;
end = -1;
/** @param {import('#compiler').Css.Combinator | null} combinator */ /** @param {import('#compiler').Css.Combinator | null} combinator */
constructor(combinator) { constructor(combinator) {
this.combinator = combinator; this.combinator = combinator;
this.host = false;
this.root = false;
this.selectors = [];
this.start = -1;
this.end = -1;
this.should_encapsulate = false;
} }
/** @param {import('#compiler').Css.SimpleSelector} selector */ /** @param {import('#compiler').Css.SimpleSelector} selector */
add(selector) { add(selector) {
if (this.selectors.length === 0) { if (this.selectors.length === 0) {
this.start = selector.start; this.start = selector.start;
this.host = selector.type === 'PseudoClassSelector' && selector.name === 'host'; this.is_host = selector.type === 'PseudoClassSelector' && selector.name === 'host';
} }
this.root = this.root || (selector.type === 'PseudoClassSelector' && selector.name === 'root'); this.is_root =
this.is_root || (selector.type === 'PseudoClassSelector' && selector.name === 'root');
this.selectors.push(selector); this.selectors.push(selector);
this.end = selector.end; this.end = selector.end;
} }
can_ignore() { can_ignore() {
return this.global || this.host || this.root; return this.is_global || this.is_host || this.is_root;
} }
get global() { get is_global() {
return ( return (
this.selectors.length >= 1 && this.selectors.length >= 1 &&
this.selectors[0].type === 'PseudoClassSelector' && this.selectors[0].type === 'PseudoClassSelector' &&
@ -835,18 +861,18 @@ class Block {
} }
} }
/** @param {import('#compiler').Css.Selector} selector */ /** @param {import('#compiler').Css.ComplexSelector} selector */
function group_selectors(selector) { function group_selectors(selector) {
let block = new Block(null); let relative_selector = new RelativeSelector(null);
const blocks = [block]; const relative_selectors = [relative_selector];
selector.children.forEach((child) => { selector.children.forEach((child) => {
if (child.type === 'Combinator') { if (child.type === 'Combinator') {
block = new Block(child); relative_selector = new RelativeSelector(child);
blocks.push(block); relative_selectors.push(relative_selector);
} else { } else {
block.add(child); relative_selector.add(child);
} }
}); });
return blocks; return relative_selectors;
} }

@ -1,11 +1,10 @@
import MagicString from 'magic-string'; import MagicString from 'magic-string';
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import Selector from './Selector.js'; import { ComplexSelector } from './Selector.js';
import hash from '../utils/hash.js'; import { hash } from './utils.js';
// import compiler_warnings from '../compiler_warnings.js'; // import compiler_warnings from '../compiler_warnings.js';
// import { extract_ignores_above_position } from '../utils/extract_svelte_ignore.js'; // import { extract_ignores_above_position } from '../utils/extract_svelte_ignore.js';
import { push_array } from '../utils/push_array.js'; import { create_attribute } from '../phases/nodes.js'; // TODO move this
import { create_attribute } from '../../nodes.js';
const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/; const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/;
const regex_name_boundary = /^[\s,;}]$/; const regex_name_boundary = /^[\s,;}]$/;
@ -49,7 +48,7 @@ function escape_comment_close(node, code) {
} }
class Rule { class Rule {
/** @type {import('./Selector.js').default[]} */ /** @type {ComplexSelector[]} */
selectors; selectors;
/** @type {import('#compiler').Css.Rule} */ /** @type {import('#compiler').Css.Rule} */
@ -69,7 +68,7 @@ class Rule {
constructor(node, stylesheet, parent) { constructor(node, stylesheet, parent) {
this.node = node; this.node = node;
this.parent = parent; this.parent = parent;
this.selectors = node.prelude.children.map((node) => new Selector(node, stylesheet)); this.selectors = node.prelude.children.map((node) => new ComplexSelector(node, stylesheet));
this.declarations = /** @type {import('#compiler').Css.Declaration[]} */ ( this.declarations = /** @type {import('#compiler').Css.Declaration[]} */ (
node.block.children node.block.children
@ -116,14 +115,14 @@ class Rule {
this.declarations.forEach((declaration) => declaration.transform(code, keyframes)); this.declarations.forEach((declaration) => declaration.transform(code, keyframes));
} }
/** @param {import('../../types.js').ComponentAnalysis} analysis */ /** @param {import('../phases/types.js').ComponentAnalysis} analysis */
validate(analysis) { validate(analysis) {
this.selectors.forEach((selector) => { this.selectors.forEach((selector) => {
selector.validate(analysis); selector.validate(analysis);
}); });
} }
/** @param {(selector: import('./Selector.js').default) => void} handler */ /** @param {(selector: ComplexSelector) => void} handler */
warn_on_unused_selector(handler) { warn_on_unused_selector(handler) {
this.selectors.forEach((selector) => { this.selectors.forEach((selector) => {
if (!selector.used) handler(selector); if (!selector.used) handler(selector);
@ -310,14 +309,14 @@ class Atrule {
}); });
} }
/** @param {import('../../types.js').ComponentAnalysis} analysis */ /** @param {import('../phases/types.js').ComponentAnalysis} analysis */
validate(analysis) { validate(analysis) {
this.children.forEach((child) => { this.children.forEach((child) => {
child.validate(analysis); child.validate(analysis);
}); });
} }
/** @param {(selector: import('./Selector.js').default) => void} handler */ /** @param {(selector: ComplexSelector) => void} handler */
warn_on_unused_selector(handler) { warn_on_unused_selector(handler) {
if (this.node.name !== 'media') return; if (this.node.name !== 'media') return;
this.children.forEach((child) => { this.children.forEach((child) => {
@ -511,14 +510,14 @@ export class Stylesheet {
}; };
} }
/** @param {import('../../types.js').ComponentAnalysis} analysis */ /** @param {import('../phases/types.js').ComponentAnalysis} analysis */
validate(analysis) { validate(analysis) {
this.children.forEach((child) => { this.children.forEach((child) => {
child.validate(analysis); child.validate(analysis);
}); });
} }
/** @param {import('../../types.js').ComponentAnalysis} analysis */ /** @param {import('../phases/types.js').ComponentAnalysis} analysis */
warn_on_unused_selectors(analysis) { warn_on_unused_selectors(analysis) {
// const ignores = !this.ast // const ignores = !this.ast
// ? [] // ? []

@ -1,4 +1,4 @@
import type { Style } from './template'; import type { Style } from '../types/template';
export interface BaseNode { export interface BaseNode {
start: number; start: number;
@ -20,10 +20,10 @@ export interface Rule extends BaseNode {
export interface SelectorList extends BaseNode { export interface SelectorList extends BaseNode {
type: 'SelectorList'; type: 'SelectorList';
children: Selector[]; children: ComplexSelector[];
} }
export interface Selector extends BaseNode { export interface ComplexSelector extends BaseNode {
type: 'Selector'; type: 'Selector';
children: Array<SimpleSelector | Combinator>; children: Array<SimpleSelector | Combinator>;
} }

@ -1,3 +1,18 @@
const regex_return_characters = /\r/g;
/**
* @param {string} str
* @returns {string}
*/
export function hash(str) {
str = str.replace(regex_return_characters, '');
let hash = 5381;
let i = str.length;
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return (hash >>> 0).toString(36);
}
const UNKNOWN = {}; const UNKNOWN = {};
/** /**

@ -149,7 +149,7 @@ function read_rule(parser) {
* @returns {import('#compiler').Css.SelectorList} * @returns {import('#compiler').Css.SelectorList}
*/ */
function read_selector_list(parser, inside_pseudo_class = false) { function read_selector_list(parser, inside_pseudo_class = false) {
/** @type {import('#compiler').Css.Selector[]} */ /** @type {import('#compiler').Css.ComplexSelector[]} */
const children = []; const children = [];
allow_comment_or_whitespace(parser); allow_comment_or_whitespace(parser);
@ -182,7 +182,7 @@ function read_selector_list(parser, inside_pseudo_class = false) {
/** /**
* @param {import('../index.js').Parser} parser * @param {import('../index.js').Parser} parser
* @param {boolean} [inside_pseudo_class] * @param {boolean} [inside_pseudo_class]
* @returns {import('#compiler').Css.Selector} * @returns {import('#compiler').Css.ComplexSelector}
*/ */
function read_selector(parser, inside_pseudo_class = false) { function read_selector(parser, inside_pseudo_class = false) {
const list_start = parser.index; const list_start = parser.index;

@ -13,7 +13,7 @@ import * as b from '../../utils/builders.js';
import { ReservedKeywords, Runes, SVGElements } from '../constants.js'; import { ReservedKeywords, Runes, SVGElements } from '../constants.js';
import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js'; import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js';
import { merge } from '../visitors.js'; import { merge } from '../visitors.js';
import { Stylesheet } from './css/Stylesheet.js'; import { Stylesheet } from '../../css/Stylesheet.js';
import { validation_legacy, validation_runes, validation_runes_js } from './validation.js'; import { validation_legacy, validation_runes, validation_runes_js } from './validation.js';
import { warn } from '../../warnings.js'; import { warn } from '../../warnings.js';
import check_graph_for_cycles from './utils/check_graph_for_cycles.js'; import check_graph_for_cycles from './utils/check_graph_for_cycles.js';

@ -1,14 +0,0 @@
const regex_return_characters = /\r/g;
/**
* @param {string} str
* @returns {string}
*/
export default function hash(str) {
str = str.replace(regex_return_characters, '');
let hash = 5381;
let i = str.length;
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
return (hash >>> 0).toString(36);
}

@ -7,7 +7,7 @@ import type {
SvelteOptions SvelteOptions
} from '#compiler'; } from '#compiler';
import type { Identifier, LabeledStatement, Program } from 'estree'; import type { Identifier, LabeledStatement, Program } from 'estree';
import { Stylesheet } from './2-analyze/css/Stylesheet.js'; import { Stylesheet } from '../css/Stylesheet.js';
import type { Scope, ScopeRoot } from './scope.js'; import type { Scope, ScopeRoot } from './scope.js';
export interface Js { export interface Js {

@ -10,7 +10,7 @@ import type { Location } from 'locate-character';
import type { SourceMap } from 'magic-string'; import type { SourceMap } from 'magic-string';
import type { Context } from 'zimmerframe'; import type { Context } from 'zimmerframe';
import type { Scope } from '../phases/scope.js'; import type { Scope } from '../phases/scope.js';
import * as Css from './css.js'; import * as Css from '../css/types.js';
import type { EachBlock, Namespace, SvelteNode } from './template.js'; import type { EachBlock, Namespace, SvelteNode } from './template.js';
/** The return value of `compile` from `svelte/compiler` */ /** The return value of `compile` from `svelte/compiler` */

Loading…
Cancel
Save