pull/1746/head
Rich Harris 7 years ago
parent e55a806a52
commit bbce893c06

@ -1,10 +1,7 @@
import deindent from '../../utils/deindent';
import Node from './shared/Node';
import Block from '../render-dom/Block';
import PendingBlock from './PendingBlock';
import ThenBlock from './ThenBlock';
import CatchBlock from './CatchBlock';
import createDebuggingComment from '../../utils/createDebuggingComment';
import Expression from './shared/Expression';
export default class AwaitBlock extends Node {
@ -29,168 +26,4 @@ export default class AwaitBlock extends Node {
this.then = new ThenBlock(component, this, scope.add(this.value, deps), info.then);
this.catch = new CatchBlock(component, this, scope.add(this.error, deps), info.catch);
}
init(
block: Block,
stripWhitespace: boolean,
nextSibling: Node
) {
this.cannotUseInnerHTML();
this.var = block.getUniqueName('await_block');
block.addDependencies(this.expression.dependencies);
let isDynamic = false;
let hasIntros = false;
let hasOutros = false;
['pending', 'then', 'catch'].forEach(status => {
const child = this[status];
child.block = block.child({
comment: createDebuggingComment(child, this.component),
name: this.component.getUniqueName(`create_${status}_block`)
});
child.initChildren(child.block, stripWhitespace, nextSibling);
this.component.target.blocks.push(child.block);
if (child.block.dependencies.size > 0) {
isDynamic = true;
block.addDependencies(child.block.dependencies);
}
if (child.block.hasIntros) hasIntros = true;
if (child.block.hasOutros) hasOutros = true;
});
this.pending.block.hasUpdateMethod = isDynamic;
this.then.block.hasUpdateMethod = isDynamic;
this.catch.block.hasUpdateMethod = isDynamic;
this.pending.block.hasIntroMethod = hasIntros;
this.then.block.hasIntroMethod = hasIntros;
this.catch.block.hasIntroMethod = hasIntros;
this.pending.block.hasOutroMethod = hasOutros;
this.then.block.hasOutroMethod = hasOutros;
this.catch.block.hasOutroMethod = hasOutros;
if (hasOutros && this.component.options.nestedTransitions) block.addOutro();
}
build(
block: Block,
parentNode: string,
parentNodes: string
) {
const name = this.var;
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor);
const { snippet } = this.expression;
const info = block.getUniqueName(`info`);
const promise = block.getUniqueName(`promise`);
block.addVariable(promise);
block.maintainContext = true;
const infoProps = [
block.alias('component') === 'component' ? 'component' : `component: #component`,
'ctx',
'current: null',
this.pending.block.name && `pending: ${this.pending.block.name}`,
this.then.block.name && `then: ${this.then.block.name}`,
this.catch.block.name && `catch: ${this.catch.block.name}`,
this.then.block.name && `value: '${this.value}'`,
this.catch.block.name && `error: '${this.error}'`,
this.pending.block.hasOutroMethod && `blocks: Array(3)`
].filter(Boolean);
block.builders.init.addBlock(deindent`
let ${info} = {
${infoProps.join(',\n')}
};
`);
block.builders.init.addBlock(deindent`
@handlePromise(${promise} = ${snippet}, ${info});
`);
block.builders.create.addBlock(deindent`
${info}.block.c();
`);
if (parentNodes) {
block.builders.claim.addBlock(deindent`
${info}.block.l(${parentNodes});
`);
}
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const hasTransitions = this.pending.block.hasIntroMethod || this.pending.block.hasOutroMethod;
block.builders.mount.addBlock(deindent`
${info}.block.${hasTransitions ? 'i' : 'm'}(${initialMountNode}, ${info}.anchor = ${anchorNode});
${info}.mount = () => ${updateMountNode};
`);
const conditions = [];
if (this.expression.dependencies.size > 0) {
conditions.push(
`(${[...this.expression.dependencies].map(dep => `'${dep}' in changed`).join(' || ')})`
);
}
conditions.push(
`${promise} !== (${promise} = ${snippet})`,
`@handlePromise(${promise}, ${info})`
);
block.builders.update.addLine(
`${info}.ctx = ctx;`
);
if (this.pending.block.hasUpdateMethod) {
block.builders.update.addBlock(deindent`
if (${conditions.join(' && ')}) {
// nothing
} else {
${info}.block.p(changed, @assign(@assign({}, ctx), ${info}.resolved));
}
`);
} else {
block.builders.update.addBlock(deindent`
${conditions.join(' && ')}
`);
}
if (this.pending.block.hasOutroMethod && this.component.options.nestedTransitions) {
const countdown = block.getUniqueName('countdown');
block.builders.outro.addBlock(deindent`
const ${countdown} = @callAfter(#outrocallback, 3);
for (let #i = 0; #i < 3; #i += 1) {
const block = ${info}.blocks[#i];
if (block) block.o(${countdown});
else ${countdown}();
}
`);
}
block.builders.destroy.addBlock(deindent`
${info}.block.d(${parentNode ? '' : 'detach'});
${info} = null;
`);
[this.pending, this.then, this.catch].forEach(status => {
status.children.forEach(child => {
child.build(status.block, null, 'nodes');
});
});
}
}

@ -0,0 +1,230 @@
import Wrapper from './shared/Wrapper';
import Renderer from '../Renderer';
import Block from '../Block';
import AwaitBlock from '../../nodes/AwaitBlock';
import createDebuggingComment from '../../../utils/createDebuggingComment';
import deindent from '../../../utils/deindent';
import FragmentWrapper from './Fragment';
import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock';
class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock;
block: Block;
fragment: FragmentWrapper;
isDynamic: boolean;
var = null;
constructor(
status: string,
renderer: Renderer,
block: Block,
parent: Wrapper,
node: AwaitBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
) {
super(renderer, block, parent, node);
this.block = block.child({
comment: createDebuggingComment(node, this.renderer.component),
name: this.renderer.component.getUniqueName(`create_${status}_block`)
});
this.fragment = new FragmentWrapper(
renderer,
this.block,
this.node.children,
parent,
stripWhitespace,
nextSibling
);
this.isDynamic = this.block.dependencies.size > 0;
}
}
export default class AwaitBlockWrapper extends Wrapper {
node: AwaitBlock;
pending: AwaitBlockBranch;
then: AwaitBlockBranch;
catch: AwaitBlockBranch;
var = 'await_block';
constructor(
renderer: Renderer,
block: Block,
parent: Wrapper,
node: AwaitBlock,
stripWhitespace: boolean,
nextSibling: Wrapper
) {
super(renderer, block, parent, node);
this.cannotUseInnerHTML();
block.addDependencies(this.node.expression.dependencies);
let isDynamic = false;
let hasIntros = false;
let hasOutros = false;
['pending', 'then', 'catch'].forEach(status => {
const child = this.node[status];
const branch = new AwaitBlockBranch(
status,
renderer,
block,
parent,
child,
stripWhitespace,
nextSibling
);
renderer.blocks.push(branch.block);
if (branch.isDynamic) {
isDynamic = true;
// TODO should blocks update their own parents?
block.addDependencies(branch.block.dependencies);
}
if (branch.block.hasIntros) hasIntros = true;
if (branch.block.hasOutros) hasOutros = true;
this[status] = branch;
});
this.pending.block.hasUpdateMethod = isDynamic;
this.then.block.hasUpdateMethod = isDynamic;
this.catch.block.hasUpdateMethod = isDynamic;
this.pending.block.hasIntroMethod = hasIntros;
this.then.block.hasIntroMethod = hasIntros;
this.catch.block.hasIntroMethod = hasIntros;
this.pending.block.hasOutroMethod = hasOutros;
this.then.block.hasOutroMethod = hasOutros;
this.catch.block.hasOutroMethod = hasOutros;
if (hasOutros && this.renderer.options.nestedTransitions) {
block.addOutro();
}
}
render(
block: Block,
parentNode: string,
parentNodes: string
) {
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
const updateMountNode = this.getUpdateMountNode(anchor);
const { snippet } = this.node.expression;
const info = block.getUniqueName(`info`);
const promise = block.getUniqueName(`promise`);
block.addVariable(promise);
block.maintainContext = true;
const infoProps = [
block.alias('component') === 'component' ? 'component' : `component: #component`,
'ctx',
'current: null',
this.pending.block.name && `pending: ${this.pending.block.name}`,
this.then.block.name && `then: ${this.then.block.name}`,
this.catch.block.name && `catch: ${this.catch.block.name}`,
this.then.block.name && `value: '${this.node.value}'`,
this.catch.block.name && `error: '${this.node.error}'`,
this.pending.block.hasOutroMethod && `blocks: Array(3)`
].filter(Boolean);
block.builders.init.addBlock(deindent`
let ${info} = {
${infoProps.join(',\n')}
};
`);
block.builders.init.addBlock(deindent`
@handlePromise(${promise} = ${snippet}, ${info});
`);
block.builders.create.addBlock(deindent`
${info}.block.c();
`);
if (parentNodes) {
block.builders.claim.addBlock(deindent`
${info}.block.l(${parentNodes});
`);
}
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';
const hasTransitions = this.pending.block.hasIntroMethod || this.pending.block.hasOutroMethod;
block.builders.mount.addBlock(deindent`
${info}.block.${hasTransitions ? 'i' : 'm'}(${initialMountNode}, ${info}.anchor = ${anchorNode});
${info}.mount = () => ${updateMountNode};
`);
const conditions = [];
if (this.node.expression.dependencies.size > 0) {
conditions.push(
`(${[...this.node.expression.dependencies].map(dep => `'${dep}' in changed`).join(' || ')})`
);
}
conditions.push(
`${promise} !== (${promise} = ${snippet})`,
`@handlePromise(${promise}, ${info})`
);
block.builders.update.addLine(
`${info}.ctx = ctx;`
);
if (this.pending.block.hasUpdateMethod) {
block.builders.update.addBlock(deindent`
if (${conditions.join(' && ')}) {
// nothing
} else {
${info}.block.p(changed, @assign(@assign({}, ctx), ${info}.resolved));
}
`);
} else {
block.builders.update.addBlock(deindent`
${conditions.join(' && ')}
`);
}
if (this.pending.block.hasOutroMethod && this.renderer.options.nestedTransitions) {
const countdown = block.getUniqueName('countdown');
block.builders.outro.addBlock(deindent`
const ${countdown} = @callAfter(#outrocallback, 3);
for (let #i = 0; #i < 3; #i += 1) {
const block = ${info}.blocks[#i];
if (block) block.o(${countdown});
else ${countdown}();
}
`);
}
block.builders.destroy.addBlock(deindent`
${info}.block.d(${parentNode ? '' : 'detach'});
${info} = null;
`);
[this.pending, this.then, this.catch].forEach(branch => {
branch.fragment.render(branch.block, null, 'nodes');
});
}
}

@ -1,7 +1,7 @@
import Renderer from '../Renderer';
import Block from '../Block';
import Node from '../../nodes/shared/Node';
import Wrapper from './shared/wrapper';
import Wrapper from './shared/Wrapper';
import createDebuggingComment from '../../../utils/createDebuggingComment';
import EachBlock from '../../nodes/EachBlock';
import FragmentWrapper from './Fragment';

@ -1,6 +1,6 @@
import Renderer from '../../Renderer';
import Element from '../../../nodes/Element';
import Wrapper from '../shared/wrapper';
import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
import Node from '../../../nodes/shared/Node';
import { CompileOptions } from '../../../../interfaces';
@ -550,7 +550,8 @@ export default class ElementWrapper extends Wrapper {
const isCustomEvent = component.events.has(handler.name);
if (handler.callee) {
handler.render(this.component, block, handler.shouldHoist);
// TODO move handler render method into a wrapper
handler.render(this.renderer.component, block, handler.shouldHoist);
}
const target = handler.shouldHoist ? 'this' : this.var;

@ -1,4 +1,5 @@
import Wrapper from './shared/wrapper';
import Wrapper from './shared/Wrapper';
import AwaitBlock from './AwaitBlock';
import EachBlock from './EachBlock';
import Element from './Element';
import IfBlock from './IfBlock';
@ -13,6 +14,7 @@ import Renderer from '../Renderer';
import Block from '../Block';
const wrappers = {
AwaitBlock,
Comment: null,
EachBlock,
Element,

@ -1,4 +1,4 @@
import Wrapper from './shared/wrapper';
import Wrapper from './shared/Wrapper';
import Renderer from '../Renderer';
import Block from '../Block';
import EachBlock from '../../nodes/EachBlock';
@ -45,7 +45,7 @@ class IfBlockBranch extends Wrapper {
)
});
this.fragment = new FragmentWrapper(renderer, block, node.children, parent.parent, stripWhitespace, nextSibling);
this.fragment = new FragmentWrapper(renderer, this.block, node.children, parent.parent, stripWhitespace, nextSibling);
this.isDynamic = this.block.dependencies.size > 0;
}
@ -74,7 +74,7 @@ export default class IfBlockWrapper extends Wrapper {
this.branches = [];
const blocks: Block[] = [];
let dynamic = false;
let isDynamic = false;
let hasIntros = false;
let hasOutros = false;
@ -93,8 +93,8 @@ export default class IfBlockWrapper extends Wrapper {
blocks.push(branch.block);
block.addDependencies(node.expression.dependencies);
if (branch.isDynamic) {
dynamic = true;
if (branch.block.dependencies.size > 0) {
isDynamic = true;
block.addDependencies(branch.block.dependencies);
}
@ -118,7 +118,7 @@ export default class IfBlockWrapper extends Wrapper {
blocks.push(branch.block);
if (branch.block.dependencies.size > 0) {
dynamic = true;
isDynamic = true;
block.addDependencies(branch.block.dependencies);
}
@ -135,7 +135,7 @@ export default class IfBlockWrapper extends Wrapper {
}
blocks.forEach(block => {
block.hasUpdateMethod = dynamic;
block.hasUpdateMethod = isDynamic;
block.hasIntroMethod = hasIntros;
block.hasOutroMethod = hasOutros;
});

@ -1,4 +1,4 @@
import Wrapper from '../shared/wrapper';
import Wrapper from '../shared/Wrapper';
import Renderer from '../../Renderer';
import Block from '../../Block';
import Node from '../../../nodes/shared/Node';

@ -1,7 +1,7 @@
import Renderer from '../Renderer';
import Block from '../Block';
import Text from '../../nodes/Text';
import Wrapper from './shared/wrapper';
import Wrapper from './shared/Wrapper';
import { CompileOptions } from '../../../interfaces';
import { stringify } from '../../../utils/stringify';

@ -1,7 +1,7 @@
import Renderer from '../Renderer';
import Block from '../Block';
import Node from '../../nodes/shared/Node';
import Wrapper from './shared/wrapper';
import Wrapper from './shared/Wrapper';
import deindent from '../../../utils/deindent';
const associatedEvents = {

@ -1,6 +1,8 @@
export default {
test(assert, component, target) {
const promise = Promise.resolve().then(() => component.set({ answer: 42 }));
const promise = Promise.resolve().then(() => {
component.set({ answer: 42 });
});
component.set({ promise });

Loading…
Cancel
Save