|
|
|
|
@ -55,7 +55,7 @@ export default class Selector {
|
|
|
|
|
this.local_selector_list = [];
|
|
|
|
|
|
|
|
|
|
// Process each block group to take trailing :global(...) selectors out of consideration
|
|
|
|
|
this.selector_list.forEach(complex_selector => {
|
|
|
|
|
this.selector_list.forEach((complex_selector) => {
|
|
|
|
|
let i = complex_selector.length;
|
|
|
|
|
while (i > 0) {
|
|
|
|
|
if (!complex_selector[i - 1].global) break;
|
|
|
|
|
@ -77,7 +77,7 @@ export default class Selector {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if there are no local blocks across all groups, or if there's a host_only or root_only situation
|
|
|
|
|
const no_local_blocks = this.local_selector_list.every(group => group.length === 0);
|
|
|
|
|
const no_local_blocks = this.local_selector_list.every((group) => group.length === 0);
|
|
|
|
|
this.used = no_local_blocks || host_only || root_only;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
@ -115,7 +115,9 @@ export default class Selector {
|
|
|
|
|
* */
|
|
|
|
|
const used_blocks = new Map();
|
|
|
|
|
|
|
|
|
|
this.local_selector_list.map(complex_selector => apply_selector(complex_selector.slice(), node, used_blocks));
|
|
|
|
|
this.local_selector_list.map((complex_selector) =>
|
|
|
|
|
apply_selector(complex_selector.slice(), node, used_blocks)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Iterate over used_blocks
|
|
|
|
|
for (const [relative_selector, nodes] of used_blocks) {
|
|
|
|
|
@ -154,7 +156,7 @@ export default class Selector {
|
|
|
|
|
let i = relative_selector.compound.selectors.length;
|
|
|
|
|
|
|
|
|
|
let first_selector = relative_selector.compound.selectors[0];
|
|
|
|
|
if (first_selector.type === "TypeSelector" && !first_selector.visible) return
|
|
|
|
|
if (first_selector.type === 'TypeSelector' && !first_selector.visible) return;
|
|
|
|
|
|
|
|
|
|
while (i--) {
|
|
|
|
|
const selector = relative_selector.compound.selectors[i];
|
|
|
|
|
@ -177,13 +179,12 @@ export default class Selector {
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const complex_selector of this.selector_list) {
|
|
|
|
|
let amount_class_specificity_to_increase =
|
|
|
|
|
max_amount_class_specificity_increased - complex_selector
|
|
|
|
|
.filter(selector => selector.should_encapsulate)
|
|
|
|
|
.length;
|
|
|
|
|
max_amount_class_specificity_increased -
|
|
|
|
|
complex_selector.filter((selector) => selector.should_encapsulate).length;
|
|
|
|
|
|
|
|
|
|
complex_selector.map((relative_selector, index) => {
|
|
|
|
|
if (relative_selector.global) {
|
|
|
|
|
@ -191,17 +192,15 @@ export default class Selector {
|
|
|
|
|
remove_global_pseudo_class(relative_selector.compound.selectors[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let amount = complex_selector
|
|
|
|
|
// Ignore invisible selectors because they are not part of the specificity
|
|
|
|
|
.filter(selector => !selector.contains_invisible_selectors)
|
|
|
|
|
.length - 1;
|
|
|
|
|
let amount =
|
|
|
|
|
complex_selector
|
|
|
|
|
// Ignore invisible selectors because they are not part of the specificity
|
|
|
|
|
.filter((selector) => !selector.contains_invisible_selectors).length - 1;
|
|
|
|
|
|
|
|
|
|
if(relative_selector.should_encapsulate) {
|
|
|
|
|
if (relative_selector.should_encapsulate) {
|
|
|
|
|
encapsulate_block(
|
|
|
|
|
relative_selector,
|
|
|
|
|
index === amount
|
|
|
|
|
? attr.repeat(amount_class_specificity_to_increase + 1)
|
|
|
|
|
: attr
|
|
|
|
|
index === amount ? attr.repeat(amount_class_specificity_to_increase + 1) : attr
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
@ -216,7 +215,6 @@ export default class Selector {
|
|
|
|
|
this.validate_invalid_combinator_without_selector(analysis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validate_invalid_css_global_placement() {
|
|
|
|
|
for (let complex_selector of this.selector_list) {
|
|
|
|
|
let start = 0;
|
|
|
|
|
@ -235,7 +233,6 @@ export default class Selector {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validate_global_with_multiple_selectors() {
|
|
|
|
|
for (const complex_selector of this.selector_list) {
|
|
|
|
|
if (complex_selector.length === 1 && complex_selector[0].compound.selectors.length === 1) {
|
|
|
|
|
@ -284,7 +281,9 @@ export default class Selector {
|
|
|
|
|
(i !== 0 ||
|
|
|
|
|
relative_selector.compound.selectors
|
|
|
|
|
.slice(1)
|
|
|
|
|
.some(s => s.type !== 'PseudoElementSelector' && s.type !== 'PseudoClassSelector'))
|
|
|
|
|
.some(
|
|
|
|
|
(s) => s.type !== 'PseudoElementSelector' && s.type !== 'PseudoClassSelector'
|
|
|
|
|
))
|
|
|
|
|
) {
|
|
|
|
|
error(selector, 'invalid-css-global-selector-list');
|
|
|
|
|
}
|
|
|
|
|
@ -297,9 +296,8 @@ export default class Selector {
|
|
|
|
|
get_amount_class_specificity_increased() {
|
|
|
|
|
// Is this right? Should we be counting the amount of blocks that are visible?
|
|
|
|
|
// Or should we be counting the amount of selectors that are visible?
|
|
|
|
|
return this.selector_list[0].filter(selector => selector.should_encapsulate).length;
|
|
|
|
|
return this.selector_list[0].filter((selector) => selector.should_encapsulate).length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -361,7 +359,7 @@ function apply_selector(blocks, node, to_encapsulate) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (blocks.every(block => block.global)) {
|
|
|
|
|
if (blocks.every((block) => block.global)) {
|
|
|
|
|
add_node(to_encapsulate, block, node);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@ -882,7 +880,6 @@ function loop_child(children, adjacent_only) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Not shared between different Selector instances
|
|
|
|
|
*/
|
|
|
|
|
@ -911,17 +908,25 @@ class RelativeSelector {
|
|
|
|
|
this.compound.add(selector);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get global() { return this.compound.global }
|
|
|
|
|
get host() { return this.compound.host }
|
|
|
|
|
get root() { return this.compound.root }
|
|
|
|
|
get end() {return this.compound.end }
|
|
|
|
|
get global() {
|
|
|
|
|
return this.compound.global;
|
|
|
|
|
}
|
|
|
|
|
get host() {
|
|
|
|
|
return this.compound.host;
|
|
|
|
|
}
|
|
|
|
|
get root() {
|
|
|
|
|
return this.compound.root;
|
|
|
|
|
}
|
|
|
|
|
get end() {
|
|
|
|
|
return this.compound.end;
|
|
|
|
|
}
|
|
|
|
|
get start() {
|
|
|
|
|
if (this.combinator) return this.combinator.start;
|
|
|
|
|
return this.compound.start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get contains_invisible_selectors() {
|
|
|
|
|
return this.compound.selectors.some(selector => !selector.visible);
|
|
|
|
|
return this.compound.selectors.some((selector) => !selector.visible);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -957,8 +962,8 @@ class CompoundSelector {
|
|
|
|
|
this.selectors = [];
|
|
|
|
|
this.start = -1;
|
|
|
|
|
this.end = -1;
|
|
|
|
|
this.host = false
|
|
|
|
|
this.root = false
|
|
|
|
|
this.host = false;
|
|
|
|
|
this.root = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @param {SimpleSelectorWithData} selector */
|
|
|
|
|
@ -977,7 +982,10 @@ class CompoundSelector {
|
|
|
|
|
this.selectors.length >= 1 &&
|
|
|
|
|
this.selectors[0].type === 'PseudoClassSelector' &&
|
|
|
|
|
this.selectors[0].name === 'global' &&
|
|
|
|
|
this.selectors.every(selector => selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector')
|
|
|
|
|
this.selectors.every(
|
|
|
|
|
(selector) =>
|
|
|
|
|
selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector'
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -995,14 +1003,14 @@ function group_selectors(selector, parent_selector_list) {
|
|
|
|
|
return [selector_to_blocks([...selector.children], null)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parent_selector_list.map(parent_complex_selector => {
|
|
|
|
|
return parent_selector_list.map((parent_complex_selector) => {
|
|
|
|
|
const block_group = selector_to_blocks(
|
|
|
|
|
[...selector.children],
|
|
|
|
|
[...parent_complex_selector] // Clone the parent's blocks to avoid modifying the original array
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return block_group;
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1010,7 +1018,7 @@ function group_selectors(selector, parent_selector_list) {
|
|
|
|
|
* @param {ComplexSelector | null} parent_complex_selector - The parent rule's selectors to insert/swap into the nesting selector positions.
|
|
|
|
|
*/
|
|
|
|
|
function selector_to_blocks(children, parent_complex_selector) {
|
|
|
|
|
let block = new RelativeSelector(null, new CompoundSelector);
|
|
|
|
|
let block = new RelativeSelector(null, new CompoundSelector());
|
|
|
|
|
const blocks = [block];
|
|
|
|
|
|
|
|
|
|
// If this is a nested rule
|
|
|
|
|
@ -1018,7 +1026,7 @@ function selector_to_blocks(children, parent_complex_selector) {
|
|
|
|
|
|
|
|
|
|
for (const child of children) {
|
|
|
|
|
if (child.type === 'Combinator') {
|
|
|
|
|
block = new RelativeSelector(child, new CompoundSelector);
|
|
|
|
|
block = new RelativeSelector(child, new CompoundSelector());
|
|
|
|
|
blocks.push(block);
|
|
|
|
|
} else if (child.type === 'NestingSelector') {
|
|
|
|
|
if (!parent_complex_selector) {
|
|
|
|
|
@ -1032,10 +1040,12 @@ function selector_to_blocks(children, parent_complex_selector) {
|
|
|
|
|
child.use_wrapper = child.use_wrapper ?? { used: false };
|
|
|
|
|
|
|
|
|
|
// Shallow copy the child to avoid modifying the original's visibility
|
|
|
|
|
block.add(/** @type {SimpleSelectorWithData} */ ({
|
|
|
|
|
...child,
|
|
|
|
|
visible: child.visible === undefined ? true : child.visible
|
|
|
|
|
}));
|
|
|
|
|
block.add(
|
|
|
|
|
/** @type {SimpleSelectorWithData} */ ({
|
|
|
|
|
...child,
|
|
|
|
|
visible: child.visible === undefined ? true : child.visible
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1053,9 +1063,9 @@ function get_parent_selectors(parent_complex_selector) {
|
|
|
|
|
parent_selectors.push(relative_selector.combinator);
|
|
|
|
|
}
|
|
|
|
|
parent_selectors.push(
|
|
|
|
|
...relative_selector.compound.selectors.map(selector => ({
|
|
|
|
|
...relative_selector.compound.selectors.map((selector) => ({
|
|
|
|
|
...selector,
|
|
|
|
|
visible: false,
|
|
|
|
|
visible: false
|
|
|
|
|
}))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -1070,7 +1080,7 @@ function get_parent_selectors(parent_complex_selector) {
|
|
|
|
|
* @param {ComplexSelector} parent_complex_selector - The parent blocks to insert into the nesting selector positions.
|
|
|
|
|
*/
|
|
|
|
|
function nest_fake_parents(children, parent_complex_selector) {
|
|
|
|
|
let nested_selector_index = children.findIndex(child => child.type === 'NestingSelector');
|
|
|
|
|
let nested_selector_index = children.findIndex((child) => child.type === 'NestingSelector');
|
|
|
|
|
let used_ampersand = false;
|
|
|
|
|
|
|
|
|
|
// TODO: Handle multiple nesting selectors?
|
|
|
|
|
@ -1102,20 +1112,19 @@ function nest_fake_parents(children, parent_complex_selector) {
|
|
|
|
|
let last_parent_child = parent_selectors[parent_selectors.length - 1];
|
|
|
|
|
let child_before = children[nested_selector_index - 1];
|
|
|
|
|
|
|
|
|
|
if(last_parent_child.type !== "Combinator" && !child_after) {
|
|
|
|
|
if (last_parent_child.type !== 'Combinator' && !child_after) {
|
|
|
|
|
// Case: b { c & { color: red }} (we need to mark b as visible so we increase specifity)
|
|
|
|
|
last_parent_child.visible = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(
|
|
|
|
|
child_before?.type !== "Combinator"
|
|
|
|
|
&& child_after?.type !== "Combinator"
|
|
|
|
|
&& !used_ampersand
|
|
|
|
|
if (
|
|
|
|
|
child_before?.type !== 'Combinator' &&
|
|
|
|
|
child_after?.type !== 'Combinator' &&
|
|
|
|
|
!used_ampersand
|
|
|
|
|
) {
|
|
|
|
|
children.splice(nested_selector_index, 0, FakeCombinator);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Finally, insert the parent selectors into the children array
|
|
|
|
|
children.splice(nested_selector_index, 0, ...parent_selectors);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|