Skip to content

Commit

Permalink
destructure await then and catch
Browse files Browse the repository at this point in the history
  • Loading branch information
tanhauhau committed Mar 13, 2020
1 parent 91d758e commit 96156d0
Show file tree
Hide file tree
Showing 14 changed files with 318 additions and 32 deletions.
54 changes: 49 additions & 5 deletions src/compiler/compile/nodes/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,71 @@ import PendingBlock from './PendingBlock';
import ThenBlock from './ThenBlock';
import CatchBlock from './CatchBlock';
import Expression from './shared/Expression';
import { Pattern } from 'estree';
import Component from '../Component';
import TemplateScope from './shared/TemplateScope';
import { TemplateNode } from '../../interfaces';
import get_object from '../utils/get_object';

export default class AwaitBlock extends Node {
type: 'AwaitBlock';
expression: Expression;
value: string;
error: string;
value: DestructurePattern;
error: DestructurePattern;

pending: PendingBlock;
then: ThenBlock;
catch: CatchBlock;

constructor(component, parent, scope, info) {
constructor(component: Component, parent, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);

this.expression = new Expression(component, this, scope, info.expression);

this.value = info.value;
this.error = info.error;
this.value = info.value && new DestructurePattern(info.value);
this.error = info.error && new DestructurePattern(info.error);

this.pending = new PendingBlock(component, this, scope, info.pending);
this.then = new ThenBlock(component, this, scope, info.then);
this.catch = new CatchBlock(component, this, scope, info.catch);
}
}

export class DestructurePattern {
pattern: Pattern;
expressions: string[];
identifier_name: string | undefined;

constructor(pattern: Pattern) {
this.pattern = pattern;
this.expressions = get_context_from_expression(this.pattern, []);
this.identifier_name = this.pattern.type === 'Identifier' ? this.pattern.name : undefined;
}
}
function get_context_from_expression(node: Pattern, result: string[]): string[] {
switch (node.type) {
case 'Identifier':
result.push(node.name);
return result;
case 'ArrayPattern':
for (const element of node.elements) {
get_context_from_expression(element, result);
}
return result;
case 'ObjectPattern':
for (const property of node.properties) {
get_context_from_expression(property.value, result);
}
return result;
case 'MemberExpression':
get_context_from_expression(get_object(node), result);
return result;
case 'RestElement':
get_context_from_expression(node.argument, result);
return result;
case 'AssignmentPattern':
get_context_from_expression(node.left, result);
return result;
}
}

11 changes: 9 additions & 2 deletions src/compiler/compile/nodes/CatchBlock.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import AwaitBlock from './AwaitBlock';
import Component from '../Component';
import { TemplateNode } from '../../interfaces';

export default class CatchBlock extends AbstractBlock {
type: 'CatchBlock';
scope: TemplateScope;

constructor(component, parent, scope, info) {
constructor(component: Component, parent: AwaitBlock, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);

this.scope = scope.child();
this.scope.add(parent.error, parent.expression.dependencies, this);
if (parent.error) {
parent.error.expressions.forEach(expression => {
this.scope.add(expression, parent.expression.dependencies, this);
});
}
this.children = map_children(component, parent, this.scope, info.children);

if (!info.skip) {
Expand Down
11 changes: 9 additions & 2 deletions src/compiler/compile/nodes/ThenBlock.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import AwaitBlock from './AwaitBlock';
import Component from '../Component';
import { TemplateNode } from '../../interfaces';

export default class ThenBlock extends AbstractBlock {
type: 'ThenBlock';
scope: TemplateScope;

constructor(component, parent, scope, info) {
constructor(component: Component, parent: AwaitBlock, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);

this.scope = scope.child();
this.scope.add(parent.value, parent.expression.dependencies, this);
if (parent.value) {
parent.value.expressions.forEach(expression => {
this.scope.add(expression, parent.expression.dependencies, this);
});
}
this.children = map_children(component, parent, this.scope, info.children);

if (!info.skip) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/compile/render_dom/Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default class Block {
}>;

chunks: {
declarations: Array<Node | Node[]>;
init: Array<Node | Node[]>;
create: Array<Node | Node[]>;
claim: Array<Node | Node[]>;
Expand Down Expand Up @@ -93,6 +94,7 @@ export default class Block {
this.bindings = options.bindings;

this.chunks = {
declarations: [],
init: [],
create: [],
claim: [],
Expand Down Expand Up @@ -384,6 +386,8 @@ export default class Block {
const block = dev && this.get_unique_name('block');

const body = b`
${this.chunks.declarations}
${Array.from(this.variables.values()).map(({ id, init }) => {
return init
? b`let ${id} = ${init}`
Expand Down
107 changes: 88 additions & 19 deletions src/compiler/compile/render_dom/wrappers/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import FragmentWrapper from './Fragment';
import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock';
import { Identifier } from 'estree';
import { Identifier, Pattern } from 'estree';

class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock;
Expand Down Expand Up @@ -46,6 +46,20 @@ class AwaitBlockBranch extends Wrapper {

this.is_dynamic = this.block.dependencies.size > 0;
}

render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
this.fragment.render(block, parent_node, parent_nodes);
}

render_destructure(block: Block, value, node, index) {
if (value && node.pattern.type !== 'Identifier') {
replace_context(block, node.pattern);
this.block.chunks.declarations.push(b`(${node.pattern} = #ctx[${index}])`);
if (this.block.has_update_method) {
this.block.chunks.update.push(b`(${node.pattern} = #ctx[${index}])`);
}
}
}
}

export default class AwaitBlockWrapper extends Wrapper {
Expand All @@ -55,6 +69,9 @@ export default class AwaitBlockWrapper extends Wrapper {
then: AwaitBlockBranch;
catch: AwaitBlockBranch;

value: string;
error: string;

var: Identifier = { type: 'Identifier', name: 'await_block' };

constructor(
Expand All @@ -71,8 +88,20 @@ export default class AwaitBlockWrapper extends Wrapper {
this.not_static_content();

block.add_dependencies(this.node.expression.dependencies);
if (this.node.value) block.renderer.add_to_context(this.node.value, true);
if (this.node.error) block.renderer.add_to_context(this.node.error, true);
if (this.node.value) {
for (const ctx of this.node.value.expressions) {
block.renderer.add_to_context(ctx, true);
}
this.value = this.node.value.identifier_name || block.get_unique_name('value').name;
block.renderer.add_to_context(this.value, true);
}
if (this.node.error) {
for (const ctx of this.node.error.expressions) {
block.renderer.add_to_context(ctx, true);
}
this.error = this.node.error.identifier_name || block.get_unique_name('error').name;
block.renderer.add_to_context(this.error, true);
}

let is_dynamic = false;
let has_intros = false;
Expand Down Expand Up @@ -105,17 +134,11 @@ export default class AwaitBlockWrapper extends Wrapper {
this[status] = branch;
});

this.pending.block.has_update_method = is_dynamic;
this.then.block.has_update_method = is_dynamic;
this.catch.block.has_update_method = is_dynamic;

this.pending.block.has_intro_method = has_intros;
this.then.block.has_intro_method = has_intros;
this.catch.block.has_intro_method = has_intros;

this.pending.block.has_outro_method = has_outros;
this.then.block.has_outro_method = has_outros;
this.catch.block.has_outro_method = has_outros;
['pending', 'then', 'catch'].forEach(status => {
this[status].block.has_update_method = is_dynamic;
this[status].block.has_intro_method = has_intros;
this[status].block.has_outro_method = has_outros;
});

if (has_outros) {
block.add_outro();
Expand All @@ -139,8 +162,8 @@ export default class AwaitBlockWrapper extends Wrapper {

block.maintain_context = true;

const value_index = this.node.value && block.renderer.context_lookup.get(this.node.value).index;
const error_index = this.node.error && block.renderer.context_lookup.get(this.node.error).index;
const value_index = this.value && block.renderer.context_lookup.get(this.value).index;
const error_index = this.error && block.renderer.context_lookup.get(this.error).index;

const info_props: any = x`{
ctx: #ctx,
Expand Down Expand Up @@ -205,7 +228,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} else {
const #child_ctx = #ctx.slice();
${this.node.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${this.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty);
}
`);
Expand All @@ -219,7 +242,7 @@ export default class AwaitBlockWrapper extends Wrapper {
block.chunks.update.push(b`
{
const #child_ctx = #ctx.slice();
${this.node.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${this.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty);
}
`);
Expand All @@ -242,7 +265,53 @@ export default class AwaitBlockWrapper extends Wrapper {
`);

[this.pending, this.then, this.catch].forEach(branch => {
branch.fragment.render(branch.block, null, x`#nodes` as Identifier);
branch.render(branch.block, null, x`#nodes` as Identifier);
});
this.then.render_destructure(block, this.value, this.node.value, value_index);
this.catch.render_destructure(block, this.error, this.node.error, error_index);
}
}

function replace_context(block: Block, pattern: Pattern) {
if (pattern.type === 'ObjectPattern') {
for (const property of pattern.properties) {
if (property.value.type === 'Identifier') {
// @ts-ignore
property.value = x`#ctx[${block.renderer.context_lookup.get(property.value.name).index}]`;
} else {
replace_context(block, property.value);
}
}
} else if (pattern.type === 'ArrayPattern') {
for (let i=0; i<pattern.elements.length; i++) {
const element = pattern.elements[i];
if (element.type === 'Identifier') {
// @ts-ignore
pattern.elements[i] = x`#ctx[${block.renderer.context_lookup.get(element.name).index}]`;
} else {
replace_context(block, element);
}
}
} else if (pattern.type === 'RestElement') {
if (pattern.argument.type === 'Identifier') {
// @ts-ignore
pattern.argument = x`#ctx[${block.renderer.context_lookup.get(pattern.argument.name).index}]`;
} else {
replace_context(block, pattern.argument);
}
} else if (pattern.type === 'AssignmentPattern') {
if (pattern.left.type === 'Identifier') {
// @ts-ignore
pattern.left = x`#ctx[${block.renderer.context_lookup.get(pattern.left.name).index}]`;
} else {
replace_context(block, pattern.left);
}
} else if (pattern.type === 'MemberExpression') {
if (pattern.object.type === 'Identifier') {
pattern.object = x`#ctx[${block.renderer.context_lookup.get(pattern.object.name).index}]`;
} else {
// @ts-ignore
replace_context(block, pattern.object);
}
}
}
2 changes: 1 addition & 1 deletion src/compiler/compile/render_ssr/handlers/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function(node: AwaitBlock, renderer: Renderer, options: RenderOpt
renderer.add_expression(x`
function(__value) {
if (@is_promise(__value)) return ${pending};
return (function(${node.value}) { return ${then}; }(__value));
return (function(${node.value.pattern}) { return ${then}; }(__value));
}(${node.expression.node})
`);
}
Loading

0 comments on commit 96156d0

Please sign in to comment.