fix handling of modified :global(...) selectors

pull/680/head
Rich Harris 7 years ago
parent 0c33eb4b1b
commit 7a752df55d

@ -81,7 +81,6 @@ export default class Generator {
// styles
this.cascade = options.cascade !== false; // TODO remove this option in v2
this.css = parsed.css ? processCss(parsed, this.code, this.cascade) : null;
this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';
this.selectors = [];
@ -91,6 +90,10 @@ export default class Generator {
this.selectors.push(new Selector(child));
});
});
this.css = processCss(this, this.code, this.cascade);
} else {
this.css = null;
}
// allow compiler to deconflict user's `import { get } from 'whatever'` and

@ -1,9 +1,10 @@
import MagicString from 'magic-string';
import { groupSelectors, isGlobalSelector, walkRules } from '../utils/css';
import { Node } from '../interfaces';
export default class Selector {
node: Node;
blocks: Node[][];
blocks: any; // TODO
parts: Node[];
used: boolean;
@ -27,7 +28,7 @@ export default class Selector {
this.parts = node.children.slice(0, i);
this.used = isGlobalSelector(this.blocks[0]);
this.used = this.blocks[0].global;
}
apply(node: Node, stack: Node[]) {
@ -42,6 +43,35 @@ export default class Selector {
if (stack[0] && this.node.children.find(isDescendantSelector)) stack[0]._needsCssAttribute = true;
}
}
transform(code: MagicString, attr: string) {
function encapsulateBlock(block) {
let i = block.selectors.length;
while (i--) {
const selector = block.selectors[i];
if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') continue;
if (selector.type === 'TypeSelector' && selector.name === '*') {
code.overwrite(selector.start, selector.end, attr);
} else {
code.appendLeft(selector.end, attr);
}
return;
}
}
this.blocks.forEach((block, i) => {
if (block.global) {
const selector = block.selectors[0];
const first = selector.children[0];
const last = selector.children[selector.children.length - 1];
code.remove(selector.start, first.start).remove(last.end, selector.end);
} else if (i === 0 || i === this.blocks.length - 1) {
encapsulateBlock(block);
}
});
}
}
function isDescendantSelector(selector: Node) {

@ -1,18 +1,19 @@
import MagicString from 'magic-string';
import { groupSelectors, isGlobalSelector, walkRules } from '../../utils/css';
import { Parsed, Node } from '../../interfaces';
import Generator from '../Generator';
import { Node } from '../../interfaces';
const commentsPattern = /\/\*[\s\S]*?\*\//g;
export default function processCss(
parsed: Parsed,
generator: Generator,
code: MagicString,
cascade: boolean
) {
const css = parsed.css.content.styles;
const offset = parsed.css.content.start;
const css = generator.parsed.css.content.styles;
const offset = generator.parsed.css.content.start;
const attr = `[svelte-${parsed.hash}]`;
const attr = `[svelte-${generator.parsed.hash}]`;
const keyframes = new Map();
@ -23,7 +24,7 @@ export default function processCss(
if (expression.name.startsWith('-global-')) {
code.remove(expression.start, expression.start + 8);
} else {
const newName = `svelte-${parsed.hash}-${expression.name}`;
const newName = `svelte-${generator.parsed.hash}-${expression.name}`;
code.overwrite(expression.start, expression.end, newName);
keyframes.set(expression.name, newName);
}
@ -36,23 +37,7 @@ export default function processCss(
}
}
parsed.css.children.forEach(walkKeyframes);
function encapsulateBlock(block: Node[]) {
let i = block.length;
while (i--) {
const child = block[i];
if (child.type === 'PseudoElementSelector' || child.type === 'PseudoClassSelector') continue;
if (child.type === 'TypeSelector' && child.name === '*') {
code.overwrite(child.start, child.end, attr);
} else {
code.appendLeft(child.end, attr);
}
return;
}
}
generator.parsed.css.children.forEach(walkKeyframes);
function transform(rule: Node) {
rule.selector.children.forEach((selector: Node) => {
@ -78,27 +63,6 @@ export default function processCss(
}
code.overwrite(selector.start, selector.end, transformed);
} else {
let shouldTransform = true;
let c = selector.start;
// separate .foo > .bar > .baz into three separate blocks, so
// that we can transform only the first and last
let block: Node[] = [];
const blocks: Node[][] = groupSelectors(selector);
blocks.forEach((block: Node[], i) => {
if (i === 0 || i === blocks.length - 1) {
encapsulateBlock(blocks[i]);
}
if (isGlobalSelector(block)) {
const selector = block[0];
const first = selector.children[0];
const last = selector.children[selector.children.length - 1];
code.remove(selector.start, first.start).remove(last.end, selector.end);
}
});
}
});
@ -119,7 +83,13 @@ export default function processCss(
});
}
walkRules(parsed.css.children, transform);
walkRules(generator.parsed.css.children, transform);
if (!cascade) {
generator.selectors.forEach(selector => {
selector.transform(code, attr);
});
}
// remove comments. TODO would be nice if this was exposed in css-tree
let match;
@ -130,5 +100,5 @@ export default function processCss(
code.remove(start, end);
}
return code.slice(parsed.css.content.start, parsed.css.content.end);
return code.slice(generator.parsed.css.content.start, generator.parsed.css.content.end);
}

@ -5,15 +5,27 @@ export function isGlobalSelector(block: Node[]) {
}
export function groupSelectors(selector: Node) {
let block: Node[] = [];
const blocks: Node[][] = [block];
let block = {
global: selector.children[0].type === 'PseudoClassSelector' && selector.children[0].name === 'global',
selectors: [],
combinator: null
};
selector.children.forEach((child: Node) => {
const blocks = [block];
selector.children.forEach((child: Node, i: number) => {
if (child.type === 'WhiteSpace' || child.type === 'Combinator') {
block = [];
const next = selector.children[i + 1];
block = {
global: next.type === 'PseudoClassSelector' && next.name === 'global',
selectors: [],
combinator: child
};
blocks.push(block);
} else {
block.push(child);
block.selectors.push(child);
}
});

@ -8,12 +8,12 @@ export default function validateCss(validator: Validator, css: Node) {
});
function validateSelector(selector: Node) {
const blocks: Node[][] = groupSelectors(selector);
const blocks = groupSelectors(selector);
blocks.forEach((block) => {
let i = block.length;
let i = block.selectors.length;
while (i-- > 1) {
const part = block[i];
const part = block.selectors[i];
if (part.type === 'PseudoClassSelector' && part.name === 'global') {
validator.error(`:global(...) must be the first element in a compound selector`, part.start);
}
@ -24,16 +24,16 @@ export default function validateCss(validator: Validator, css: Node) {
let end = blocks.length;
for (; start < end; start += 1) {
if (!isGlobalSelector(blocks[start])) break;
if (!blocks[start].global) break;
}
for (; end > start; end -= 1) {
if (!isGlobalSelector(blocks[end - 1])) break;
if (!blocks[end - 1].global) break;
}
for (let i = start; i < end; i += 1) {
if (isGlobalSelector(blocks[i])) {
validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, blocks[i][0].start);
if (blocks[i].global) {
validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, blocks[i].selectors[0].start);
}
}
}

@ -6,7 +6,7 @@
color: red;
}
:global(div.foo) {
:global(div).foo {
color: blue;
}

Loading…
Cancel
Save