refactor: tidy up compile/css

pull/8000/head
Simon He 4 years ago
parent b05b684b95
commit dd248bd15a

@ -42,11 +42,7 @@ export default class Selector {
this.blocks = group_selectors(node);
// take trailing :global(...) selectors out of consideration
let i = this.blocks.length;
while (i > 0) {
if (!this.blocks[i - 1].global) break;
i -= 1;
}
const i = this.blocks.findIndex(block => !block.global);
this.local_blocks = this.blocks.slice(0, i);
@ -74,10 +70,8 @@ export default class Selector {
minify(code: MagicString) {
let c: number = null;
this.blocks.forEach((block, i) => {
if (i > 0) {
if (block.start - c > 1) {
code.overwrite(c, block.start, block.combinator.name || ' ');
}
if (i > 0 && block.start - c > 1) {
code.overwrite(c, block.start, block.combinator.name || ' ');
}
c = block.end;
@ -105,8 +99,8 @@ export default class Selector {
while (i--) {
const selector = block.selectors[i];
if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') {
if (selector.name !== 'root' && selector.name !== 'host') {
if (i === 0) code.prependRight(selector.start, attr);
if (selector.name !== 'root' && selector.name !== 'host' && i === 0) {
code.prependRight(selector.start, attr);
}
continue;
}
@ -159,10 +153,8 @@ export default class Selector {
for (const block of this.blocks) {
for (const selector of block.selectors) {
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
if (regex_is_single_css_selector.test(selector.children[0].value)) {
component.error(selector, compiler_errors.css_invalid_global_selector);
}
if (selector.type === 'PseudoClassSelector' && selector.name === 'global' && regex_is_single_css_selector.test(selector.children[0].value)) {
component.error(selector, compiler_errors.css_invalid_global_selector);
}
}
}
@ -183,7 +175,7 @@ export default class Selector {
let count = 0;
for (const block of this.blocks) {
if (block.should_encapsulate) {
count ++;
count++;
}
}
return count;
@ -206,7 +198,7 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{
return false;
case BlockAppliesToNode.UnknownSelectorType:
// bail. TODO figure out what these could be
// bail. TODO figure out what these could be
to_encapsulate.push({ node, block });
return true;
}
@ -293,11 +285,10 @@ function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToN
const selector = block.selectors[i];
const name = typeof selector.name === 'string' && selector.name.replace(regex_backslash_and_following_character, '$1');
if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) {
return BlockAppliesToNode.NotPossible;
}
if (block.selectors.length === 1 && selector.type === 'PseudoClassSelector' && name === 'global') {
if ((
selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) ||
block.selectors.length === 1 && selector.type === 'PseudoClassSelector' && name === 'global'
) {
return BlockAppliesToNode.NotPossible;
}
@ -343,9 +334,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
function attribute_matches(node: CssNode, name: string, expected_value: string, operator: string, case_insensitive: boolean) {
const spread = node.attributes.find(attr => attr.type === 'Spread');
if (spread) return true;
if (node.bindings.some((binding: CssNode) => binding.name === name)) return true;
if (spread || node.bindings.some((binding: CssNode) => binding.name === name)) return true;
const attr = node.attributes.find((attr: CssNode) => attr.name === name);
if (!attr) return false;
@ -428,13 +417,10 @@ function attribute_matches(node: CssNode, name: string, expected_value: string,
}
prev_values.forEach(prev_value => possible_values.add(prev_value));
if (possible_values.has(UNKNOWN)) return true;
for (const value of possible_values) {
if (test_attribute(operator, expected_value, case_insensitive, value)) return true;
}
return false;
return possible_values.has(UNKNOWN) ||
[...possible_values].some(value =>
test_attribute(operator, expected_value, case_insensitive, value)
);
}
function unquote(value: CssNode) {
@ -469,8 +455,8 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map
add_to_map(possible_last_child, result);
if (adjacent_only && has_definite_elements(possible_last_child)) {
return result;
}
return result;
}
}
}
@ -552,24 +538,21 @@ function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjace
}
function has_definite_elements(result: Map<Element, NodeExist>): boolean {
if (result.size === 0) return false;
for (const exist of result.values()) {
if (exist === NodeExist.Definitely) {
return true;
}
}
return false;
return result.size === 0
? false
: [...result.values()].some(exist => exist === NodeExist.Definitely);
}
function add_to_map(from: Map<Element, NodeExist>, to: Map<Element, NodeExist>) {
from.forEach((exist, element) => {
to.set(element, higher_existence(exist, to.get(element)));
});
from.forEach((exist, element) =>
to.set(element, higher_existence(exist, to.get(element)))
);
}
function higher_existence(exist1: NodeExist | null, exist2: NodeExist | null): NodeExist {
if (exist1 === undefined || exist2 === undefined) return exist1 || exist2;
return exist1 > exist2 ? exist1 : exist2;
return exist1 === undefined || exist2 === undefined
? exist1 || exist2
: Math.max(exist1, exist2);
}
function mark_as_probably(result: Map<Element, NodeExist>) {

@ -30,18 +30,14 @@ function minify_declarations(
start: number,
declarations: Declaration[]
): number {
let c = start;
declarations.forEach((declaration, i) => {
return declarations.reduce((result, declaration, i) => {
const separator = i > 0 ? ';' : '';
if ((declaration.node.start - c) > separator.length) {
code.overwrite(c, declaration.node.start, separator);
if ((declaration.node.start - result) > separator.length) {
code.overwrite(result, declaration.node.start, separator);
}
declaration.minify(code);
c = declaration.node.end;
});
return c;
return declaration.node.end;
}, start);
}
class Rule {
@ -72,17 +68,16 @@ class Rule {
let started = false;
this.selectors.forEach((selector) => {
if (selector.used) {
const separator = started ? ',' : '';
if ((selector.node.start - c) > separator.length) {
code.overwrite(c, selector.node.start, separator);
}
if (!selector.used) return;
const separator = started ? ',' : '';
if ((selector.node.start - c) > separator.length) {
code.overwrite(c, selector.node.start, separator);
}
selector.minify(code);
c = selector.node.end;
selector.minify(code);
c = selector.node.end;
started = true;
}
started = true;
});
code.remove(c, this.node.block.start);
@ -128,16 +123,14 @@ class Declaration {
transform(code: MagicString, keyframes: Map<string, string>) {
const property = this.node.property && remove_css_prefix(this.node.property.toLowerCase());
if (property === 'animation' || property === 'animation-name') {
this.node.value.children.forEach((block: CssNode) => {
if (block.type === 'Identifier') {
const name = block.name;
if (keyframes.has(name)) {
code.overwrite(block.start, block.end, keyframes.get(name));
}
}
});
}
if (property !== 'animation' && property !== 'animation-name') return;
this.node.value.children.forEach((block: CssNode) => {
if (block.type !== 'Identifier') return;
const name = block.name;
if (keyframes.has(name)) {
code.overwrite(block.start, block.end, keyframes.get(name));
}
});
}
minify(code: MagicString) {
@ -174,9 +167,7 @@ class Atrule {
apply(node: Element) {
if (this.node.name === 'media' || this.node.name === 'supports' || this.node.name === 'layer') {
this.children.forEach(child => {
child.apply(node);
});
this.children.forEach(child => child.apply(node));
} else if (is_keyframes_node(this.node)) {
this.children.forEach((rule: Rule) => {
rule.selectors.forEach(selector => {
@ -246,17 +237,16 @@ class Atrule {
transform(code: MagicString, id: string, keyframes: Map<string, string>, max_amount_class_specificity_increased: number) {
if (is_keyframes_node(this.node)) {
this.node.prelude.children.forEach(({ type, name, start, end }: CssNode) => {
if (type === 'Identifier') {
if (name.startsWith('-global-')) {
code.remove(start, start + 8);
this.children.forEach((rule: Rule) => {
rule.selectors.forEach(selector => {
selector.used = true;
});
if (type !== 'Identifier') return;
if (name.startsWith('-global-')) {
code.remove(start, start + 8);
this.children.forEach((rule: Rule) => {
rule.selectors.forEach(selector => {
selector.used = true;
});
} else {
code.overwrite(start, end, keyframes.get(name));
}
});
} else {
code.overwrite(start, end, keyframes.get(name));
}
});
}
@ -267,17 +257,12 @@ class Atrule {
}
validate(component: Component) {
this.children.forEach(child => {
child.validate(component);
});
this.children.forEach(child => child.validate(component));
}
warn_on_unused_selector(handler: (selector: Selector) => void) {
if (this.node.name !== 'media') return;
this.children.forEach(child => {
child.warn_on_unused_selector(handler);
});
this.children.forEach(child => child.warn_on_unused_selector(handler));
}
get_max_amount_class_specificity_increased() {
@ -322,74 +307,75 @@ export default class Stylesheet {
this.ast = ast;
this.filename = filename;
this.dev = dev;
if (!ast.css || !ast.css.children.length) {
this.has_styles = false;
return;
}
if (ast.css && ast.css.children.length) {
this.id = get_css_hash({
filename,
name: component_name,
css: ast.css.content.styles,
hash
});
this.id = get_css_hash({
filename,
name: component_name,
css: ast.css.content.styles,
hash
});
this.has_styles = true;
const stack: Atrule[] = [];
let depth = 0;
let current_atrule: Atrule = null;
walk(ast.css as any, {
enter: (node: any) => {
if (node.type === 'Atrule') {
const atrule = new Atrule(node);
stack.push(atrule);
if (current_atrule) {
current_atrule.children.push(atrule);
} else if (depth <= 1) {
this.children.push(atrule);
}
if (is_keyframes_node(node)) {
node.prelude.children.forEach((expression: CssNode) => {
if (expression.type === 'Identifier' && !expression.name.startsWith('-global-')) {
this.keyframes.set(expression.name, `${this.id}-${expression.name}`);
}
});
} else if (at_rule_has_declaration(node)) {
const at_rule_declarations = node.block.children
.filter(node => node.type === 'Declaration')
.map(node => new Declaration(node));
push_array(atrule.declarations, at_rule_declarations);
}
current_atrule = atrule;
}
this.has_styles = true;
const stack: Atrule[] = [];
let depth = 0;
let current_atrule: Atrule = null;
if (node.type === 'Rule') {
const rule = new Rule(node, this, current_atrule);
walk(ast.css as any, {
enter: (node: any) => {
if (node.type === 'Atrule') {
const atrule = new Atrule(node);
stack.push(atrule);
if (current_atrule) {
current_atrule.children.push(atrule);
} else if (depth <= 1) {
this.children.push(atrule);
}
if (current_atrule) {
current_atrule.children.push(rule);
} else if (depth <= 1) {
this.children.push(rule);
}
if (is_keyframes_node(node)) {
node.prelude.children.forEach((expression: CssNode) => {
if (expression.type === 'Identifier' && !expression.name.startsWith('-global-')) {
this.keyframes.set(expression.name, `${this.id}-${expression.name}`);
}
});
} else if (at_rule_has_declaration(node)) {
const at_rule_declarations = node.block.children
.filter(node => node.type === 'Declaration')
.map(node => new Declaration(node));
push_array(atrule.declarations, at_rule_declarations);
}
depth += 1;
},
current_atrule = atrule;
}
leave: (node: any) => {
if (node.type === 'Atrule') {
stack.pop();
current_atrule = stack[stack.length - 1];
if (node.type === 'Rule') {
const rule = new Rule(node, this, current_atrule);
if (current_atrule) {
current_atrule.children.push(rule);
} else if (depth <= 1) {
this.children.push(rule);
}
}
depth += 1;
},
depth -= 1;
leave: (node: any) => {
if (node.type === 'Atrule') {
stack.pop();
current_atrule = stack[stack.length - 1];
}
});
} else {
this.has_styles = false;
}
depth -= 1;
}
});
}
apply(node: Element) {
@ -402,15 +388,13 @@ export default class Stylesheet {
}
reify() {
this.nodes_with_css_class.forEach((node: Element) => {
node.add_css_class();
});
this.nodes_with_css_class.forEach((node: Element) => node.add_css_class());
}
render(file: string, should_transform_selectors: boolean) {
if (!this.has_styles) {
return { code: null, map: null };
}
return { code: null, map: null };
}
const code = new MagicString(this.source);
@ -450,9 +434,7 @@ export default class Stylesheet {
}
validate(component: Component) {
this.children.forEach(child => {
child.validate(component);
});
this.children.forEach(child => child.validate(component));
}
warn_on_unused_selectors(component: Component) {

Loading…
Cancel
Save