start refactoring contextualise/findDependencies

pull/924/head
Rich Harris 8 years ago
parent ffb0f23d22
commit 30345ea0db

@ -51,7 +51,7 @@
"eslint": "^4.3.0",
"eslint-plugin-html": "^3.0.0",
"eslint-plugin-import": "^2.2.0",
"estree-walker": "^0.5.0",
"estree-walker": "^0.5.1",
"glob": "^7.1.1",
"jsdom": "^11.1.0",
"locate-character": "^2.0.0",

@ -10,7 +10,7 @@ import reservedNames from '../utils/reservedNames';
import namespaces from '../utils/namespaces';
import { removeNode, removeObjectKey } from '../utils/removeNode';
import wrapModule from './shared/utils/wrapModule';
import annotateWithScopes from '../utils/annotateWithScopes';
import annotateWithScopes, { Scope } from '../utils/annotateWithScopes';
import getName from '../utils/getName';
import clone from '../utils/clone';
import DomBlock from './dom/Block';
@ -155,7 +155,12 @@ export default class Generator {
this.aliases = new Map();
this.usedNames = new Set();
this.parseJs(dom);
this.computations = [];
this.templateProperties = {};
this.walkJs(dom);
this.walkTemplate();
this.name = this.alias(name);
if (options.customElement === true) {
@ -208,7 +213,7 @@ export default class Generator {
const { code, helpers } = this;
const { contexts, indexes } = block;
let scope = annotateWithScopes(expression); // TODO this already happens in findDependencies
let scope: Scope;
let lexicalDepth = 0;
const self = this;
@ -230,7 +235,7 @@ export default class Generator {
});
} else if (isReference(node, parent)) {
const { name } = flattenReference(node);
if (scope.has(name)) return;
if (scope && scope.has(name)) return;
if (name === 'event' && isEventHandler) {
// noop
@ -307,10 +312,10 @@ export default class Generator {
) {
if (expression._dependencies) return expression._dependencies;
let scope = annotateWithScopes(expression);
let scope: Scope;
const dependencies: string[] = [];
const generator = this; // can't use arrow functions, because of this.skip()
const { helpers } = this; // can't use arrow functions, because of this.skip()
walk(expression, {
enter(node: Node, parent: Node) {
@ -321,7 +326,7 @@ export default class Generator {
if (isReference(node, parent)) {
const { name } = flattenReference(node);
if (scope.has(name) || generator.helpers.has(name)) return;
if (scope && scope.has(name) || helpers.has(name)) return;
if (contextDependencies.has(name)) {
dependencies.push(...contextDependencies.get(name));
@ -442,17 +447,19 @@ export default class Generator {
};
}
parseJs(dom: boolean) {
const { code, source } = this;
walkJs(dom: boolean) {
const {
code,
source,
computations,
templateProperties,
imports
} = this;
const { js } = this.parsed;
const imports = this.imports;
const computations: Computation[] = [];
const templateProperties: Record<string, Node> = {};
const componentDefinition = new CodeBuilder();
let namespace = null;
if (js) {
this.addSourcemapLocations(js.content);
@ -625,7 +632,7 @@ export default class Generator {
if (templateProperties.namespace) {
const ns = templateProperties.namespace.value.value;
namespace = namespaces[ns] || ns;
this.namespace = namespaces[ns] || ns;
}
if (templateProperties.onrender) templateProperties.oncreate = templateProperties.onrender; // remove after v2
@ -681,9 +688,122 @@ export default class Generator {
this.javascript = a === b ? null : `[✂${a}-${b}✂]`;
}
}
}
walkTemplate() {
const {
expectedProperties,
helpers
} = this;
const { html } = this.parsed;
function findDependencies(node: Node, contextDependencies: Map<string, string[]>, indexes: Set<string>) {
const dependencies: Set<string> = new Set();
let scope = annotateWithScopes(html);
walk(node, {
enter(node: Node, parent: Node) {
if (node._scope) {
scope = node._scope;
return;
}
if (isReference(node, parent)) {
const { name } = flattenReference(node);
if (scope && scope.has(name) || helpers.has(name)) return;
if (contextDependencies.has(name)) {
contextDependencies.get(name).forEach(dependency => {
dependencies.add(dependency);
});
} else if (!indexes.has(name)) {
dependencies.add(name);
}
this.skip();
}
},
leave(node: Node, parent: Node) {
if (node._scope) scope = scope.parent;
}
});
dependencies.forEach(dependency => {
expectedProperties.add(dependency);
});
return Array.from(dependencies);
}
const contextStack = [];
const indexStack = [];
const dependenciesStack = [];
let contextDependencies = new Map();
const contextDependenciesStack: Map<string, string[]>[] = [contextDependencies];
let indexes = new Set();
const indexesStack: Set<string>[] = [indexes];
this.computations = computations;
this.namespace = namespace;
this.templateProperties = templateProperties;
walk(html, {
enter(node: Node, parent: Node) {
if (node.type === 'EachBlock') {
node.dependencies = findDependencies(node.expression, contextDependencies, indexes);
contextDependencies = new Map(contextDependencies);
contextDependencies.set(node.context, node.dependencies);
// if (node.destructuredContexts) {
// for (let i = 0; i < node.destructuredContexts.length; i += 1) {
// contexts.set(node.destructuredContexts[i], `${context}[${i}]`);
// contextDependencies.set(node.destructuredContexts[i], dependencies);
// }
// }
contextDependenciesStack.push(contextDependencies);
if (node.index) {
indexes = new Set(indexes);
indexes.add(node.index);
indexesStack.push(indexes);
}
}
if (node.type === 'IfBlock') {
node.dependencies = findDependencies(node.expression, contextDependencies, indexes);
}
if (node.type === 'MustacheTag' || node.type === 'RawMustacheTag' || node.type === 'AttributeShorthand') {
node.dependencies = findDependencies(node.expression, contextDependencies, indexes);
this.skip();
}
if (node.type === 'Binding') {
node.dependencies = findDependencies(node.value, contextDependencies, indexes);
this.skip();
}
if (node.type === 'EventHandler' && node.expression) {
node.expression.arguments.forEach((arg: Node) => {
arg.dependencies = findDependencies(arg, contextDependencies, indexes);
});
this.skip();
}
},
leave(node: Node, parent: Node) {
if (node.type === 'EachBlock') {
contextDependenciesStack.pop();
contextDependencies = contextDependenciesStack[contextDependenciesStack.length - 1];
if (node.index) {
indexesStack.pop();
indexes = indexesStack[indexesStack.length - 1];
}
}
}
});
}
}

@ -176,14 +176,6 @@ export default class Block {
);
}
findDependencies(expression: Node) {
return this.generator.findDependencies(
this.contextDependencies,
this.indexes,
expression
);
}
mount(name: string, parentNode: string) {
if (parentNode) {
this.builders.mount.addLine(`@appendNode(${name}, ${parentNode});`);

@ -74,9 +74,7 @@ const preprocessors = {
) => {
cannotUseInnerHTML(node);
node.var = block.getUniqueName('text');
const dependencies = block.findDependencies(node.expression);
block.addDependencies(dependencies);
block.addDependencies(node.dependencies);
},
RawMustacheTag: (
@ -90,9 +88,7 @@ const preprocessors = {
) => {
cannotUseInnerHTML(node);
node.var = block.getUniqueName('raw');
const dependencies = block.findDependencies(node.expression);
block.addDependencies(dependencies);
block.addDependencies(node.dependencies);
},
Text: (
@ -133,8 +129,7 @@ const preprocessors = {
function attachBlocks(node: Node) {
node.var = block.getUniqueName(`if_block`);
const dependencies = block.findDependencies(node.expression);
block.addDependencies(dependencies);
block.addDependencies(node.dependencies);
node._block = block.child({
comment: createDebuggingComment(node, generator),
@ -209,7 +204,7 @@ const preprocessors = {
cannotUseInnerHTML(node);
node.var = block.getUniqueName(`each`);
const dependencies = block.findDependencies(node.expression);
const { dependencies } = node;
block.addDependencies(dependencies);
const indexNames = new Map(block.indexNames);
@ -319,7 +314,7 @@ const preprocessors = {
if (chunk.type !== 'Text') {
if (node.parent) cannotUseInnerHTML(node.parent);
const dependencies = block.findDependencies(chunk.expression);
const dependencies = chunk.dependencies;
block.addDependencies(dependencies);
// special case — <option value='{{foo}}'> — see below
@ -341,12 +336,10 @@ const preprocessors = {
if (attribute.type === 'EventHandler' && attribute.expression) {
attribute.expression.arguments.forEach((arg: Node) => {
const dependencies = block.findDependencies(arg);
block.addDependencies(dependencies);
block.addDependencies(arg.dependencies);
});
} else if (attribute.type === 'Binding') {
const dependencies = block.findDependencies(attribute.value);
block.addDependencies(dependencies);
block.addDependencies(attribute.dependencies);
} else if (attribute.type === 'Transition') {
if (attribute.intro)
generator.hasIntroTransitions = block.hasIntroMethod = true;
@ -384,7 +377,8 @@ const preprocessors = {
if (node.name === 'select') {
if (valueAttribute) {
// TODO does this also apply to e.g. `<input type='checkbox' bind:group='foo'>`?
const dependencies = block.findDependencies(valueAttribute.value);
const dependencies = valueAttribute.dependencies;
console.log({ dependencies });
state.selectBindingDependencies = dependencies;
dependencies.forEach((prop: string) => {
generator.indirectDependencies.set(prop, new Set());

@ -352,7 +352,8 @@ function mungeAttribute(attribute: Node, block: Block): Attribute {
}
// simple dynamic attributes
const { dependencies, snippet } = block.contextualise(value.expression);
const { snippet } = block.contextualise(value.expression);
const dependencies = value.dependencies;
// TODO only update attributes that have changed
return {
@ -373,9 +374,10 @@ function mungeAttribute(attribute: Node, block: Block): Attribute {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
const { dependencies, snippet } = block.contextualise(
const { snippet } = block.contextualise(
chunk.expression
);
const dependencies = chunk.dependencies;
dependencies.forEach(dependency => {
allDependencies.add(dependency);
@ -396,9 +398,10 @@ function mungeAttribute(attribute: Node, block: Block): Attribute {
function mungeBinding(binding: Node, block: Block): Binding {
const { name } = getObject(binding.value);
const { snippet, contexts, dependencies } = block.contextualise(
const { snippet, contexts } = block.contextualise(
binding.value
);
const dependencies = binding.dependencies;
const contextual = block.contexts.has(name);

@ -402,7 +402,7 @@ function unkeyed(
}
`);
const dependencies = block.findDependencies(node.expression);
const dependencies = node.dependencies;
const allDependencies = new Set(node._block.dependencies);
dependencies.forEach((dependency: string) => {
allDependencies.add(dependency);

@ -72,7 +72,9 @@ export default function visitAttribute(
if (attribute.value.length === 1) {
// single {{tag}} — may be a non-string
const { expression } = attribute.value[0];
const { snippet, dependencies, indexes } = block.contextualise(expression);
const { snippet, indexes } = block.contextualise(expression);
const dependencies = attribute.value[0].dependencies;
value = snippet;
dependencies.forEach(d => {
allDependencies.add(d);
@ -94,7 +96,8 @@ export default function visitAttribute(
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
const { snippet, dependencies, indexes } = block.contextualise(chunk.expression);
const { snippet, indexes } = block.contextualise(chunk.expression);
const dependencies = chunk.dependencies;
if (Array.from(indexes).some(index => block.changeableIndexes.get(index))) {
hasChangeableIndex = true;

@ -36,7 +36,8 @@ export default function visitStyleAttribute(
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
const { snippet, dependencies, indexes } = block.contextualise(chunk.expression);
const { snippet, indexes } = block.contextualise(chunk.expression);
const dependencies = chunk.dependencies;
if (Array.from(indexes).some(index => block.changeableIndexes.get(index))) {
hasChangeableIndex = true;

@ -92,9 +92,10 @@ export default function addBindings(
let updateCondition: string;
const { name } = getObject(binding.value);
const { snippet, contexts, dependencies } = block.contextualise(
const { snippet, contexts } = block.contextualise(
binding.value
);
const dependencies = binding.dependencies;
contexts.forEach(context => {
if (!~state.allUsedContexts.indexOf(context))

@ -12,7 +12,8 @@ export default function visitTag(
name: string,
update: (value: string) => string
) {
const { dependencies, indexes, snippet } = block.contextualise(node.expression);
const { indexes, snippet } = block.contextualise(node.expression);
const dependencies = node.dependencies;
const hasChangeableIndex = Array.from(indexes).some(index => block.changeableIndexes.get(index));
@ -30,7 +31,7 @@ export default function visitTag(
if (dependencies.length || hasChangeableIndex) {
const changedCheck = (
(block.hasOutroMethod ? `#outroing || ` : '') +
dependencies.map(dependency => `changed.${dependency}`).join(' || ')
dependencies.map((dependency: string) => `changed.${dependency}`).join(' || ')
);
const updateCachedValue = `${value} !== (${value} = ${snippet})`;

@ -38,7 +38,7 @@ export default function annotateWithScopes(expression: Node) {
return scope;
}
class Scope {
export class Scope {
parent: Scope;
block: boolean;
declarations: Set<string>;

@ -5,7 +5,7 @@ SvelteComponent.data = function() {
};
SvelteComponent.render = function(state, options) {
state = state || {};
state = Object.assign({}, state);
return ``.trim();
};

@ -7,7 +7,7 @@ SvelteComponent.data = function() {
};
SvelteComponent.render = function(state, options) {
state = state || {};
state = Object.assign({}, state);
return ``.trim();
};

@ -954,8 +954,8 @@ estree-walker@^0.3.0:
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.3.1.tgz#e6b1a51cf7292524e7237c312e5fe6660c1ce1aa"
estree-walker@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.0.tgz#aae3b57c42deb8010e349c892462f0e71c5dd1aa"
version "0.5.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.1.tgz#64fc375053abc6f57d73e9bd2f004644ad3c5854"
esutils@^2.0.2:
version "2.0.2"

Loading…
Cancel
Save