move CSS analysis into preprocess

pull/689/head
Rich Harris 8 years ago
parent 72f39fd7f9
commit ab40007406

@ -216,13 +216,17 @@ export default class Generator {
};
}
cssAppliesTo(node: Node, stack: Node[]) {
applyCss(node: Node, stack: Node[]) {
if (!this.cssId) return;
if (this.cascade) {
if (stack.length === 0) node._needsCssAttribute = true;
return;
}
for (let i = 0; i < this.selectors.length; i += 1) {
const selector = this.selectors[i];
if (selector.appliesTo(node, stack)) {
selector.used = true;
return true;
}
selector.apply(node, stack);
}
}

@ -40,6 +40,7 @@ const preprocessors = {
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean
) => {
const dependencies = block.findDependencies(node.expression);
@ -55,6 +56,7 @@ const preprocessors = {
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean
) => {
const dependencies = block.findDependencies(node.expression);
@ -66,7 +68,14 @@ const preprocessors = {
node._state = getChildState(state, { basename, name });
},
Text: (generator: DomGenerator, block: Block, state: State, node: Node, stripWhitespace: boolean) => {
Text: (
generator: DomGenerator,
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean
) => {
node._state = getChildState(state);
if (!/\S/.test(node.data)) {
@ -83,6 +92,7 @@ const preprocessors = {
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) => {
@ -102,7 +112,7 @@ const preprocessors = {
node._state = getChildState(state);
blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node, stripWhitespace, node);
preprocessChildren(generator, node._block, node._state, node, elementStack, stripWhitespace, nextSibling);
if (node._block.dependencies.size > 0) {
dynamic = true;
@ -127,6 +137,7 @@ const preprocessors = {
node.else._block,
node.else._state,
node.else,
elementStack,
stripWhitespace,
nextSibling
);
@ -154,6 +165,7 @@ const preprocessors = {
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) => {
@ -202,7 +214,7 @@ const preprocessors = {
});
generator.blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node, stripWhitespace, nextSibling);
preprocessChildren(generator, node._block, node._state, node, elementStack, stripWhitespace, nextSibling);
block.addDependencies(node._block.dependencies);
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
@ -219,6 +231,7 @@ const preprocessors = {
node.else._block,
node.else._state,
node.else,
elementStack,
stripWhitespace,
nextSibling
);
@ -231,6 +244,7 @@ const preprocessors = {
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) => {
@ -315,6 +329,8 @@ const preprocessors = {
: state.namespace,
allUsedContexts: [],
});
generator.applyCss(node, elementStack);
}
if (node.children.length) {
@ -328,12 +344,12 @@ const preprocessors = {
});
generator.blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node, stripWhitespace, nextSibling);
preprocessChildren(generator, node._block, node._state, node, elementStack, stripWhitespace, nextSibling);
block.addDependencies(node._block.dependencies);
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
} else {
if (node.name === 'pre' || node.name === 'textarea') stripWhitespace = false;
preprocessChildren(generator, block, node._state, node, stripWhitespace, nextSibling);
preprocessChildren(generator, block, node._state, node, elementStack.concat(node), stripWhitespace, nextSibling);
}
}
},
@ -344,6 +360,7 @@ function preprocessChildren(
block: Block,
state: State,
node: Node,
elementStack: Node[],
stripWhitespace: boolean,
nextSibling: Node
) {
@ -373,7 +390,7 @@ function preprocessChildren(
cleaned.forEach((child: Node, i: number) => {
const preprocessor = preprocessors[child.type];
if (preprocessor) preprocessor(generator, block, state, child, stripWhitespace, cleaned[i + 1] || nextSibling);
if (preprocessor) preprocessor(generator, block, state, child, elementStack, stripWhitespace, cleaned[i + 1] || nextSibling);
if (lastChild) {
lastChild.next = child;
@ -432,7 +449,7 @@ export default function preprocess(
};
generator.blocks.push(block);
preprocessChildren(generator, block, state, node, true, null);
preprocessChildren(generator, block, state, node, [], true, null);
block.hasUpdateMethod = block.dependencies.size > 0;
return { block, state };

@ -81,7 +81,8 @@ export default function visitElement(
}
// add CSS encapsulation attribute
if (generator.cssId && (generator.cascade ? state.isTopLevel : generator.cssAppliesTo(node, elementStack))) {
// TODO add a helper for this, rather than repeating it
if (node._needsCssAttribute) {
block.builders.hydrate.addLine(
`@setAttribute( ${name}, '${generator.cssId}', '' );`
);

@ -6,6 +6,15 @@ export default function extractSelectors(css: Node) :Node[] {
const selectors = [];
function processRule(rule: Node) {
if (rule.type === 'Atrule') {
if (rule.name === 'keyframes') return;
if (rule.name == 'media') {
rule.block.children.forEach(processRule);
return;
}
console.log(rule);
throw new Error('nope');
}
if (rule.type !== 'Rule') {
// TODO @media etc
throw new Error(`not supported: ${rule.type}`);
@ -18,14 +27,37 @@ export default function extractSelectors(css: Node) :Node[] {
function processSelector(selector: Node) {
selectors.push({
used: false,
appliesTo: (node: Node, stack: Node[]) => {
let i = selector.children.length;
apply: (node: Node, stack: Node[]) => {
const applies = selectorAppliesTo(selector.children, node, stack.slice());
if (applies) {
node._needsCssAttribute = true;
if (selector.children.find(isDescendantSelector)) stack[0]._needsCssAttribute = true;
}
}
});
}
css.children.forEach(processRule);
return selectors;
}
function isDescendantSelector(selector: Node) {
return selector.type === 'WhiteSpace'; // TODO or '>'
}
function selectorAppliesTo(parts: Node[], node: Node, stack: Node[]) {
let i = parts.length;
let j = stack.length;
while (i--) {
if (!node) return false;
if (!node) return;
const part = parts[i];
const part = selector.children[i];
if (part.type === 'PseudoClassSelector' || part.type === 'PseudoElementSelector') {
continue;
}
if (part.type === 'ClassSelector') {
if (!classMatches(node, part.name)) return false;
@ -36,22 +68,32 @@ export default function extractSelectors(css: Node) :Node[] {
}
else if (part.type === 'WhiteSpace') {
node = stack[--j];
parts = parts.slice(0, i);
while (stack.length) {
if (selectorAppliesTo(parts, stack.pop(), stack)) {
return true;
}
}
else {
throw new Error(`TODO ${part.type}`);
return false;
}
else if (part.type === 'Combinator') {
if (part.name === '>') {
return selectorAppliesTo(parts.slice(0, i), stack.pop(), stack);
}
console.log(part);
return true;
}
});
}
css.children.forEach(processRule);
else {
throw new Error(`TODO ${part.type}`);
}
}
return selectors;
return true;
}
function classMatches(node: Node, className: string) {

@ -1,4 +0,0 @@
div[svelte-1941127328] p[svelte-1941127328] {
color: red;
}

@ -1 +0,0 @@
<div svelte-1941127328=""><p svelte-1941127328="">this is styled</p></div>

@ -1,9 +0,0 @@
<div>
<p>this is styled</p>
</div>
<style>
div p {
color: red;
}
</style>

@ -0,0 +1,4 @@
div[svelte-2557028325] > p[svelte-2557028325] {
color: red;
}

@ -0,0 +1 @@
<div><section><p>this is not styled</p></section></div>

@ -0,0 +1,11 @@
<div>
<section>
<p>this is not styled</p>
</section>
</div>
<style>
div > p {
color: red;
}
</style>

@ -0,0 +1,4 @@
div[svelte-4240344240] p[svelte-4240344240] {
color: red;
}

@ -0,0 +1 @@
<div svelte-4240344240=""><section><p svelte-4240344240="">this is styled</p></section></div>

@ -0,0 +1,11 @@
<div>
<section>
<p>this is styled</p>
</section>
</div>
<style>
div p {
color: red;
}
</style>
Loading…
Cancel
Save