start attempting to simplify binding code a bit

pull/1934/head
Richard Harris 7 years ago
parent 08605efcab
commit 287590e338

@ -29,7 +29,7 @@ export default class Block {
dependencies: Set<string>; dependencies: Set<string>;
bindings: Map<string, () => { object: string, property: string, snippet: string }>; bindings: Map<string, { object: string, property: string, snippet: string }>;
contextOwners: Map<string, EachBlockWrapper>; contextOwners: Map<string, EachBlockWrapper>;
builders: { builders: {

@ -97,15 +97,33 @@ export default class EachBlockWrapper extends Wrapper {
this.indexName = this.node.index || renderer.component.getUniqueName(`${this.node.context}_index`); this.indexName = this.node.index || renderer.component.getUniqueName(`${this.node.context}_index`);
// hack the sourcemap, so that if data is missing the bug
// is easy to find
let c = this.node.start + 2;
while (renderer.component.source[c] !== 'e') c += 1;
renderer.component.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`;
this.vars = {
create_each_block: this.block.name,
each_block_value: renderer.component.getUniqueName(`${this.var}_value`),
get_each_context: renderer.component.getUniqueName(`get_${this.var}_context`),
iterations: block.getUniqueName(`${this.var}_blocks`),
length: `[✂${c}-${c+4}✂]`,
// filled out later
anchor: null,
mountOrIntro: null
};
node.contexts.forEach(prop => { node.contexts.forEach(prop => {
this.block.contextOwners.set(prop.key.name, this); this.block.contextOwners.set(prop.key.name, this);
// TODO this doesn't feel great this.block.bindings.set(prop.key.name, {
this.block.bindings.set(prop.key.name, () => ({
object: this.vars.each_block_value, object: this.vars.each_block_value,
property: this.indexName, property: this.indexName,
snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}` snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}`
})); });
}); });
if (this.node.index) { if (this.node.index) {
@ -147,30 +165,15 @@ export default class EachBlockWrapper extends Wrapper {
const { renderer } = this; const { renderer } = this;
const { component } = renderer; const { component } = renderer;
// hack the sourcemap, so that if data is missing the bug
// is easy to find
let c = this.node.start + 2;
while (component.source[c] !== 'e') c += 1;
component.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`;
const needsAnchor = this.next const needsAnchor = this.next
? !this.next.isDomNode() : ? !this.next.isDomNode() :
!parentNode || !this.parent.isDomNode(); !parentNode || !this.parent.isDomNode();
this.vars = { this.vars.anchor = needsAnchor
anchor: needsAnchor ? block.getUniqueName(`${this.var}_anchor`)
? block.getUniqueName(`${this.var}_anchor`) : (this.next && this.next.var) || 'null';
: (this.next && this.next.var) || 'null',
create_each_block: this.block.name, this.vars.mountOrIntro = (this.block.hasIntroMethod || this.block.hasOutroMethod) ? 'i' : 'm';
each_block_value: renderer.component.getUniqueName(`${this.var}_value`),
get_each_context: renderer.component.getUniqueName(`get_${this.var}_context`),
iterations: block.getUniqueName(`${this.var}_blocks`),
length: `[✂${c}-${c+4}✂]`,
mountOrIntro: (this.block.hasIntroMethod || this.block.hasOutroMethod)
? 'i'
: 'm'
};
this.contextProps = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`); this.contextProps = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`);
@ -212,7 +215,7 @@ export default class EachBlockWrapper extends Wrapper {
// TODO neaten this up... will end up with an empty line in the block // TODO neaten this up... will end up with an empty line in the block
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
if (!${this.vars.each_block_value}.${length}) { if (!${this.vars.each_block_value}.${this.vars.length}) {
${each_block_else} = ${this.else.block.name}(#component, ctx); ${each_block_else} = ${this.else.block.name}(#component, ctx);
${each_block_else}.c(); ${each_block_else}.c();
} }
@ -228,9 +231,9 @@ export default class EachBlockWrapper extends Wrapper {
if (this.else.block.hasUpdateMethod) { if (this.else.block.hasUpdateMethod) {
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
if (!${this.vars.each_block_value}.${length} && ${each_block_else}) { if (!${this.vars.each_block_value}.${this.vars.length} && ${each_block_else}) {
${each_block_else}.p(changed, ctx); ${each_block_else}.p(changed, ctx);
} else if (!${this.vars.each_block_value}.${length}) { } else if (!${this.vars.each_block_value}.${this.vars.length}) {
${each_block_else} = ${this.else.block.name}(#component, ctx); ${each_block_else} = ${this.else.block.name}(#component, ctx);
${each_block_else}.c(); ${each_block_else}.c();
${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor}); ${each_block_else}.${mountOrIntro}(${initialMountNode}, ${this.vars.anchor});
@ -241,7 +244,7 @@ export default class EachBlockWrapper extends Wrapper {
`); `);
} else { } else {
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
if (${this.vars.each_block_value}.${length}) { if (${this.vars.each_block_value}.${this.vars.length}) {
if (${each_block_else}) { if (${each_block_else}) {
${each_block_else}.d(1); ${each_block_else}.d(1);
${each_block_else} = null; ${each_block_else} = null;

@ -21,7 +21,11 @@ export default class BindingWrapper {
parent: ElementWrapper; parent: ElementWrapper;
object: string; object: string;
handler: any; // TODO handler: {
usesContext: boolean;
mutation: string;
contextual_dependencies: Set<string>
};
updateDom: string; updateDom: string;
initialUpdate: string; initialUpdate: string;
needsLock: boolean; needsLock: boolean;
@ -51,6 +55,15 @@ export default class BindingWrapper {
eachBlock.hasBinding = true; eachBlock.hasBinding = true;
} }
this.object = getObject(this.node.expression.node).name;
// TODO unfortunate code is necessary because we need to use `ctx`
// inside the fragment, but not inside the <script>
const contextless_snippet = this.parent.renderer.component.source.slice(this.node.expression.node.start, this.node.expression.node.end);
// view to model
this.handler = getEventHandler(this, parent.renderer, block, this.object, contextless_snippet);
} }
isReadOnlyMediaAttribute() { isReadOnlyMediaAttribute() {
@ -73,14 +86,8 @@ export default class BindingWrapper {
let updateConditions: string[] = []; let updateConditions: string[] = [];
const { name } = getObject(this.node.expression.node);
const snippet = this.node.expression.render(); const snippet = this.node.expression.render();
// TODO unfortunate code is necessary because we need to use `ctx`
// inside the fragment, but not inside the <script>
const contextless_snippet = this.parent.renderer.component.source.slice(this.node.expression.node.start, this.node.expression.node.end);
// special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>` // special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>`
// and `selected` is an object chosen with a <select>, then when `checked` changes, // and `selected` is an object chosen with a <select>, then when `checked` changes,
// we need to tell the component to update all the values `selected` might be // we need to tell the component to update all the values `selected` might be
@ -97,10 +104,6 @@ export default class BindingWrapper {
} }
}); });
// view to model
const valueFromDom = getValueFromDom(renderer, this.parent, this);
const handler = getEventHandler(this, renderer, block, name, contextless_snippet, valueFromDom);
// model to view // model to view
let updateDom = getDomUpdater(parent, this, snippet); let updateDom = getDomUpdater(parent, this, snippet);
let initialUpdate = updateDom; let initialUpdate = updateDom;
@ -152,10 +155,9 @@ export default class BindingWrapper {
return { return {
name: this.node.name, name: this.node.name,
object: name, object: this.object,
handler, handler: this.handler,
snippet, snippet,
usesContext: handler.usesContext,
updateDom: updateDom, updateDom: updateDom,
initialUpdate: initialUpdate, initialUpdate: initialUpdate,
needsLock: !isReadOnly && needsLock, needsLock: !isReadOnly && needsLock,
@ -221,9 +223,10 @@ function getEventHandler(
renderer: Renderer, renderer: Renderer,
block: Block, block: Block,
name: string, name: string,
snippet: string, snippet: string
value: string
) { ) {
const value = getValueFromDom(renderer, binding.parent, binding);
if (binding.node.isContextual) { if (binding.node.isContextual) {
let tail = ''; let tail = '';
if (binding.node.expression.node.type === 'MemberExpression') { if (binding.node.expression.node.type === 'MemberExpression') {
@ -231,7 +234,7 @@ function getEventHandler(
tail = renderer.component.source.slice(start, end); tail = renderer.component.source.slice(start, end);
} }
const { object, property, snippet } = block.bindings.get(name)(); const { object, property, snippet } = block.bindings.get(name);
return { return {
usesContext: true, usesContext: true,

@ -520,7 +520,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.declarations.push(name); renderer.component.declarations.push(name);
renderer.component.template_references.add(name); renderer.component.template_references.add(name);
const { handler, object } = this_binding.munge(block); const { handler, object } = this_binding;
renderer.component.partly_hoisted.push(deindent` renderer.component.partly_hoisted.push(deindent`
function ${name}($$node) { function ${name}($$node) {

@ -211,7 +211,7 @@ export default class InlineComponentWrapper extends Wrapper {
// bind:x={y} — we can't just do `y = x`, we need to // bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x; // to `array[index] = x;
const { name } = binding.expression.node; const { name } = binding.expression.node;
const { object, property, snippet } = block.bindings.get(name)(); const { object, property, snippet } = block.bindings.get(name);
lhs = snippet; lhs = snippet;
// TODO we need to invalidate... something // TODO we need to invalidate... something
@ -263,7 +263,7 @@ export default class InlineComponentWrapper extends Wrapper {
// bind:x={y} — we can't just do `y = x`, we need to // bind:x={y} — we can't just do `y = x`, we need to
// to `array[index] = x; // to `array[index] = x;
const { name } = binding.expression.node; const { name } = binding.expression.node;
const { object, property, snippet } = block.bindings.get(name)(); const { object, property, snippet } = block.bindings.get(name);
lhs = snippet; lhs = snippet;
contextual_dependencies.push(object, property); contextual_dependencies.push(object, property);
} }

Loading…
Cancel
Save