Skip to content

Commit

Permalink
fix handling of modified :global(...) selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed Jul 5, 2017
1 parent 0c33eb4 commit 7a752df
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 62 deletions.
5 changes: 4 additions & 1 deletion src/generators/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];

Expand All @@ -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
Expand Down
34 changes: 32 additions & 2 deletions src/generators/Selector.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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[]) {
Expand All @@ -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) {
Expand Down
62 changes: 16 additions & 46 deletions src/generators/shared/processCss.ts
Original file line number Diff line number Diff line change
@@ -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();

Expand All @@ -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);
}
Expand All @@ -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) => {
Expand All @@ -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);
}
});
}
});

Expand All @@ -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;
Expand All @@ -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);
}
22 changes: 17 additions & 5 deletions src/utils/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});

Expand Down
14 changes: 7 additions & 7 deletions src/validate/css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/css/samples/cascade-false-global/input.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
color: red;
}

:global(div.foo) {
:global(div).foo {
color: blue;
}

Expand Down

0 comments on commit 7a752df

Please sign in to comment.