only apply svelte-123xyz attributes where necessary (WIP)

pull/689/head
Rich Harris 8 years ago
parent 0cbd201200
commit fae9036ce6

@ -13,6 +13,7 @@ import annotateWithScopes from '../utils/annotateWithScopes';
import clone from '../utils/clone'; import clone from '../utils/clone';
import DomBlock from './dom/Block'; import DomBlock from './dom/Block';
import SsrBlock from './server-side-rendering/Block'; import SsrBlock from './server-side-rendering/Block';
import extractSelectors from './extractSelectors';
import { Node, Parsed, CompileOptions } from '../interfaces'; import { Node, Parsed, CompileOptions } from '../interfaces';
const test = typeof global !== 'undefined' && global.__svelte_test; const test = typeof global !== 'undefined' && global.__svelte_test;
@ -40,6 +41,8 @@ export default class Generator {
cssId: string; cssId: string;
usesRefs: boolean; usesRefs: boolean;
selectors: any[]; // TODO how to indicate it takes `(Node[]) => boolean` functions?
importedNames: Set<string>; importedNames: Set<string>;
aliases: Map<string, string>; aliases: Map<string, string>;
usedNames: Set<string>; usedNames: Set<string>;
@ -76,6 +79,8 @@ export default class Generator {
this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';
this.usesRefs = false; this.usesRefs = false;
this.selectors = extractSelectors(parsed.css);
// allow compiler to deconflict user's `import { get } from 'whatever'` and // allow compiler to deconflict user's `import { get } from 'whatever'` and
// Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`; // Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`;
this.importedNames = new Set(); this.importedNames = new Set();
@ -211,6 +216,16 @@ export default class Generator {
}; };
} }
cssAppliesTo(node: Node, stack: Node[]) {
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;
}
}
}
findDependencies( findDependencies(
contextDependencies: Map<string, string[]>, contextDependencies: Map<string, string[]>,
indexes: Map<string, string>, indexes: Map<string, string>,

@ -73,7 +73,7 @@ export default function dom(
const { block, state } = preprocess(generator, namespace, parsed.html); const { block, state } = preprocess(generator, namespace, parsed.html);
parsed.html.children.forEach((node: Node) => { parsed.html.children.forEach((node: Node) => {
visit(generator, block, state, node); visit(generator, block, state, node, []);
}); });
const builders = { const builders = {

@ -2,13 +2,15 @@ import visitors from './visitors/index';
import { DomGenerator } from './index'; import { DomGenerator } from './index';
import Block from './Block'; import Block from './Block';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
import { State } from './interfaces';
export default function visit( export default function visit(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
const visitor = visitors[node.type]; const visitor = visitors[node.type];
visitor(generator, block, state, node); visitor(generator, block, state, node, elementStack);
} }

@ -40,7 +40,8 @@ export default function visitComponent(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
const hasChildren = node.children.length > 0; const hasChildren = node.children.length > 0;
const name = block.getUniqueName( const name = block.getUniqueName(
@ -123,7 +124,7 @@ export default function visitComponent(
const childBlock = node._block; const childBlock = node._block;
node.children.forEach((child: Node) => { node.children.forEach((child: Node) => {
visit(generator, childBlock, childState, child); visit(generator, childBlock, childState, child, elementStack);
}); });
const yieldFragment = block.getUniqueName(`${name}_yield_fragment`); const yieldFragment = block.getUniqueName(`${name}_yield_fragment`);

@ -9,7 +9,8 @@ export default function visitEachBlock(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
const each_block = generator.getUniqueName(`each_block`); const each_block = generator.getUniqueName(`each_block`);
const create_each_block = node._block.name; const create_each_block = node._block.name;
@ -118,12 +119,12 @@ export default function visitEachBlock(
} }
node.children.forEach((child: Node) => { node.children.forEach((child: Node) => {
visit(generator, node._block, node._state, child); visit(generator, node._block, node._state, child, elementStack);
}); });
if (node.else) { if (node.else) {
node.else.children.forEach((child: Node) => { node.else.children.forEach((child: Node) => {
visit(generator, node.else._block, node.else._state, child); visit(generator, node.else._block, node.else._state, child, elementStack);
}); });
} }
} }

@ -35,14 +35,15 @@ export default function visitElement(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
if (node.name in meta) { if (node.name in meta) {
return meta[node.name](generator, block, node); return meta[node.name](generator, block, node);
} }
if (generator.components.has(node.name) || node.name === ':Self') { if (generator.components.has(node.name) || node.name === ':Self') {
return visitComponent(generator, block, state, node); return visitComponent(generator, block, state, node, elementStack);
} }
const childState = node._state; const childState = node._state;
@ -67,7 +68,7 @@ export default function visitElement(
} }
// add CSS encapsulation attribute // add CSS encapsulation attribute
if (generator.cssId && (!generator.cascade || state.isTopLevel)) { if (generator.cssId && (generator.cascade ? state.isTopLevel : generator.cssAppliesTo(node, elementStack))) {
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`${generator.helper( `${generator.helper(
'setAttribute' 'setAttribute'
@ -172,7 +173,7 @@ export default function visitElement(
} }
node.children.forEach((child: Node) => { node.children.forEach((child: Node) => {
visit(generator, block, childState, child); visit(generator, block, childState, child, elementStack.concat(node));
}); });
if (node.lateUpdate) { if (node.lateUpdate) {

@ -19,7 +19,8 @@ function getBranches(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
const branches = [ const branches = [
{ {
@ -31,11 +32,11 @@ function getBranches(
}, },
]; ];
visitChildren(generator, block, state, node); visitChildren(generator, block, state, node, elementStack);
if (isElseIf(node.else)) { if (isElseIf(node.else)) {
branches.push( branches.push(
...getBranches(generator, block, state, node.else.children[0]) ...getBranches(generator, block, state, node.else.children[0], elementStack)
); );
} else { } else {
branches.push({ branches.push({
@ -47,7 +48,7 @@ function getBranches(
}); });
if (node.else) { if (node.else) {
visitChildren(generator, block, state, node.else); visitChildren(generator, block, state, node.else, elementStack);
} }
} }
@ -58,10 +59,11 @@ function visitChildren(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
node.children.forEach((child: Node) => { node.children.forEach((child: Node) => {
visit(generator, node._block, node._state, child); visit(generator, node._block, node._state, child, elementStack);
}); });
} }
@ -69,7 +71,8 @@ export default function visitIfBlock(
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
elementStack: Node[]
) { ) {
const name = generator.getUniqueName(`if_block`); const name = generator.getUniqueName(`if_block`);
const anchor = node.needsAnchor const anchor = node.needsAnchor
@ -77,7 +80,7 @@ export default function visitIfBlock(
: (node.next && node.next._state.name) || 'null'; : (node.next && node.next._state.name) || 'null';
const params = block.params.join(', '); const params = block.params.join(', ');
const branches = getBranches(generator, block, state, node); const branches = getBranches(generator, block, state, node, elementStack);
const hasElse = isElseBranch(branches[branches.length - 1]); const hasElse = isElseBranch(branches[branches.length - 1]);
const if_name = hasElse ? '' : `if ( ${name} ) `; const if_name = hasElse ? '' : `if ( ${name} ) `;

@ -0,0 +1,64 @@
import { Node } from '../interfaces';
export default function extractSelectors(css: Node) :Node[] {
if (!css) return [];
const selectors = [];
function processRule(rule: Node) {
if (rule.type !== 'Rule') {
// TODO @media etc
throw new Error(`not supported: ${rule.type}`);
}
const selectors = rule.selector.children;
selectors.forEach(processSelector);
}
function processSelector(selector: Node) {
selectors.push({
used: false,
appliesTo: (node: Node, stack: Node[]) => {
let i = selector.children.length;
let j = stack.length;
while (i--) {
if (!node) return false;
const part = selector.children[i];
if (part.type === 'ClassSelector') {
if (!classMatches(node, part.name)) return false;
}
else if (part.type === 'TypeSelector') {
if (node.name !== part.name) return false;
}
else if (part.type === 'WhiteSpace') {
node = stack[--j];
}
else {
throw new Error(`TODO ${part.type}`);
}
}
return true;
}
});
}
css.children.forEach(processRule);
return selectors;
}
function classMatches(node: Node, className: string) {
const attr = node.attributes.find((attr: Node) => attr.name === 'class');
if (!attr) return false;
if (attr.value.length > 1) return true;
if (attr.value[0].type !== 'Text') return true;
return attr.value[0].data.split(' ').indexOf(className) !== -1;
}

@ -0,0 +1,4 @@
.foo[svelte-2643270928] {
color: red;
}

@ -0,0 +1,2 @@
<p svelte-2643270928="" class="whatever">this is styled</p>
<p class="bar">this is unstyled</p>

@ -0,0 +1,18 @@
<p class='{{unknown}}'>this is styled</p>
<p class='bar'>this is unstyled</p>
<style>
.foo {
color: red;
}
</style>
<script>
export default {
data () {
return {
unknown: 'whatever'
};
}
};
</script>

@ -0,0 +1,4 @@
.foo[svelte-633959357] {
color: red;
}

@ -0,0 +1,2 @@
<p svelte-633959357="" class="foo">this is styled</p>
<p class="bar">this is unstyled</p>

@ -0,0 +1,8 @@
<p class='foo'>this is styled</p>
<p class='bar'>this is unstyled</p>
<style>
.foo {
color: red;
}
</style>

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

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

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

@ -0,0 +1,3 @@
export default {
cascade: false
};

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

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

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