|
|
|
import deindent from '../../utils/deindent';
|
|
|
|
import Node from './shared/Node';
|
|
|
|
import { DomGenerator } from '../dom/index';
|
|
|
|
import Block from '../dom/Block';
|
|
|
|
import PendingBlock from './PendingBlock';
|
|
|
|
import ThenBlock from './ThenBlock';
|
|
|
|
import CatchBlock from './CatchBlock';
|
|
|
|
import createDebuggingComment from '../../utils/createDebuggingComment';
|
|
|
|
|
|
|
|
export default class AwaitBlock extends Node {
|
|
|
|
value: string;
|
|
|
|
error: string;
|
|
|
|
expression: Node;
|
|
|
|
|
|
|
|
pending: PendingBlock;
|
|
|
|
then: ThenBlock;
|
|
|
|
catch: CatchBlock;
|
|
|
|
|
|
|
|
init(
|
|
|
|
block: Block,
|
|
|
|
stripWhitespace: boolean,
|
|
|
|
nextSibling: Node
|
|
|
|
) {
|
|
|
|
this.cannotUseInnerHTML();
|
|
|
|
|
|
|
|
this.var = block.getUniqueName('await_block');
|
|
|
|
block.addDependencies(this.metadata.dependencies);
|
|
|
|
|
|
|
|
let dynamic = false;
|
|
|
|
|
|
|
|
[
|
|
|
|
['pending', null],
|
|
|
|
['then', this.value],
|
|
|
|
['catch', this.error]
|
|
|
|
].forEach(([status, arg]) => {
|
|
|
|
const child = this[status];
|
|
|
|
|
|
|
|
const context = block.getUniqueName(arg || '_'); // TODO can we remove the extra param from pending blocks?
|
|
|
|
const contexts = new Map(block.contexts);
|
|
|
|
contexts.set(arg, context);
|
|
|
|
|
|
|
|
const contextTypes = new Map(block.contextTypes);
|
|
|
|
contextTypes.set(arg, status);
|
|
|
|
|
|
|
|
child.block = block.child({
|
|
|
|
comment: createDebuggingComment(child, this.generator),
|
|
|
|
name: this.generator.getUniqueName(`create_${status}_block`),
|
|
|
|
params: block.params.concat(context),
|
|
|
|
context,
|
|
|
|
contexts,
|
|
|
|
contextTypes
|
|
|
|
});
|
|
|
|
|
|
|
|
child.initChildren(child.block, stripWhitespace, nextSibling);
|
|
|
|
this.generator.blocks.push(child.block);
|
|
|
|
|
|
|
|
if (child.block.dependencies.size > 0) {
|
|
|
|
dynamic = true;
|
|
|
|
block.addDependencies(child.block.dependencies);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this.pending.block.hasUpdateMethod = dynamic;
|
|
|
|
this.then.block.hasUpdateMethod = dynamic;
|
|
|
|
this.catch.block.hasUpdateMethod = dynamic;
|
|
|
|
}
|
|
|
|
|
|
|
|
build(
|
|
|
|
block: Block,
|
|
|
|
parentNode: string,
|
|
|
|
parentNodes: string
|
|
|
|
) {
|
|
|
|
const name = this.var;
|
|
|
|
|
|
|
|
const anchor = this.getOrCreateAnchor(block, parentNode, parentNodes);
|
|
|
|
const updateMountNode = this.getUpdateMountNode(anchor);
|
|
|
|
|
|
|
|
const params = block.params.join(', ');
|
|
|
|
|
|
|
|
block.contextualise(this.expression);
|
|
|
|
const { snippet } = this.metadata;
|
|
|
|
|
|
|
|
const promise = block.getUniqueName(`promise`);
|
|
|
|
const resolved = block.getUniqueName(`resolved`);
|
|
|
|
const await_block = block.getUniqueName(`await_block`);
|
|
|
|
const await_block_type = block.getUniqueName(`await_block_type`);
|
|
|
|
const token = block.getUniqueName(`token`);
|
|
|
|
const await_token = block.getUniqueName(`await_token`);
|
|
|
|
const handle_promise = block.getUniqueName(`handle_promise`);
|
|
|
|
const replace_await_block = block.getUniqueName(`replace_await_block`);
|
|
|
|
const old_block = block.getUniqueName(`old_block`);
|
|
|
|
const value = block.getUniqueName(`value`);
|
|
|
|
const error = block.getUniqueName(`error`);
|
|
|
|
const create_pending_block = this.pending.block.name;
|
|
|
|
const create_then_block = this.then.block.name;
|
|
|
|
const create_catch_block = this.catch.block.name;
|
|
|
|
|
|
|
|
block.addVariable(await_block);
|
|
|
|
block.addVariable(await_block_type);
|
|
|
|
block.addVariable(await_token);
|
|
|
|
block.addVariable(promise);
|
|
|
|
block.addVariable(resolved);
|
|
|
|
|
|
|
|
// the `#component.root.set({})` below is just a cheap way to flush
|
|
|
|
// any oncreate handlers. We could have a dedicated `flush()` method
|
|
|
|
// but it's probably not worth it
|
|
|
|
|
|
|
|
block.builders.init.addBlock(deindent`
|
|
|
|
function ${replace_await_block}(${token}, type, ${value}, ${params}) {
|
|
|
|
if (${token} !== ${await_token}) return;
|
|
|
|
|
|
|
|
var ${old_block} = ${await_block};
|
|
|
|
${await_block} = (${await_block_type} = type)(${params}, ${resolved} = ${value}, #component);
|
|
|
|
|
|
|
|
if (${old_block}) {
|
|
|
|
${old_block}.u();
|
|
|
|
${old_block}.d();
|
|
|
|
${await_block}.c();
|
|
|
|
${await_block}.m(${updateMountNode}, ${anchor});
|
|
|
|
|
|
|
|
#component.root.set({});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function ${handle_promise}(${promise}, ${params}) {
|
|
|
|
var ${token} = ${await_token} = {};
|
|
|
|
|
|
|
|
if (@isPromise(${promise})) {
|
|
|
|
${promise}.then(function(${value}) {
|
|
|
|
${replace_await_block}(${token}, ${create_then_block}, ${value}, ${params});
|
|
|
|
}, function (${error}) {
|
|
|
|
${replace_await_block}(${token}, ${create_catch_block}, ${error}, ${params});
|
|
|
|
});
|
|
|
|
|
|
|
|
// if we previously had a then/catch block, destroy it
|
|
|
|
if (${await_block_type} !== ${create_pending_block}) {
|
|
|
|
${replace_await_block}(${token}, ${create_pending_block}, null, ${params});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
${resolved} = ${promise};
|
|
|
|
if (${await_block_type} !== ${create_then_block}) {
|
|
|
|
${replace_await_block}(${token}, ${create_then_block}, ${resolved}, ${params});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
${handle_promise}(${promise} = ${snippet}, ${params});
|
|
|
|
`);
|
|
|
|
|
|
|
|
block.builders.create.addBlock(deindent`
|
|
|
|
${await_block}.c();
|
|
|
|
`);
|
|
|
|
|
|
|
|
if (parentNodes) {
|
|
|
|
block.builders.claim.addBlock(deindent`
|
|
|
|
${await_block}.l(${parentNodes});
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const initialMountNode = parentNode || '#target';
|
|
|
|
const anchorNode = parentNode ? 'null' : 'anchor';
|
|
|
|
|
|
|
|
block.builders.mount.addBlock(deindent`
|
|
|
|
${await_block}.m(${initialMountNode}, ${anchorNode});
|
|
|
|
`);
|
|
|
|
|
|
|
|
const conditions = [];
|
|
|
|
if (this.metadata.dependencies) {
|
|
|
|
conditions.push(
|
|
|
|
`(${this.metadata.dependencies.map(dep => `'${dep}' in changed`).join(' || ')})`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
conditions.push(
|
|
|
|
`${promise} !== (${promise} = ${snippet})`,
|
|
|
|
`${handle_promise}(${promise}, ${params})`
|
|
|
|
);
|
|
|
|
|
|
|
|
if (this.pending.block.hasUpdateMethod) {
|
|
|
|
block.builders.update.addBlock(deindent`
|
|
|
|
if (${conditions.join(' && ')}) {
|
|
|
|
// nothing
|
|
|
|
} else {
|
|
|
|
${await_block}.p(changed, ${params}, ${resolved});
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
} else {
|
|
|
|
block.builders.update.addBlock(deindent`
|
|
|
|
if (${conditions.join(' && ')}) {
|
|
|
|
${await_block}.c();
|
|
|
|
${await_block}.m(${anchor}.parentNode, ${anchor});
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
|
|
|
|
block.builders.unmount.addBlock(deindent`
|
|
|
|
${await_block}.u();
|
|
|
|
`);
|
|
|
|
|
|
|
|
block.builders.destroy.addBlock(deindent`
|
|
|
|
${await_token} = null;
|
|
|
|
${await_block}.d();
|
|
|
|
`);
|
|
|
|
|
|
|
|
[this.pending, this.then, this.catch].forEach(status => {
|
|
|
|
status.children.forEach(child => {
|
|
|
|
child.build(status.block, null,'nodes');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|