You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/src/generators/shared/processCss.ts

144 lines
3.8 KiB

import MagicString from 'magic-string';
import { Parsed, Node } from '../../interfaces';
const commentsPattern = /\/\*[\s\S]*?\*\//g;
export default function processCss(
parsed: Parsed,
code: MagicString,
cascade: boolean
) {
const css = parsed.css.content.styles;
const offset = parsed.css.content.start;
const attr = `[svelte-${parsed.hash}]`;
const keyframes = new Map();
function walkKeyframes(node: Node) {
if (node.type === 'Atrule' && node.name.toLowerCase() === 'keyframes') {
node.expression.children.forEach((expression: Node) => {
if (expression.type === 'Identifier') {
if (expression.name.startsWith('-global-')) {
code.remove(expression.start, expression.start + 8);
} else {
const newName = `svelte-${parsed.hash}-${expression.name}`;
code.overwrite(expression.start, expression.end, newName);
keyframes.set(expression.name, newName);
}
}
});
} else if (node.children) {
node.children.forEach(walkKeyframes);
} else if (node.block) {
walkKeyframes(node.block);
}
}
parsed.css.children.forEach(walkKeyframes);
function transform(rule: Node) {
rule.selector.children.forEach((selector: Node) => {
if (cascade) {
// TODO disable cascading (without :global(...)) in v2
const start = selector.start - offset;
const end = selector.end - offset;
const selectorString = css.slice(start, end);
const firstToken = selector.children[0];
let transformed;
if (firstToken.type === 'TypeSelector') {
const insert = firstToken.end - offset;
const head = css.slice(start, insert);
const tail = css.slice(insert, end);
transformed = `${head}${attr}${tail}, ${attr} ${selectorString}`;
} else {
transformed = `${attr}${selectorString}, ${attr} ${selectorString}`;
}
code.overwrite(selector.start, selector.end, transformed);
} else {
let shouldTransform = true;
let c = selector.start;
selector.children.forEach((child: Node) => {
if (child.type === 'WhiteSpace' || child.type === 'Combinator') {
code.appendLeft(c, attr);
shouldTransform = true;
return;
}
if (!shouldTransform) return;
if (child.type === 'PseudoClassSelector') {
// `:global(xyz)` > xyz
if (child.name === 'global') {
const first = child.children[0];
const last = child.children[child.children.length - 1];
code.remove(child.start, first.start).remove(last.end, child.end);
} else {
code.prependRight(c, attr);
}
shouldTransform = false;
}
c = child.end;
});
if (shouldTransform) {
code.appendLeft(c, attr);
}
}
});
rule.block.children.forEach((block: Node) => {
if (block.type === 'Declaration') {
const property = block.property.toLowerCase();
if (property === 'animation' || property === 'animation-name') {
block.value.children.forEach((block: Node) => {
if (block.type === 'Identifier') {
const name = block.name;
if (keyframes.has(name)) {
code.overwrite(block.start, block.end, keyframes.get(name));
}
}
});
}
}
});
}
function walk(node: Node) {
if (node.type === 'Rule') {
transform(node);
} else if (
node.type === 'Atrule' &&
node.name.toLowerCase() === 'keyframes'
) {
// these have already been processed
} else if (node.children) {
node.children.forEach(walk);
} else if (node.block) {
walk(node.block);
}
}
parsed.css.children.forEach(walk);
// remove comments. TODO would be nice if this was exposed in css-tree
let match;
while ((match = commentsPattern.exec(css))) {
const start = match.index + offset;
const end = start + match[0].length;
code.remove(start, end);
}
return code.slice(parsed.css.content.start, parsed.css.content.end);
}