Skip to content

Commit

Permalink
dry await destructure
Browse files Browse the repository at this point in the history
  • Loading branch information
tanhauhau committed Mar 25, 2020
1 parent e50ad1d commit 05ef2ab
Show file tree
Hide file tree
Showing 14 changed files with 152 additions and 211 deletions.
39 changes: 20 additions & 19 deletions src/compiler/compile/nodes/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@ 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 traverse_destructure_pattern from '../utils/traverse_destructure_pattern';
import { Context, unpack_destructuring } from './shared/Context';
import { Node as ESTreeNode } from 'estree';

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

value_contexts: Context[];
error_contexts: Context[];

value_node: ESTreeNode | null;
error_node: ESTreeNode | null;

pending: PendingBlock;
then: ThenBlock;
Expand All @@ -24,24 +28,21 @@ export default class AwaitBlock extends Node {

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

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

if (this.value_node) {
this.value_contexts = [];
unpack_destructuring(this.value_contexts, info.value, node => node);
}

if (this.error_node) {
this.error_contexts = [];
unpack_destructuring(this.error_contexts, info.error, node => node);
}

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 = [];
traverse_destructure_pattern(pattern, (node) => this.expressions.push(node.name));
this.identifier_name = this.pattern.type === 'Identifier' ? this.pattern.name : undefined;
}
}
6 changes: 3 additions & 3 deletions src/compiler/compile/nodes/CatchBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export default class CatchBlock extends AbstractBlock {
super(component, parent, scope, info);

this.scope = scope.child();
if (parent.error) {
parent.error.expressions.forEach(expression => {
this.scope.add(expression, parent.expression.dependencies, this);
if (parent.error_node) {
parent.error_contexts.forEach(context => {
this.scope.add(context.key.name, parent.expression.dependencies, this);
});
}
this.children = map_children(component, parent, this.scope, info.children);
Expand Down
52 changes: 2 additions & 50 deletions src/compiler/compile/nodes/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,8 @@ import map_children from './shared/map_children';
import TemplateScope from './shared/TemplateScope';
import AbstractBlock from './shared/AbstractBlock';
import Element from './Element';
import { x } from 'code-red';
import { Node, Identifier, RestElement } from 'estree';

interface Context {
key: Identifier;
name?: string;
modifier: (node: Node) => Node;
}

function unpack_destructuring(contexts: Context[], node: Node, modifier: (node: Node) => Node) {
if (!node) return;

if (node.type === 'Identifier' || (node as any).type === 'RestIdentifier') { // TODO is this right? not RestElement?
contexts.push({
key: node as Identifier,
modifier
});
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
if (element && (element as any).type === 'RestIdentifier') {
unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node);
} else {
unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node);
}
});
} else if (node.type === 'ObjectPattern') {
const used_properties = [];

node.properties.forEach((property, i) => {
if ((property as any).kind === 'rest') { // TODO is this right?
const replacement: RestElement = {
type: 'RestElement',
argument: property.key as Identifier
};

node.properties[i] = replacement as any;

unpack_destructuring(
contexts,
property.value,
node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node
);
} else {
used_properties.push(x`"${(property.key as Identifier).name}"`);

unpack_destructuring(contexts, property.value, node => x`${modifier(node)}.${(property.key as Identifier).name}` as Node);
}
});
}
}
import { Context, unpack_destructuring } from './shared/Context';
import { Node } from 'estree';

export default class EachBlock extends AbstractBlock {
type: 'EachBlock';
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/compile/nodes/ThenBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export default class ThenBlock extends AbstractBlock {
super(component, parent, scope, info);

this.scope = scope.child();
if (parent.value) {
parent.value.expressions.forEach(expression => {
this.scope.add(expression, parent.expression.dependencies, this);
if (parent.value_node) {
parent.value_contexts.forEach(context => {
this.scope.add(context.key.name, parent.expression.dependencies, this);
});
}
this.children = map_children(component, parent, this.scope, info.children);
Expand Down
50 changes: 50 additions & 0 deletions src/compiler/compile/nodes/shared/Context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { x } from 'code-red';
import { Node, Identifier, RestElement, Property } from 'estree';

export interface Context {
key: Identifier;
name?: string;
modifier: (node: Node) => Node;
}

export function unpack_destructuring(contexts: Context[], node: Node, modifier: (node: Node) => Node) {
if (!node) return;

if (node.type === 'Identifier') {
contexts.push({
key: node as Identifier,
modifier
});
} else if (node.type === 'RestElement') {
contexts.push({
key: node.argument as Identifier,
modifier
});
} else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
if (element && (element as any).type === 'RestElement') {
unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node);
} else {
unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node);
}
});
} else if (node.type === 'ObjectPattern') {
const used_properties = [];

node.properties.forEach((property) => {
const props: (RestElement | Property) = (property as any);

if (props.type === 'RestElement') {
unpack_destructuring(
contexts,
props.argument,
node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node
);
} else {
used_properties.push(x`"${(property.key as Identifier).name}"`);

unpack_destructuring(contexts, property.value, node => x`${modifier(node)}.${(property.key as Identifier).name}` as Node);
}
});
}
}
85 changes: 48 additions & 37 deletions src/compiler/compile/render_dom/wrappers/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import FragmentWrapper from './Fragment';
import PendingBlock from '../../nodes/PendingBlock';
import ThenBlock from '../../nodes/ThenBlock';
import CatchBlock from '../../nodes/CatchBlock';
import { Identifier } from 'estree';
import traverse_destructure_pattern from '../../utils/traverse_destructure_pattern';
import { Context } from '../../nodes/shared/Context';
import { Identifier, Literal, Node } from 'estree';

class AwaitBlockBranch extends Wrapper {
node: PendingBlock | ThenBlock | CatchBlock;
Expand All @@ -18,6 +18,12 @@ class AwaitBlockBranch extends Wrapper {
is_dynamic: boolean;

var = null;
status: string;

value: string;
value_index: Literal;
value_contexts: Context[];
is_destructured: boolean;

constructor(
status: string,
Expand All @@ -29,6 +35,7 @@ class AwaitBlockBranch extends Wrapper {
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.status = status;

this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component),
Expand All @@ -48,20 +55,43 @@ class AwaitBlockBranch extends Wrapper {
this.is_dynamic = this.block.dependencies.size > 0;
}

add_context(node: Node | null, contexts: Context[]) {
if (!node) return;

if (node.type === 'Identifier') {
this.value = node.name;
this.block.renderer.add_to_context(this.value, true);
} else {
contexts.forEach(context => {
this.block.renderer.add_to_context(context.key.name, true);
});
this.value = this.block.parent.get_unique_name('value').name;
this.value_contexts = contexts;
this.block.renderer.add_to_context(this.value, true);
this.is_destructured = true;
}
this.value_index = this.block.renderer.context_lookup.get(this.value).index;
}

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') {
traverse_destructure_pattern(node.pattern, (node, parent, index) => {
parent[index] = x`#ctx[${block.renderer.context_lookup.get(node.name).index}]`;
});
if (this.is_destructured) {
this.render_destructure();
}
}

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}])`);
render_destructure() {
const props = this.value_contexts.map(prop => b`#ctx[${this.block.renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`#ctx[${this.value_index}]`)};`);
const get_context = this.block.renderer.component.get_unique_name(`get_${this.status}_context`);
this.block.renderer.blocks.push(b`
function ${get_context}(#ctx) {
${props}
}
`);
this.block.chunks.declarations.push(b`${get_context}(#ctx)`);
if (this.block.has_update_method) {
this.block.chunks.update.push(b`${get_context}(#ctx)`);
}
}
}
Expand All @@ -73,9 +103,6 @@ export default class AwaitBlockWrapper extends Wrapper {
then: AwaitBlockBranch;
catch: AwaitBlockBranch;

value: string;
error: string;

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

constructor(
Expand All @@ -92,20 +119,6 @@ export default class AwaitBlockWrapper extends Wrapper {
this.not_static_content();

block.add_dependencies(this.node.expression.dependencies);
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 @@ -144,6 +157,9 @@ export default class AwaitBlockWrapper extends Wrapper {
this[status].block.has_outro_method = has_outros;
});

this.then.add_context(this.node.value_node, this.node.value_contexts);
this.catch.add_context(this.node.error_node, this.node.error_contexts);

if (has_outros) {
block.add_outro();
}
Expand All @@ -166,18 +182,15 @@ export default class AwaitBlockWrapper extends Wrapper {

block.maintain_context = true;

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,
current: null,
token: null,
pending: ${this.pending.block.name},
then: ${this.then.block.name},
catch: ${this.catch.block.name},
value: ${value_index},
error: ${error_index},
value: ${this.then.value_index},
error: ${this.catch.value_index},
blocks: ${this.pending.block.has_outro_method && x`[,,,]`}
}`;

Expand Down Expand Up @@ -232,7 +245,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} else {
const #child_ctx = #ctx.slice();
${this.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${this.then.value && b`#child_ctx[${this.then.value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty);
}
`);
Expand All @@ -246,7 +259,7 @@ export default class AwaitBlockWrapper extends Wrapper {
block.chunks.update.push(b`
{
const #child_ctx = #ctx.slice();
${this.value && b`#child_ctx[${value_index}] = ${info}.resolved;`}
${this.then.value && b`#child_ctx[${this.then.value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty);
}
`);
Expand All @@ -271,7 +284,5 @@ export default class AwaitBlockWrapper extends Wrapper {
[this.pending, this.then, this.catch].forEach(branch => {
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);
}
}
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 ? node.value.pattern : ''}) { return ${then}; }(__value));
return (function(${node.value_node ? node.value_node : ''}) { return ${then}; }(__value));
}(${node.expression.node})
`);
}
Loading

0 comments on commit 05ef2ab

Please sign in to comment.