prep for component spread

pull/1289/head
Rich-Harris 7 years ago
parent 8e280c5b1f
commit 0c62db5db9

@ -69,7 +69,6 @@ export default class Component extends Node {
const name = this.var;
const componentInitProperties = [`root: #component.root`];
let componentInitialData = null;
if (this.children.length > 0) {
const slots = Array.from(this._slots).map(name => `${quoteIfNecessary(name, generator.legacy)}: @createFragment()`);
@ -83,12 +82,12 @@ export default class Component extends Node {
const allContexts = new Set();
const statements: string[] = [];
const name_initial_data = block.getUniqueName(`${name}_initial_data`);
let name_updating: string;
let name_initial_data: string;
let beforecreate: string = null;
const attributes = this.attributes
.filter(a => a.type === 'Attribute')
.filter(a => a.type === 'Attribute' || a.type === 'Spread')
.map(a => mungeAttribute(a, block));
const bindings = this.attributes
@ -104,147 +103,145 @@ export default class Component extends Node {
const updates: string[] = [];
const attributeObject = stringifyProps(
attributes.map((attribute: Attribute) => `${attribute.name}: ${attribute.value}`)
);
if (attributes.length || bindings.length) {
const initialProps = attributes
.map((attribute: Attribute) => `${attribute.name}: ${attribute.value}`);
const initialPropString = stringifyProps(initialProps);
attributes
.filter((attribute: Attribute) => attribute.dynamic)
.forEach((attribute: Attribute) => {
if (attribute.dependencies.length) {
updates.push(deindent`
if (${attribute.dependencies
.map(dependency => `changed.${dependency}`)
.join(' || ')}) ${name}_changes.${attribute.name} = ${attribute.value};
`);
}
componentInitProperties.push(`data: ${name_initial_data}`);
}
else {
// TODO this is an odd situation to encounter I *think* it should only happen with
// each block indices, in which case it may be possible to optimise this
updates.push(`${name}_changes.${attribute.name} = ${attribute.value};`);
}
});
if (attributes.length) {
if (attributes.find(a => a.type === 'Spread')) {
// TODO
} else {
attributes
.filter((attribute: Attribute) => attribute.dynamic)
.forEach((attribute: Attribute) => {
if (attribute.dependencies.length) {
updates.push(deindent`
if (${attribute.dependencies
.map(dependency => `changed.${dependency}`)
.join(' || ')}) ${name}_changes.${attribute.name} = ${attribute.value};
`);
}
else {
// TODO this is an odd situation to encounter I *think* it should only happen with
// each block indices, in which case it may be possible to optimise this
updates.push(`${name}_changes.${attribute.name} = ${attribute.value};`);
}
});
}
}
if (bindings.length) {
generator.hasComplexBindings = true;
if (bindings.length) {
generator.hasComplexBindings = true;
name_updating = block.alias(`${name}_updating`);
name_initial_data = block.getUniqueName(`${name}_initial_data`);
name_updating = block.alias(`${name}_updating`);
block.addVariable(name_updating, '{}');
block.addVariable(name_updating, '{}');
statements.push(`var ${name_initial_data} = ${initialPropString};`);
let hasLocalBindings = false;
let hasStoreBindings = false;
let hasLocalBindings = false;
let hasStoreBindings = false;
const builder = new CodeBuilder();
const builder = new CodeBuilder();
bindings.forEach((binding: Binding) => {
let { name: key } = getObject(binding.value);
bindings.forEach((binding: Binding) => {
let { name: key } = getObject(binding.value);
binding.contexts.forEach(context => {
allContexts.add(context);
});
binding.contexts.forEach(context => {
allContexts.add(context);
});
let setFromChild;
let setFromChild;
if (block.contexts.has(key)) {
const computed = isComputed(binding.value);
const tail = binding.value.type === 'MemberExpression' ? getTailSnippet(binding.value) : '';
if (block.contexts.has(key)) {
const computed = isComputed(binding.value);
const tail = binding.value.type === 'MemberExpression' ? getTailSnippet(binding.value) : '';
const list = block.listNames.get(key);
const index = block.indexNames.get(key);
const list = block.listNames.get(key);
const index = block.indexNames.get(key);
setFromChild = deindent`
${list}[${index}]${tail} = childState.${binding.name};
setFromChild = deindent`
${list}[${index}]${tail} = childState.${binding.name};
${binding.dependencies
.map((name: string) => {
const isStoreProp = generator.options.store && name[0] === '$';
const prop = isStoreProp ? name.slice(1) : name;
const newState = isStoreProp ? 'newStoreState' : 'newState';
${binding.dependencies
.map((name: string) => {
const isStoreProp = generator.options.store && name[0] === '$';
const prop = isStoreProp ? name.slice(1) : name;
const newState = isStoreProp ? 'newStoreState' : 'newState';
if (isStoreProp) hasStoreBindings = true;
else hasLocalBindings = true;
return `${newState}.${prop} = state.${name};`;
})}
`;
}
if (isStoreProp) hasStoreBindings = true;
else hasLocalBindings = true;
else {
const isStoreProp = generator.options.store && key[0] === '$';
const prop = isStoreProp ? key.slice(1) : key;
const newState = isStoreProp ? 'newStoreState' : 'newState';
return `${newState}.${prop} = state.${name};`;
})
.join('\n')}
if (isStoreProp) hasStoreBindings = true;
else hasLocalBindings = true;
if (binding.value.type === 'MemberExpression') {
setFromChild = deindent`
${binding.snippet} = childState.${binding.name};
${newState}.${prop} = state.${key};
`;
}
else {
const isStoreProp = generator.options.store && key[0] === '$';
const prop = isStoreProp ? key.slice(1) : key;
const newState = isStoreProp ? 'newStoreState' : 'newState';
if (isStoreProp) hasStoreBindings = true;
else hasLocalBindings = true;
if (binding.value.type === 'MemberExpression') {
setFromChild = deindent`
${binding.snippet} = childState.${binding.name};
${newState}.${prop} = state.${key};
`;
}
else {
setFromChild = `${newState}.${prop} = childState.${binding.name};`;
}
setFromChild = `${newState}.${prop} = childState.${binding.name};`;
}
}
statements.push(deindent`
if (${binding.prop} in ${binding.obj}) {
${name_initial_data}.${binding.name} = ${binding.snippet};
${name_updating}.${binding.name} = true;
}`
);
builder.addConditional(
`!${name_updating}.${binding.name} && changed.${binding.name}`,
setFromChild
);
// TODO could binding.dependencies.length ever be 0?
if (binding.dependencies.length) {
updates.push(deindent`
if (!${name_updating}.${binding.name} && ${binding.dependencies.map((dependency: string) => `changed.${dependency}`).join(' || ')}) {
${name}_changes.${binding.name} = ${binding.snippet};
${name_updating}.${binding.name} = true;
}
`);
}
});
statements.push(deindent`
if (${binding.prop} in ${binding.obj}) {
${name_initial_data}.${binding.name} = ${binding.snippet};
${name_updating}.${binding.name} = true;
}`
);
componentInitProperties.push(`data: ${name_initial_data}`);
const initialisers = [
'state = #component.get()',
hasLocalBindings && 'newState = {}',
hasStoreBindings && 'newStoreState = {}',
].filter(Boolean).join(', ');
componentInitProperties.push(deindent`
_bind: function(changed, childState) {
var ${initialisers};
${builder}
${hasStoreBindings && `#component.store.set(newStoreState);`}
${hasLocalBindings && `#component._set(newState);`}
${name_updating} = {};
builder.addConditional(
`!${name_updating}.${binding.name} && changed.${binding.name}`,
setFromChild
);
updates.push(deindent`
if (!${name_updating}.${binding.name} && ${binding.dependencies.map((dependency: string) => `changed.${dependency}`).join(' || ')}) {
${name}_changes.${binding.name} = ${binding.snippet};
${name_updating}.${binding.name} = true;
}
`);
});
beforecreate = deindent`
#component.root._beforecreate.push(function() {
${name}._bind({ ${bindings.map(b => `${b.name}: 1`).join(', ')} }, ${name}.get());
});
`;
} else if (initialProps.length) {
componentInitProperties.push(`data: ${initialPropString}`);
}
componentInitProperties.push(`data: ${name_initial_data}`);
const initialisers = [
'state = #component.get()',
hasLocalBindings && 'newState = {}',
hasStoreBindings && 'newStoreState = {}',
].filter(Boolean).join(', ');
componentInitProperties.push(deindent`
_bind: function(changed, childState) {
var ${initialisers};
${builder}
${hasStoreBindings && `#component.store.set(newStoreState);`}
${hasLocalBindings && `#component._set(newState);`}
${name_updating} = {};
}
`);
beforecreate = deindent`
#component.root._beforecreate.push(function() {
${name}._bind({ ${bindings.map(b => `${b.name}: 1`).join(', ')} }, ${name}.get());
});
`;
}
if (this.name === ':Component') {
@ -260,7 +257,9 @@ export default class Component extends Node {
var ${switch_value} = ${snippet};
function ${switch_props}(state) {
${statements.length > 0 && statements.join('\n')}
${(attributes.length || bindings.length) && deindent`
var ${name_initial_data} = ${attributeObject};`}
${statements}
return {
${componentInitProperties.join(',\n')}
};
@ -329,7 +328,7 @@ export default class Component extends Node {
block.builders.update.addBlock(deindent`
else {
var ${name}_changes = {};
${updates.join('\n')}
${updates}
${name}._set(${name}_changes);
${bindings.length && `${name_updating} = {};`}
}
@ -345,7 +344,9 @@ export default class Component extends Node {
: `%components-${this.name}`;
block.builders.init.addBlock(deindent`
${statements.join('\n')}
${(attributes.length || bindings.length) && deindent`
var ${name_initial_data} = ${attributeObject};`}
${statements}
var ${name} = new ${expression}({
${componentInitProperties.join(',\n')}
});
@ -376,7 +377,7 @@ export default class Component extends Node {
if (updates.length) {
block.builders.update.addBlock(deindent`
var ${name}_changes = {};
${updates.join('\n')}
${updates}
${name}._set(${name}_changes);
${bindings.length && `${name_updating} = {};`}
`);

@ -4,21 +4,46 @@ import Node from './Node';
import Attribute from '../Attribute';
import Block from '../../dom/Block';
export default function mungeAttribute(attribute: Node, block: Block): Attribute {
type MungedAttribute = {
spread: boolean;
name: string;
value: string | true;
dependencies: string[];
dynamic: boolean;
}
export default function mungeAttribute(attribute: Node, block: Block): MungedAttribute {
if (attribute.type === 'Spread') {
block.contextualise(attribute.expression); // TODO remove
const { dependencies, snippet } = attribute.metadata;
return {
spread: true,
name: null,
value: snippet,
dynamic: dependencies.length > 0,
dependencies
};
}
if (attribute.value === true) {
// attributes without values, e.g. <textarea readonly>
return {
spread: false,
name: attribute.name,
value: true,
dynamic: false
dynamic: false,
dependencies: []
};
}
if (attribute.value.length === 0) {
return {
spread: false,
name: attribute.name,
value: `''`,
dynamic: false
dynamic: false,
dependencies: []
};
}
@ -28,9 +53,11 @@ export default function mungeAttribute(attribute: Node, block: Block): Attribute
if (value.type === 'Text') {
// static attributes
return {
spread: false,
name: attribute.name,
value: isNaN(value.data) ? stringify(value.data) : value.data,
dynamic: false
dynamic: false,
dependencies: []
};
}
@ -40,6 +67,7 @@ export default function mungeAttribute(attribute: Node, block: Block): Attribute
// TODO only update attributes that have changed
return {
spread: false,
name: attribute.name,
value: snippet,
dependencies,
@ -70,6 +98,7 @@ export default function mungeAttribute(attribute: Node, block: Block): Attribute
.join(' + ');
return {
spread: false,
name: attribute.name,
value,
dependencies: Array.from(allDependencies),

Loading…
Cancel
Save