|
|
|
@ -1,16 +1,16 @@
|
|
|
|
import { gather_possible_values, UNKNOWN } from './gather_possible_values.js';
|
|
|
|
import { gather_possible_values, UNKNOWN } from './gather_possible_values.js';
|
|
|
|
import compiler_errors from '../compiler_errors.js';
|
|
|
|
import compiler_errors from '../compiler_errors.js';
|
|
|
|
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns.js';
|
|
|
|
import { regex_starts_with_whitespace, regex_ends_with_whitespace } from '../../utils/patterns.js';
|
|
|
|
var BlockAppliesToNode;
|
|
|
|
let BlockAppliesToNode;
|
|
|
|
(function (BlockAppliesToNode) {
|
|
|
|
(function (BlockAppliesToNode) {
|
|
|
|
BlockAppliesToNode[BlockAppliesToNode["NotPossible"] = 0] = "NotPossible";
|
|
|
|
BlockAppliesToNode[(BlockAppliesToNode['NotPossible'] = 0)] = 'NotPossible';
|
|
|
|
BlockAppliesToNode[BlockAppliesToNode["Possible"] = 1] = "Possible";
|
|
|
|
BlockAppliesToNode[(BlockAppliesToNode['Possible'] = 1)] = 'Possible';
|
|
|
|
BlockAppliesToNode[BlockAppliesToNode["UnknownSelectorType"] = 2] = "UnknownSelectorType";
|
|
|
|
BlockAppliesToNode[(BlockAppliesToNode['UnknownSelectorType'] = 2)] = 'UnknownSelectorType';
|
|
|
|
})(BlockAppliesToNode || (BlockAppliesToNode = {}));
|
|
|
|
})(BlockAppliesToNode || (BlockAppliesToNode = {}));
|
|
|
|
var NodeExist;
|
|
|
|
let NodeExist;
|
|
|
|
(function (NodeExist) {
|
|
|
|
(function (NodeExist) {
|
|
|
|
NodeExist[NodeExist["Probably"] = 1] = "Probably";
|
|
|
|
NodeExist[(NodeExist['Probably'] = 1)] = 'Probably';
|
|
|
|
NodeExist[NodeExist["Definitely"] = 2] = "Definitely";
|
|
|
|
NodeExist[(NodeExist['Definitely'] = 2)] = 'Definitely';
|
|
|
|
})(NodeExist || (NodeExist = {}));
|
|
|
|
})(NodeExist || (NodeExist = {}));
|
|
|
|
const whitelist_attribute_selector = new Map([
|
|
|
|
const whitelist_attribute_selector = new Map([
|
|
|
|
['details', new Set(['open'])],
|
|
|
|
['details', new Set(['open'])],
|
|
|
|
@ -18,7 +18,6 @@ const whitelist_attribute_selector = new Map([
|
|
|
|
]);
|
|
|
|
]);
|
|
|
|
const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/;
|
|
|
|
const regex_is_single_css_selector = /[^\\],(?!([^([]+[^\\]|[^([\\])[)\]])/;
|
|
|
|
export default class Selector {
|
|
|
|
export default class Selector {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {import('./private.js').CssNode} */
|
|
|
|
/** @type {import('./private.js').CssNode} */
|
|
|
|
node;
|
|
|
|
node;
|
|
|
|
|
|
|
|
|
|
|
|
@ -45,8 +44,7 @@ export default class Selector {
|
|
|
|
// take trailing :global(...) selectors out of consideration
|
|
|
|
// take trailing :global(...) selectors out of consideration
|
|
|
|
let i = this.blocks.length;
|
|
|
|
let i = this.blocks.length;
|
|
|
|
while (i > 0) {
|
|
|
|
while (i > 0) {
|
|
|
|
if (!this.blocks[i - 1].global)
|
|
|
|
if (!this.blocks[i - 1].global) break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
i -= 1;
|
|
|
|
i -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.local_blocks = this.blocks.slice(0, i);
|
|
|
|
this.local_blocks = this.blocks.slice(0, i);
|
|
|
|
@ -57,7 +55,6 @@ export default class Selector {
|
|
|
|
|
|
|
|
|
|
|
|
/** @param {import('../nodes/Element.js').default} node */
|
|
|
|
/** @param {import('../nodes/Element.js').default} node */
|
|
|
|
apply(node) {
|
|
|
|
apply(node) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Array<{ node: import('../nodes/Element.js').default; block: Block }>} */
|
|
|
|
/** @type {Array<{ node: import('../nodes/Element.js').default; block: Block }>} */
|
|
|
|
const to_encapsulate = [];
|
|
|
|
const to_encapsulate = [];
|
|
|
|
apply_selector(this.local_blocks.slice(), node, to_encapsulate);
|
|
|
|
apply_selector(this.local_blocks.slice(), node, to_encapsulate);
|
|
|
|
@ -72,7 +69,6 @@ export default class Selector {
|
|
|
|
|
|
|
|
|
|
|
|
/** @param {import('magic-string').default} code */
|
|
|
|
/** @param {import('magic-string').default} code */
|
|
|
|
minify(code) {
|
|
|
|
minify(code) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
/** @type {number} */
|
|
|
|
let c = null;
|
|
|
|
let c = null;
|
|
|
|
this.blocks.forEach((block, i) => {
|
|
|
|
this.blocks.forEach((block, i) => {
|
|
|
|
@ -91,7 +87,8 @@ export default class Selector {
|
|
|
|
* @param {number} max_amount_class_specificity_increased
|
|
|
|
* @param {number} max_amount_class_specificity_increased
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
transform(code, attr, max_amount_class_specificity_increased) {
|
|
|
|
transform(code, attr, max_amount_class_specificity_increased) {
|
|
|
|
const amount_class_specificity_to_increase = max_amount_class_specificity_increased -
|
|
|
|
const amount_class_specificity_to_increase =
|
|
|
|
|
|
|
|
max_amount_class_specificity_increased -
|
|
|
|
this.blocks.filter((block) => block.should_encapsulate).length;
|
|
|
|
this.blocks.filter((block) => block.should_encapsulate).length;
|
|
|
|
|
|
|
|
|
|
|
|
/** @param {import('./private.js').CssNode} selector */
|
|
|
|
/** @param {import('./private.js').CssNode} selector */
|
|
|
|
@ -116,15 +113,13 @@ export default class Selector {
|
|
|
|
const selector = block.selectors[i];
|
|
|
|
const selector = block.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') {
|
|
|
|
if (i === 0)
|
|
|
|
if (i === 0) code.prependRight(selector.start, attr);
|
|
|
|
code.prependRight(selector.start, attr);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (selector.type === 'TypeSelector' && selector.name === '*') {
|
|
|
|
if (selector.type === 'TypeSelector' && selector.name === '*') {
|
|
|
|
code.update(selector.start, selector.end, attr);
|
|
|
|
code.update(selector.start, selector.end, attr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
code.appendLeft(selector.end, attr);
|
|
|
|
code.appendLeft(selector.end, attr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@ -135,9 +130,12 @@ export default class Selector {
|
|
|
|
remove_global_pseudo_class(block.selectors[0]);
|
|
|
|
remove_global_pseudo_class(block.selectors[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (block.should_encapsulate)
|
|
|
|
if (block.should_encapsulate)
|
|
|
|
encapsulate_block(block, index === this.blocks.length - 1
|
|
|
|
encapsulate_block(
|
|
|
|
|
|
|
|
block,
|
|
|
|
|
|
|
|
index === this.blocks.length - 1
|
|
|
|
? attr.repeat(amount_class_specificity_to_increase + 1)
|
|
|
|
? attr.repeat(amount_class_specificity_to_increase + 1)
|
|
|
|
: attr);
|
|
|
|
: attr
|
|
|
|
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -146,12 +144,10 @@ export default class Selector {
|
|
|
|
let start = 0;
|
|
|
|
let start = 0;
|
|
|
|
let end = this.blocks.length;
|
|
|
|
let end = this.blocks.length;
|
|
|
|
for (; start < end; start += 1) {
|
|
|
|
for (; start < end; start += 1) {
|
|
|
|
if (!this.blocks[start].global)
|
|
|
|
if (!this.blocks[start].global) break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; end > start; end -= 1) {
|
|
|
|
for (; end > start; end -= 1) {
|
|
|
|
if (!this.blocks[end - 1].global)
|
|
|
|
if (!this.blocks[end - 1].global) break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let i = start; i < end; i += 1) {
|
|
|
|
for (let i = start; i < end; i += 1) {
|
|
|
|
if (this.blocks[i].global) {
|
|
|
|
if (this.blocks[i].global) {
|
|
|
|
@ -185,10 +181,20 @@ export default class Selector {
|
|
|
|
for (let i = 0; i < this.blocks.length; i++) {
|
|
|
|
for (let i = 0; i < this.blocks.length; i++) {
|
|
|
|
const block = this.blocks[i];
|
|
|
|
const block = this.blocks[i];
|
|
|
|
if (block.combinator && block.selectors.length === 0) {
|
|
|
|
if (block.combinator && block.selectors.length === 0) {
|
|
|
|
component.error(this.node, compiler_errors.css_invalid_selector(component.source.slice(this.node.start, this.node.end)));
|
|
|
|
component.error(
|
|
|
|
|
|
|
|
this.node,
|
|
|
|
|
|
|
|
compiler_errors.css_invalid_selector(
|
|
|
|
|
|
|
|
component.source.slice(this.node.start, this.node.end)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!block.combinator && block.selectors.length === 0) {
|
|
|
|
if (!block.combinator && block.selectors.length === 0) {
|
|
|
|
component.error(this.node, compiler_errors.css_invalid_selector(component.source.slice(this.node.start, this.node.end)));
|
|
|
|
component.error(
|
|
|
|
|
|
|
|
this.node,
|
|
|
|
|
|
|
|
compiler_errors.css_invalid_selector(
|
|
|
|
|
|
|
|
component.source.slice(this.node.start, this.node.end)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -198,12 +204,14 @@ export default class Selector {
|
|
|
|
for (const block of this.blocks) {
|
|
|
|
for (const block of this.blocks) {
|
|
|
|
for (let index = 0; index < block.selectors.length; index++) {
|
|
|
|
for (let index = 0; index < block.selectors.length; index++) {
|
|
|
|
const selector = block.selectors[index];
|
|
|
|
const selector = block.selectors[index];
|
|
|
|
if (selector.type === 'PseudoClassSelector' &&
|
|
|
|
if (
|
|
|
|
|
|
|
|
selector.type === 'PseudoClassSelector' &&
|
|
|
|
selector.name === 'global' &&
|
|
|
|
selector.name === 'global' &&
|
|
|
|
index !== 0 &&
|
|
|
|
index !== 0 &&
|
|
|
|
selector.children &&
|
|
|
|
selector.children &&
|
|
|
|
selector.children.length > 0 &&
|
|
|
|
selector.children.length > 0 &&
|
|
|
|
!/[.:#\s]/.test(selector.children[0].value[0])) {
|
|
|
|
!/[.:#\s]/.test(selector.children[0].value[0])
|
|
|
|
|
|
|
|
) {
|
|
|
|
component.error(selector, compiler_errors.css_invalid_global_selector_position);
|
|
|
|
component.error(selector, compiler_errors.css_invalid_global_selector_position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -228,10 +236,11 @@ export default class Selector {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function apply_selector(blocks, node, to_encapsulate) {
|
|
|
|
function apply_selector(blocks, node, to_encapsulate) {
|
|
|
|
const block = blocks.pop();
|
|
|
|
const block = blocks.pop();
|
|
|
|
if (!block)
|
|
|
|
if (!block) return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!node) {
|
|
|
|
if (!node) {
|
|
|
|
return ((block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0));
|
|
|
|
return (
|
|
|
|
|
|
|
|
(block.global && blocks.every((block) => block.global)) || (block.host && blocks.length === 0)
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (block_might_apply_to_node(block, node)) {
|
|
|
|
switch (block_might_apply_to_node(block, node)) {
|
|
|
|
case BlockAppliesToNode.NotPossible:
|
|
|
|
case BlockAppliesToNode.NotPossible:
|
|
|
|
@ -253,7 +262,9 @@ function apply_selector(blocks, node, to_encapsulate) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let parent = node;
|
|
|
|
let parent = node;
|
|
|
|
while ((parent = get_element_parent(parent))) {
|
|
|
|
while ((parent = get_element_parent(parent))) {
|
|
|
|
if (block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
block_might_apply_to_node(ancestor_block, parent) !== BlockAppliesToNode.NotPossible
|
|
|
|
|
|
|
|
) {
|
|
|
|
to_encapsulate.push({ node: parent, block: ancestor_block });
|
|
|
|
to_encapsulate.push({ node: parent, block: ancestor_block });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -267,16 +278,14 @@ function apply_selector(blocks, node, to_encapsulate) {
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (block.combinator.name === '>') {
|
|
|
|
else if (block.combinator.name === '>') {
|
|
|
|
|
|
|
|
const has_global_parent = blocks.every((block) => block.global);
|
|
|
|
const has_global_parent = blocks.every((block) => block.global);
|
|
|
|
if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
|
|
|
|
if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) {
|
|
|
|
to_encapsulate.push({ node, block });
|
|
|
|
to_encapsulate.push({ node, block });
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
|
|
|
|
else if (block.combinator.name === '+' || block.combinator.name === '~') {
|
|
|
|
|
|
|
|
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
|
|
|
|
const siblings = get_possible_element_siblings(node, block.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
|
|
|
|
@ -316,44 +325,56 @@ function block_might_apply_to_node(block, node) {
|
|
|
|
let i = block.selectors.length;
|
|
|
|
let i = block.selectors.length;
|
|
|
|
while (i--) {
|
|
|
|
while (i--) {
|
|
|
|
const selector = block.selectors[i];
|
|
|
|
const selector = block.selectors[i];
|
|
|
|
const name = typeof selector.name === 'string' &&
|
|
|
|
const name =
|
|
|
|
|
|
|
|
typeof selector.name === 'string' &&
|
|
|
|
selector.name.replace(regex_backslash_and_following_character, '$1');
|
|
|
|
selector.name.replace(regex_backslash_and_following_character, '$1');
|
|
|
|
if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) {
|
|
|
|
if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) {
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (block.selectors.length === 1 &&
|
|
|
|
if (
|
|
|
|
|
|
|
|
block.selectors.length === 1 &&
|
|
|
|
selector.type === 'PseudoClassSelector' &&
|
|
|
|
selector.type === 'PseudoClassSelector' &&
|
|
|
|
name === 'global') {
|
|
|
|
name === 'global'
|
|
|
|
|
|
|
|
) {
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') {
|
|
|
|
if (selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector') {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (selector.type === 'ClassSelector') {
|
|
|
|
if (selector.type === 'ClassSelector') {
|
|
|
|
if (!attribute_matches(node, 'class', name, '~=', false) &&
|
|
|
|
if (
|
|
|
|
!node.classes.some((c) => c.name === name))
|
|
|
|
!attribute_matches(node, 'class', name, '~=', false) &&
|
|
|
|
|
|
|
|
!node.classes.some((c) => c.name === name)
|
|
|
|
|
|
|
|
)
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
}
|
|
|
|
} else if (selector.type === 'IdSelector') {
|
|
|
|
else if (selector.type === 'IdSelector') {
|
|
|
|
if (!attribute_matches(node, 'id', name, '=', false)) return BlockAppliesToNode.NotPossible;
|
|
|
|
if (!attribute_matches(node, 'id', name, '=', false))
|
|
|
|
} else if (selector.type === 'AttributeSelector') {
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
if (
|
|
|
|
}
|
|
|
|
!(
|
|
|
|
else if (selector.type === 'AttributeSelector') {
|
|
|
|
whitelist_attribute_selector.has(node.name.toLowerCase()) &&
|
|
|
|
if (!(whitelist_attribute_selector.has(node.name.toLowerCase()) &&
|
|
|
|
|
|
|
|
whitelist_attribute_selector
|
|
|
|
whitelist_attribute_selector
|
|
|
|
.get(node.name.toLowerCase())
|
|
|
|
.get(node.name.toLowerCase())
|
|
|
|
.has(selector.name.name.toLowerCase())) &&
|
|
|
|
.has(selector.name.name.toLowerCase())
|
|
|
|
!attribute_matches(node, selector.name.name, selector.value && unquote(selector.value), selector.matcher, selector.flags)) {
|
|
|
|
) &&
|
|
|
|
|
|
|
|
!attribute_matches(
|
|
|
|
|
|
|
|
node,
|
|
|
|
|
|
|
|
selector.name.name,
|
|
|
|
|
|
|
|
selector.value && unquote(selector.value),
|
|
|
|
|
|
|
|
selector.matcher,
|
|
|
|
|
|
|
|
selector.flags
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
) {
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (selector.type === 'TypeSelector') {
|
|
|
|
else if (selector.type === 'TypeSelector') {
|
|
|
|
if (
|
|
|
|
if (node.name.toLowerCase() !== name.toLowerCase() &&
|
|
|
|
node.name.toLowerCase() !== name.toLowerCase() &&
|
|
|
|
name !== '*' &&
|
|
|
|
name !== '*' &&
|
|
|
|
!node.is_dynamic_element)
|
|
|
|
!node.is_dynamic_element
|
|
|
|
|
|
|
|
)
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
return BlockAppliesToNode.NotPossible;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
return BlockAppliesToNode.UnknownSelectorType;
|
|
|
|
return BlockAppliesToNode.UnknownSelectorType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -398,21 +419,15 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function attribute_matches(node, name, expected_value, operator, case_insensitive) {
|
|
|
|
function attribute_matches(node, name, expected_value, operator, case_insensitive) {
|
|
|
|
const spread = node.attributes.find((attr) => attr.type === 'Spread');
|
|
|
|
const spread = node.attributes.find((attr) => attr.type === 'Spread');
|
|
|
|
if (spread)
|
|
|
|
if (spread) return true;
|
|
|
|
return true;
|
|
|
|
if (node.bindings.some((binding) => binding.name === name)) return true;
|
|
|
|
if (node.bindings.some((binding) => binding.name === name))
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
const attr = node.attributes.find((attr) => attr.name === name);
|
|
|
|
const attr = node.attributes.find((attr) => attr.name === name);
|
|
|
|
if (!attr)
|
|
|
|
if (!attr) return false;
|
|
|
|
return false;
|
|
|
|
if (attr.is_true) return operator === null;
|
|
|
|
if (attr.is_true)
|
|
|
|
if (expected_value == null) return true;
|
|
|
|
return operator === null;
|
|
|
|
|
|
|
|
if (expected_value == null)
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (attr.chunks.length === 1) {
|
|
|
|
if (attr.chunks.length === 1) {
|
|
|
|
const value = attr.chunks[0];
|
|
|
|
const value = attr.chunks[0];
|
|
|
|
if (!value)
|
|
|
|
if (!value) return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (value.type === 'Text')
|
|
|
|
if (value.type === 'Text')
|
|
|
|
return test_attribute(operator, expected_value, case_insensitive, value.data);
|
|
|
|
return test_attribute(operator, expected_value, case_insensitive, value.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -422,21 +437,18 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
|
|
|
|
const current_possible_values = new Set();
|
|
|
|
const current_possible_values = new Set();
|
|
|
|
if (chunk.type === 'Text') {
|
|
|
|
if (chunk.type === 'Text') {
|
|
|
|
current_possible_values.add(chunk.data);
|
|
|
|
current_possible_values.add(chunk.data);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
gather_possible_values(chunk.node, current_possible_values);
|
|
|
|
gather_possible_values(chunk.node, current_possible_values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// impossible to find out all combinations
|
|
|
|
// impossible to find out all combinations
|
|
|
|
if (current_possible_values.has(UNKNOWN))
|
|
|
|
if (current_possible_values.has(UNKNOWN)) return true;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (prev_values.length > 0) {
|
|
|
|
if (prev_values.length > 0) {
|
|
|
|
const start_with_space = [];
|
|
|
|
const start_with_space = [];
|
|
|
|
const remaining = [];
|
|
|
|
const remaining = [];
|
|
|
|
current_possible_values.forEach((current_possible_value) => {
|
|
|
|
current_possible_values.forEach((current_possible_value) => {
|
|
|
|
if (regex_starts_with_whitespace.test(current_possible_value)) {
|
|
|
|
if (regex_starts_with_whitespace.test(current_possible_value)) {
|
|
|
|
start_with_space.push(current_possible_value);
|
|
|
|
start_with_space.push(current_possible_value);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
remaining.push(current_possible_value);
|
|
|
|
remaining.push(current_possible_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -454,14 +466,12 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
|
|
|
|
start_with_space.forEach((value) => {
|
|
|
|
start_with_space.forEach((value) => {
|
|
|
|
if (regex_ends_with_whitespace.test(value)) {
|
|
|
|
if (regex_ends_with_whitespace.test(value)) {
|
|
|
|
possible_values.add(value);
|
|
|
|
possible_values.add(value);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
prev_values.push(value);
|
|
|
|
prev_values.push(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
prev_values.forEach((prev_value) => possible_values.add(prev_value));
|
|
|
|
prev_values.forEach((prev_value) => possible_values.add(prev_value));
|
|
|
|
prev_values = [];
|
|
|
|
prev_values = [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -469,8 +479,7 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
|
|
|
|
current_possible_values.forEach((current_possible_value) => {
|
|
|
|
current_possible_values.forEach((current_possible_value) => {
|
|
|
|
if (regex_ends_with_whitespace.test(current_possible_value)) {
|
|
|
|
if (regex_ends_with_whitespace.test(current_possible_value)) {
|
|
|
|
possible_values.add(current_possible_value);
|
|
|
|
possible_values.add(current_possible_value);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
prev_values.push(current_possible_value);
|
|
|
|
prev_values.push(current_possible_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -483,19 +492,16 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prev_values.forEach((prev_value) => possible_values.add(prev_value));
|
|
|
|
prev_values.forEach((prev_value) => possible_values.add(prev_value));
|
|
|
|
if (possible_values.has(UNKNOWN))
|
|
|
|
if (possible_values.has(UNKNOWN)) return true;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for (const value of possible_values) {
|
|
|
|
for (const value of possible_values) {
|
|
|
|
if (test_attribute(operator, expected_value, case_insensitive, value))
|
|
|
|
if (test_attribute(operator, expected_value, case_insensitive, value)) return true;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** @param {import('./private.js').CssNode} value */
|
|
|
|
/** @param {import('./private.js').CssNode} value */
|
|
|
|
function unquote(value) {
|
|
|
|
function unquote(value) {
|
|
|
|
if (value.type === 'Identifier')
|
|
|
|
if (value.type === 'Identifier') return value.name;
|
|
|
|
return value.name;
|
|
|
|
|
|
|
|
const str = value.value;
|
|
|
|
const str = value.value;
|
|
|
|
if ((str[0] === str[str.length - 1] && str[0] === "'") || str[0] === '"') {
|
|
|
|
if ((str[0] === str[str.length - 1] && str[0] === "'") || str[0] === '"') {
|
|
|
|
return str.slice(1, str.length - 1);
|
|
|
|
return str.slice(1, str.length - 1);
|
|
|
|
@ -508,11 +514,9 @@ function unquote(value) {
|
|
|
|
* @returns {any}
|
|
|
|
* @returns {any}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function get_element_parent(node) {
|
|
|
|
function get_element_parent(node) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {import('../nodes/interfaces.js').INode} */
|
|
|
|
/** @type {import('../nodes/interfaces.js').INode} */
|
|
|
|
let parent = node;
|
|
|
|
let parent = node;
|
|
|
|
while ((parent = parent.parent) && parent.type !== 'Element')
|
|
|
|
while ((parent = parent.parent) && parent.type !== 'Element');
|
|
|
|
;
|
|
|
|
|
|
|
|
return /** @type {import('../nodes/Element.js').default | null} */ (parent);
|
|
|
|
return /** @type {import('../nodes/Element.js').default | null} */ (parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@ -534,7 +538,6 @@ function get_element_parent(node) {
|
|
|
|
* @returns {import('../nodes/interfaces.js').INode}
|
|
|
|
* @returns {import('../nodes/interfaces.js').INode}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function find_previous_sibling(node) {
|
|
|
|
function find_previous_sibling(node) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {import('../nodes/interfaces.js').INode} */
|
|
|
|
/** @type {import('../nodes/interfaces.js').INode} */
|
|
|
|
let current_node = node;
|
|
|
|
let current_node = node;
|
|
|
|
do {
|
|
|
|
do {
|
|
|
|
@ -559,7 +562,6 @@ function find_previous_sibling(node) {
|
|
|
|
* @returns {Map<import('../nodes/Element.js').default, NodeExist>}
|
|
|
|
* @returns {Map<import('../nodes/Element.js').default, NodeExist>}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const result = new Map();
|
|
|
|
const result = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
@ -567,14 +569,17 @@ function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
let prev = node;
|
|
|
|
let prev = node;
|
|
|
|
while ((prev = find_previous_sibling(prev))) {
|
|
|
|
while ((prev = find_previous_sibling(prev))) {
|
|
|
|
if (prev.type === 'Element') {
|
|
|
|
if (prev.type === 'Element') {
|
|
|
|
if (!prev.attributes.find((attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot')) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
!prev.attributes.find(
|
|
|
|
|
|
|
|
(attr) => attr.type === 'Attribute' && attr.name.toLowerCase() === 'slot'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
) {
|
|
|
|
result.set(prev, NodeExist.Definitely);
|
|
|
|
result.set(prev, NodeExist.Definitely);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (adjacent_only) {
|
|
|
|
if (adjacent_only) {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
|
|
|
|
else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
|
|
|
|
|
|
|
|
const possible_last_child = get_possible_last_child(prev, adjacent_only);
|
|
|
|
const possible_last_child = get_possible_last_child(prev, adjacent_only);
|
|
|
|
add_to_map(possible_last_child, result);
|
|
|
|
add_to_map(possible_last_child, result);
|
|
|
|
if (adjacent_only && has_definite_elements(possible_last_child)) {
|
|
|
|
if (adjacent_only && has_definite_elements(possible_last_child)) {
|
|
|
|
@ -583,27 +588,26 @@ function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!prev || !adjacent_only) {
|
|
|
|
if (!prev || !adjacent_only) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {import('../nodes/interfaces.js').INode} */
|
|
|
|
/** @type {import('../nodes/interfaces.js').INode} */
|
|
|
|
let parent = node;
|
|
|
|
let parent = node;
|
|
|
|
let skip_each_for_last_child = node.type === 'ElseBlock';
|
|
|
|
let skip_each_for_last_child = node.type === 'ElseBlock';
|
|
|
|
while ((parent = parent.parent) &&
|
|
|
|
while (
|
|
|
|
|
|
|
|
(parent = parent.parent) &&
|
|
|
|
(parent.type === 'EachBlock' ||
|
|
|
|
(parent.type === 'EachBlock' ||
|
|
|
|
parent.type === 'IfBlock' ||
|
|
|
|
parent.type === 'IfBlock' ||
|
|
|
|
parent.type === 'ElseBlock' ||
|
|
|
|
parent.type === 'ElseBlock' ||
|
|
|
|
parent.type === 'AwaitBlock')) {
|
|
|
|
parent.type === 'AwaitBlock')
|
|
|
|
|
|
|
|
) {
|
|
|
|
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
|
|
|
|
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
|
|
|
|
add_to_map(possible_siblings, result);
|
|
|
|
add_to_map(possible_siblings, result);
|
|
|
|
if (parent.type === 'EachBlock') {
|
|
|
|
if (parent.type === 'EachBlock') {
|
|
|
|
// first child of each block can select the last child of each block as previous sibling
|
|
|
|
// first child of each block can select the last child of each block as previous sibling
|
|
|
|
if (skip_each_for_last_child) {
|
|
|
|
if (skip_each_for_last_child) {
|
|
|
|
skip_each_for_last_child = false;
|
|
|
|
skip_each_for_last_child = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
add_to_map(get_possible_last_child(parent, adjacent_only), result);
|
|
|
|
add_to_map(get_possible_last_child(parent, adjacent_only), result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (parent.type === 'ElseBlock') {
|
|
|
|
else if (parent.type === 'ElseBlock') {
|
|
|
|
|
|
|
|
skip_each_for_last_child = true;
|
|
|
|
skip_each_for_last_child = true;
|
|
|
|
parent = parent.parent;
|
|
|
|
parent = parent.parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -621,18 +625,14 @@ function get_possible_element_siblings(node, adjacent_only) {
|
|
|
|
* @returns {Map<import('../nodes/Element.js').default, NodeExist>}
|
|
|
|
* @returns {Map<import('../nodes/Element.js').default, NodeExist>}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function get_possible_last_child(block, adjacent_only) {
|
|
|
|
function get_possible_last_child(block, adjacent_only) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const result = new Map();
|
|
|
|
const result = new Map();
|
|
|
|
if (block.type === 'EachBlock') {
|
|
|
|
if (block.type === 'EachBlock') {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const each_result = loop_child(block.children, adjacent_only);
|
|
|
|
const each_result = loop_child(block.children, adjacent_only);
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const else_result = block.else
|
|
|
|
const else_result = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
|
|
|
|
? loop_child(block.else.children, adjacent_only)
|
|
|
|
|
|
|
|
: new Map();
|
|
|
|
|
|
|
|
const not_exhaustive = !has_definite_elements(else_result);
|
|
|
|
const not_exhaustive = !has_definite_elements(else_result);
|
|
|
|
if (not_exhaustive) {
|
|
|
|
if (not_exhaustive) {
|
|
|
|
mark_as_probably(each_result);
|
|
|
|
mark_as_probably(each_result);
|
|
|
|
@ -640,16 +640,12 @@ 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 (block.type === 'IfBlock') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const if_result = loop_child(block.children, adjacent_only);
|
|
|
|
const if_result = loop_child(block.children, adjacent_only);
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const else_result = block.else
|
|
|
|
const else_result = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
|
|
|
|
? loop_child(block.else.children, adjacent_only)
|
|
|
|
|
|
|
|
: 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) {
|
|
|
|
mark_as_probably(if_result);
|
|
|
|
mark_as_probably(if_result);
|
|
|
|
@ -657,24 +653,19 @@ 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 (block.type === 'AwaitBlock') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const pending_result = block.pending
|
|
|
|
const pending_result = block.pending
|
|
|
|
? loop_child(block.pending.children, adjacent_only)
|
|
|
|
? loop_child(block.pending.children, adjacent_only)
|
|
|
|
: new Map();
|
|
|
|
: new Map();
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const then_result = block.then
|
|
|
|
const then_result = block.then ? loop_child(block.then.children, adjacent_only) : new Map();
|
|
|
|
? loop_child(block.then.children, adjacent_only)
|
|
|
|
|
|
|
|
: new Map();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const catch_result = block.catch
|
|
|
|
const catch_result = block.catch ? loop_child(block.catch.children, adjacent_only) : new Map();
|
|
|
|
? loop_child(block.catch.children, adjacent_only)
|
|
|
|
const not_exhaustive =
|
|
|
|
: new Map();
|
|
|
|
!has_definite_elements(pending_result) ||
|
|
|
|
const not_exhaustive = !has_definite_elements(pending_result) ||
|
|
|
|
|
|
|
|
!has_definite_elements(then_result) ||
|
|
|
|
!has_definite_elements(then_result) ||
|
|
|
|
!has_definite_elements(catch_result);
|
|
|
|
!has_definite_elements(catch_result);
|
|
|
|
if (not_exhaustive) {
|
|
|
|
if (not_exhaustive) {
|
|
|
|
@ -694,8 +685,7 @@ function get_possible_last_child(block, adjacent_only) {
|
|
|
|
* @returns {boolean}
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function has_definite_elements(result) {
|
|
|
|
function has_definite_elements(result) {
|
|
|
|
if (result.size === 0)
|
|
|
|
if (result.size === 0) return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (const exist of result.values()) {
|
|
|
|
for (const exist of result.values()) {
|
|
|
|
if (exist === NodeExist.Definitely) {
|
|
|
|
if (exist === NodeExist.Definitely) {
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
@ -720,8 +710,7 @@ function add_to_map(from, to) {
|
|
|
|
* @returns {NodeExist}
|
|
|
|
* @returns {NodeExist}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function higher_existence(exist1, exist2) {
|
|
|
|
function higher_existence(exist1, exist2) {
|
|
|
|
if (exist1 === undefined || exist2 === undefined)
|
|
|
|
if (exist1 === undefined || exist2 === undefined) return exist1 || exist2;
|
|
|
|
return exist1 || exist2;
|
|
|
|
|
|
|
|
return exist1 > exist2 ? exist1 : exist2;
|
|
|
|
return exist1 > exist2 ? exist1 : exist2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -737,7 +726,6 @@ function mark_as_probably(result) {
|
|
|
|
* @param {boolean} adjacent_only
|
|
|
|
* @param {boolean} adjacent_only
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
function loop_child(children, adjacent_only) {
|
|
|
|
function loop_child(children, adjacent_only) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
/** @type {Map<import('../nodes/Element.js').default, NodeExist>} */
|
|
|
|
const result = new Map();
|
|
|
|
const result = new Map();
|
|
|
|
for (let i = children.length - 1; i >= 0; i--) {
|
|
|
|
for (let i = children.length - 1; i >= 0; i--) {
|
|
|
|
@ -747,10 +735,11 @@ function loop_child(children, adjacent_only) {
|
|
|
|
if (adjacent_only) {
|
|
|
|
if (adjacent_only) {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (
|
|
|
|
else if (child.type === 'EachBlock' ||
|
|
|
|
child.type === 'EachBlock' ||
|
|
|
|
child.type === 'IfBlock' ||
|
|
|
|
child.type === 'IfBlock' ||
|
|
|
|
child.type === 'AwaitBlock') {
|
|
|
|
child.type === 'AwaitBlock'
|
|
|
|
|
|
|
|
) {
|
|
|
|
const child_result = get_possible_last_child(child, adjacent_only);
|
|
|
|
const child_result = get_possible_last_child(child, adjacent_only);
|
|
|
|
add_to_map(child_result, result);
|
|
|
|
add_to_map(child_result, result);
|
|
|
|
if (adjacent_only && has_definite_elements(child_result)) {
|
|
|
|
if (adjacent_only && has_definite_elements(child_result)) {
|
|
|
|
@ -761,7 +750,6 @@ function loop_child(children, adjacent_only) {
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class Block {
|
|
|
|
class Block {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
|
/** @type {boolean} */
|
|
|
|
host;
|
|
|
|
host;
|
|
|
|
|
|
|
|
|
|
|
|
@ -805,16 +793,20 @@ class Block {
|
|
|
|
this.end = selector.end;
|
|
|
|
this.end = selector.end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
get global() {
|
|
|
|
get global() {
|
|
|
|
return (this.selectors.length >= 1 &&
|
|
|
|
return (
|
|
|
|
|
|
|
|
this.selectors.length >= 1 &&
|
|
|
|
this.selectors[0].type === 'PseudoClassSelector' &&
|
|
|
|
this.selectors[0].type === 'PseudoClassSelector' &&
|
|
|
|
this.selectors[0].name === 'global' &&
|
|
|
|
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'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** @param {import('./private.js').CssNode} selector */
|
|
|
|
/** @param {import('./private.js').CssNode} selector */
|
|
|
|
function group_selectors(selector) {
|
|
|
|
function group_selectors(selector) {
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {Block} */
|
|
|
|
/** @type {Block} */
|
|
|
|
let block = new Block(null);
|
|
|
|
let block = new Block(null);
|
|
|
|
const blocks = [block];
|
|
|
|
const blocks = [block];
|
|
|
|
@ -822,14 +814,9 @@ function group_selectors(selector) {
|
|
|
|
if (child.type === 'WhiteSpace' || child.type === 'Combinator') {
|
|
|
|
if (child.type === 'WhiteSpace' || child.type === 'Combinator') {
|
|
|
|
block = new Block(child);
|
|
|
|
block = new Block(child);
|
|
|
|
blocks.push(block);
|
|
|
|
blocks.push(block);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
block.add(child);
|
|
|
|
block.add(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return blocks;
|
|
|
|
return blocks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|