From bbce893c06dffa02866734d414d622aaffd746a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 20 Sep 2018 08:16:40 -0400 Subject: [PATCH] more --- src/compile/nodes/AwaitBlock.ts | 167 ------------- src/compile/render-dom/wrappers/AwaitBlock.ts | 230 ++++++++++++++++++ src/compile/render-dom/wrappers/EachBlock.ts | 2 +- .../render-dom/wrappers/Element/index.ts | 5 +- src/compile/render-dom/wrappers/Fragment.ts | 4 +- src/compile/render-dom/wrappers/IfBlock.ts | 14 +- .../wrappers/InlineComponent/index.ts | 2 +- src/compile/render-dom/wrappers/Text.ts | 2 +- src/compile/render-dom/wrappers/Window.ts | 2 +- .../samples/await-set-simultaneous/_config.js | 4 +- 10 files changed, 250 insertions(+), 182 deletions(-) create mode 100644 src/compile/render-dom/wrappers/AwaitBlock.ts diff --git a/src/compile/nodes/AwaitBlock.ts b/src/compile/nodes/AwaitBlock.ts index a6846c32b1..4d0559711f 100644 --- a/src/compile/nodes/AwaitBlock.ts +++ b/src/compile/nodes/AwaitBlock.ts @@ -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'); - }); - }); - } } diff --git a/src/compile/render-dom/wrappers/AwaitBlock.ts b/src/compile/render-dom/wrappers/AwaitBlock.ts new file mode 100644 index 0000000000..bfd3143333 --- /dev/null +++ b/src/compile/render-dom/wrappers/AwaitBlock.ts @@ -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'); + }); + } +} \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/EachBlock.ts b/src/compile/render-dom/wrappers/EachBlock.ts index 0483afd687..e04f3cbb39 100644 --- a/src/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compile/render-dom/wrappers/EachBlock.ts @@ -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'; diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 3eeb37d8ab..e0a2c8e316 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -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; diff --git a/src/compile/render-dom/wrappers/Fragment.ts b/src/compile/render-dom/wrappers/Fragment.ts index f577b8c57a..6be72d81fa 100644 --- a/src/compile/render-dom/wrappers/Fragment.ts +++ b/src/compile/render-dom/wrappers/Fragment.ts @@ -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, diff --git a/src/compile/render-dom/wrappers/IfBlock.ts b/src/compile/render-dom/wrappers/IfBlock.ts index 82ffb4835b..63bb9cb683 100644 --- a/src/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compile/render-dom/wrappers/IfBlock.ts @@ -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; }); diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index 0dced0daca..78bef9488f 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -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'; diff --git a/src/compile/render-dom/wrappers/Text.ts b/src/compile/render-dom/wrappers/Text.ts index dbb9096b71..98b6d55671 100644 --- a/src/compile/render-dom/wrappers/Text.ts +++ b/src/compile/render-dom/wrappers/Text.ts @@ -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'; diff --git a/src/compile/render-dom/wrappers/Window.ts b/src/compile/render-dom/wrappers/Window.ts index 24014df081..1686cc25c9 100644 --- a/src/compile/render-dom/wrappers/Window.ts +++ b/src/compile/render-dom/wrappers/Window.ts @@ -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 = { diff --git a/test/runtime/samples/await-set-simultaneous/_config.js b/test/runtime/samples/await-set-simultaneous/_config.js index 97f79f6119..d7e3f12b45 100644 --- a/test/runtime/samples/await-set-simultaneous/_config.js +++ b/test/runtime/samples/await-set-simultaneous/_config.js @@ -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 });