Handle rule nesting, remove NestedSelector from SimpleSelector, improve logic for encapsulation

pull/9549/head
Albert Marashi 3 years ago
parent 0e67006d4b
commit d9174c4fbf

@ -129,14 +129,15 @@ function read_at_rule(parser) {
/** /**
* @param {import('../index.js').Parser} parser * @param {import('../index.js').Parser} parser
* @param {boolean} nested Whether this rule is nested inside another rule
* @returns {import('#compiler').Css.Rule} * @returns {import('#compiler').Css.Rule}
*/ */
function read_rule(parser) { function read_rule(parser, nested = false) {
const start = parser.index; const start = parser.index;
return { return {
type: 'Rule', type: 'Rule',
prelude: read_selector_list(parser), prelude: read_selector_list(parser, nested),
block: read_block(parser), block: read_block(parser),
start, start,
end: parser.index end: parser.index
@ -145,16 +146,17 @@ function read_rule(parser) {
/** /**
* @param {import('../index.js').Parser} parser * @param {import('../index.js').Parser} parser
* @param {boolean} nested Whether this selector list is nested inside another rule
* @returns {import('#compiler').Css.SelectorList} * @returns {import('#compiler').Css.SelectorList}
*/ */
function read_selector_list(parser) { function read_selector_list(parser, nested) {
/** @type {import('#compiler').Css.Selector[]} */ /** @type {import('#compiler').Css.Selector[]} */
const children = []; const children = [];
const start = parser.index; const start = parser.index;
while (parser.index < parser.template.length) { while (parser.index < parser.template.length) {
children.push(read_selector(parser)); children.push(read_selector(parser, nested));
const end = parser.index; const end = parser.index;
@ -178,28 +180,29 @@ function read_selector_list(parser) {
/** /**
* @param {import('../index.js').Parser} parser * @param {import('../index.js').Parser} parser
* @param {boolean} nested Whether this selector is nested inside another rule
* @returns {import('#compiler').Css.Selector} * @returns {import('#compiler').Css.Selector}
*/ */
function read_selector(parser) { function read_selector(parser, nested) {
const list_start = parser.index; const list_start = parser.index;
/** @type {Array<import('#compiler').Css.SimpleSelector | import('#compiler').Css.Combinator>} */ /** @type {Array<import('#compiler').Css.SimpleSelector | import('#compiler').Css.Combinator | import('#compiler').Css.NestingSelector>} */
const children = []; const children = [];
while (parser.index < parser.template.length) { while (parser.index < parser.template.length) {
const start = parser.index; const start = parser.index;
if (parser.eat('*')) { if (nested && parser.eat('&')){
children.push({ children.push({
type: 'TypeSelector', type: 'NestedSelector',
name: '*', name: '&',
start, start,
end: parser.index end: parser.index
}); });
} else if (parser.eat('&')){ } else if (parser.eat('*')) {
children.push({ children.push({
type: 'NestedSelector', type: 'TypeSelector',
name: '&', name: '*',
start, start,
end: parser.index end: parser.index
}); });
@ -396,7 +399,7 @@ function read_declaration_or_rule(parser) {
// due to complexities with https://bugs.chromium.org/p/chromium/issues/detail?id=1427259 // due to complexities with https://bugs.chromium.org/p/chromium/issues/detail?id=1427259
// as most browsers as of 17/11/2023 do not support nesting without & selector // as most browsers as of 17/11/2023 do not support nesting without & selector
if (parser.match('&')) { if (parser.match('&')) {
return read_rule(parser) return read_rule(parser, true)
} else { } else {
return read_declaration(parser); return read_declaration(parser);
} }

@ -100,15 +100,12 @@ export default class Selector {
while (i--) { while (i--) {
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 (!block.root && !block.host && !block.nested) {
if (i === 0) code.prependRight(selector.start, attr); if (i === 0) code.prependRight(selector.start, attr);
} }
continue; continue;
} }
if (selector.type === "NestedSelector") { if (selector.type === 'TypeSelector' && selector.name === '*') {
// do we want to add the attr to the nested selector?
// it's kind of implied that it's a child of the parent selector
} else 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);
@ -123,7 +120,7 @@ export default class Selector {
if (block.should_encapsulate) { if (block.should_encapsulate) {
encapsulate_block( encapsulate_block(
block, block,
index === this.blocks.filter(block => block.nested !== true).length - 1 index === this.blocks.length - 1 + (block.nested ? 1 : 0)
? attr.repeat(amount_class_specificity_to_increase + 1) ? attr.repeat(amount_class_specificity_to_increase + 1)
: attr : attr
); );
@ -838,12 +835,18 @@ function group_selectors(selector) {
selector.children.forEach((child) => { selector.children.forEach((child) => {
if (child.type === 'Combinator') { if (child.type === 'Combinator') {
if(block.nested && !block.combinator) {
block.combinator = child;
} else {
block = new Block(child); block = new Block(child);
blocks.push(block); blocks.push(block);
}
} else if (child.type === "NestedSelector") {
block.nested = true;
} else { } else {
if (child.type === "NestedSelector") block.nested = true;
block.add(child); block.add(child);
} }
}); });
return blocks; return blocks;
} }

@ -25,7 +25,7 @@ export interface SelectorList extends BaseNode {
export interface Selector extends BaseNode { export interface Selector extends BaseNode {
type: 'Selector'; type: 'Selector';
children: Array<SimpleSelector | Combinator>; children: Array<SimpleSelector | Combinator | NestingSelector>;
} }
export interface TypeSelector extends BaseNode { export interface TypeSelector extends BaseNode {
@ -67,11 +67,6 @@ export interface Percentage extends BaseNode {
value: string; value: string;
} }
export interface NestedSelector extends BaseNode {
type: 'NestedSelector';
name: "&";
}
export type SimpleSelector = export type SimpleSelector =
| TypeSelector | TypeSelector
| IdSelector | IdSelector
@ -79,7 +74,6 @@ export type SimpleSelector =
| AttributeSelector | AttributeSelector
| PseudoElementSelector | PseudoElementSelector
| PseudoClassSelector | PseudoClassSelector
| NestedSelector
| Percentage; | Percentage;
export interface Combinator extends BaseNode { export interface Combinator extends BaseNode {
@ -87,6 +81,11 @@ export interface Combinator extends BaseNode {
name: string; name: string;
} }
export interface NestingSelector extends BaseNode {
type: 'NestedSelector';
name: "&";
}
export interface Block extends BaseNode { export interface Block extends BaseNode {
type: 'Block'; type: 'Block';
children: Array<Declaration | Rule | Atrule>; children: Array<Declaration | Rule | Atrule>;

Loading…
Cancel
Save