Fixed issue with computed properties in object destructuring

pull/8386/head
Nguyen Tran 4 years ago
parent 8ddefd90e0
commit 8e3d500f84

@ -35,12 +35,12 @@ export default class AwaitBlock extends Node {
if (this.then_node) { if (this.then_node) {
this.then_contexts = []; this.then_contexts = [];
unpack_destructuring({ contexts: this.then_contexts, node: info.value, scope, component, context_rest_properties: this.context_rest_properties }); unpack_destructuring({ contexts: this.then_contexts, node: info.value, scope, component, context_rest_properties: this.context_rest_properties, owner: this });
} }
if (this.catch_node) { if (this.catch_node) {
this.catch_contexts = []; this.catch_contexts = [];
unpack_destructuring({ contexts: this.catch_contexts, node: info.error, scope, component, context_rest_properties: this.context_rest_properties }); unpack_destructuring({ contexts: this.catch_contexts, node: info.error, scope, component, context_rest_properties: this.context_rest_properties, owner: this });
} }
this.pending = new PendingBlock(component, this, scope, info.pending); this.pending = new PendingBlock(component, this, scope, info.pending);

@ -17,6 +17,7 @@ export default class CatchBlock extends AbstractBlock {
this.scope = scope.child(); this.scope = scope.child();
if (parent.catch_node) { if (parent.catch_node) {
parent.catch_contexts.forEach(context => { parent.catch_contexts.forEach(context => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this); this.scope.add(context.key.name, parent.expression.dependencies, this);
}); });
} }

@ -61,10 +61,12 @@ export default class ConstTag extends Node {
node: this.node.expression.left, node: this.node.expression.left,
scope: this.scope, scope: this.scope,
component: this.component, component: this.component,
context_rest_properties: this.context_rest_properties context_rest_properties: this.context_rest_properties,
owner: this
}); });
this.expression = new Expression(this.component, this, this.scope, this.node.expression.right); this.expression = new Expression(this.component, this, this.scope, this.node.expression.right);
this.contexts.forEach(context => { this.contexts.forEach(context => {
if (context.type !== 'DestructuredVariable') return;
const owner = this.scope.get_owner(context.key.name); const owner = this.scope.get_owner(context.key.name);
if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) { if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) {
this.component.error(this.node, compiler_errors.invalid_const_declaration(context.key.name)); this.component.error(this.node, compiler_errors.invalid_const_declaration(context.key.name));

@ -42,10 +42,12 @@ export default class EachBlock extends AbstractBlock {
this.scope = scope.child(); this.scope = scope.child();
this.context_rest_properties = new Map(); this.context_rest_properties = new Map();
this.contexts = []; this.contexts = [];
unpack_destructuring({ contexts: this.contexts, node: info.context, scope, component, context_rest_properties: this.context_rest_properties }); unpack_destructuring({ contexts: this.contexts, node: info.context, scope, component, owner: this, context_rest_properties: this.context_rest_properties });
this.contexts.forEach(context => { this.contexts.forEach(context => {
if (context.type === 'DestructuredVariable') {
this.scope.add(context.key.name, this.expression.dependencies, this); this.scope.add(context.key.name, this.expression.dependencies, this);
}
}); });
if (this.index) { if (this.index) {

@ -17,6 +17,7 @@ export default class ThenBlock extends AbstractBlock {
this.scope = scope.child(); this.scope = scope.child();
if (parent.then_node) { if (parent.then_node) {
parent.then_contexts.forEach(context => { parent.then_contexts.forEach(context => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this); this.scope.add(context.key.name, parent.expression.dependencies, this);
}); });
} }

@ -1,13 +1,24 @@
import { x } from 'code-red'; import { b, x } from 'code-red';
import { Node, Identifier, Expression } from 'estree'; import { Node, Identifier, Expression as ESTreeExpression } from 'estree';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import is_reference, { NodeWithPropertyDefinition } from 'is-reference'; import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
import { clone } from '../../../utils/clone'; import { clone } from '../../../utils/clone';
import Component from '../../Component'; import Component from '../../Component';
import Block from '../../render_dom/Block';
import flatten_reference from '../../utils/flatten_reference'; import flatten_reference from '../../utils/flatten_reference';
import { INode } from '../interfaces';
import Expression from './Expression';
import TemplateScope from './TemplateScope'; import TemplateScope from './TemplateScope';
export interface Context { export type Context = DestructuredVariable | ComputedProperty;
interface ComputedProperty {
type: 'ComputedProperty';
declaration: (block: Block, scope: TemplateScope, ctx: string) => Node[];
}
interface DestructuredVariable {
type: 'DestructuredVariable'
key: Identifier; key: Identifier;
name?: string; name?: string;
modifier: (node: Node) => Node; modifier: (node: Node) => Node;
@ -21,26 +32,32 @@ export function unpack_destructuring({
default_modifier = (node) => node, default_modifier = (node) => node,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props = 0
}: { }: {
contexts: Context[]; contexts: Context[];
node: Node; node: Node;
modifier?: Context['modifier']; modifier?: DestructuredVariable['modifier'];
default_modifier?: Context['default_modifier']; default_modifier?: DestructuredVariable['default_modifier'];
scope: TemplateScope; scope: TemplateScope;
component: Component; component: Component;
context_rest_properties: Map<string, Node>; context_rest_properties: Map<string, Node>;
owner: INode;
number_of_computed_props?: number;
}) { }) {
if (!node) return; if (!node) return;
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
contexts.push({ contexts.push({
type: 'DestructuredVariable',
key: node as Identifier, key: node as Identifier,
modifier, modifier,
default_modifier default_modifier
}); });
} else if (node.type === 'RestElement') { } else if (node.type === 'RestElement') {
contexts.push({ contexts.push({
type: 'DestructuredVariable',
key: node.argument as Identifier, key: node.argument as Identifier,
modifier, modifier,
default_modifier default_modifier
@ -56,7 +73,9 @@ export function unpack_destructuring({
default_modifier, default_modifier,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props
}); });
context_rest_properties.set((element.argument as Identifier).name, element); context_rest_properties.set((element.argument as Identifier).name, element);
} else if (element && element.type === 'AssignmentPattern') { } else if (element && element.type === 'AssignmentPattern') {
@ -76,7 +95,9 @@ export function unpack_destructuring({
)}` as Node, )}` as Node,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props
}); });
} else { } else {
unpack_destructuring({ unpack_destructuring({
@ -86,7 +107,9 @@ export function unpack_destructuring({
default_modifier, default_modifier,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props
}); });
} }
}); });
@ -105,29 +128,43 @@ export function unpack_destructuring({
default_modifier, default_modifier,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props
}); });
context_rest_properties.set((property.argument as Identifier).name, property); context_rest_properties.set((property.argument as Identifier).name, property);
} else if (property.type === 'Property') { } else if (property.type === 'Property') {
const key = property.key; const key = property.key;
const value = property.value; const value = property.value;
let property_name: any;
let new_modifier: (node: Node) => Node; let new_modifier: (node: Node) => Node;
if (property.computed) { if (property.computed) {
// TODO: If the property is computed, ie, { [computed_key]: prop }, the computed_key can be any type of expression. // TODO: If the property is computed, ie, { [computed_key]: prop }, the computed_key can be any type of expression.
const computed_property = `#computed_property_${number_of_computed_props}`;
new_modifier = (node) => x`${modifier(node)}[${computed_property}]`;
used_properties.push(x`${computed_property}`);
number_of_computed_props += 1;
contexts.push({
type: 'ComputedProperty',
declaration: (block, scope, ctx) => {
const computed_expression = new Expression(component, owner, scope, key);
return b`const ${computed_property} = ${computed_expression.manipulate(block, ctx)}`;
}
});
} else if (key.type === 'Identifier') { } else if (key.type === 'Identifier') {
// e.g. { someProperty: ... } // e.g. { someProperty: ... }
property_name = key.name; const property_name = key.name;
new_modifier = (node) => x`${modifier(node)}.${property_name}`; new_modifier = (node) => x`${modifier(node)}.${property_name}`;
used_properties.push(x`"${property_name}"`);
} else if (key.type === 'Literal') { } else if (key.type === 'Literal') {
// e.g. { "property-in-quotes": ... } or { 14: ... } // e.g. { "property-in-quotes": ... } or { 14: ... }
property_name = key.value; const property_name = key.value;
new_modifier = (node) => x`${modifier(node)}["${property_name}"]`; new_modifier = (node) => x`${modifier(node)}["${property_name}"]`;
used_properties.push(x`"${property_name}"`);
} }
used_properties.push(x`"${property_name}"`);
if (value.type === 'AssignmentPattern') { if (value.type === 'AssignmentPattern') {
// e.g. { property = default } or { property: newName = default } // e.g. { property = default } or { property: newName = default }
const n = contexts.length; const n = contexts.length;
@ -147,7 +184,9 @@ export function unpack_destructuring({
)}` as Node, )}` as Node,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props
}); });
} else { } else {
// e.g. { property } or { property: newName } // e.g. { property } or { property: newName }
@ -158,7 +197,9 @@ export function unpack_destructuring({
default_modifier, default_modifier,
scope, scope,
component, component,
context_rest_properties context_rest_properties,
owner,
number_of_computed_props
}); });
} }
} }
@ -169,16 +210,19 @@ export function unpack_destructuring({
function update_reference( function update_reference(
contexts: Context[], contexts: Context[],
n: number, n: number,
expression: Expression, expression: ESTreeExpression,
to_ctx: (name: string) => Node to_ctx: (name: string) => Node
): Node { ): Node {
const find_from_context = (node: Identifier) => { const find_from_context = (node: Identifier) => {
for (let i = n; i < contexts.length; i++) { for (let i = n; i < contexts.length; i++) {
const { key } = contexts[i]; const cur_context = contexts[i];
if (cur_context.type === 'DestructuredVariable') {
const { key } = cur_context;
if (node.name === key.name) { if (node.name === key.name) {
throw new Error(`Cannot access '${node.name}' before initialization`); throw new Error(`Cannot access '${node.name}' before initialization`);
} }
} }
}
return to_ctx(node.name); return to_ctx(node.name);
}; };
@ -187,7 +231,7 @@ function update_reference(
} }
// NOTE: avoid unnecessary deep clone? // NOTE: avoid unnecessary deep clone?
expression = clone(expression) as Expression; expression = clone(expression) as ESTreeExpression;
walk(expression, { walk(expression, {
enter(node, parent: Node) { enter(node, parent: Node) {
if ( if (

@ -373,6 +373,7 @@ export default class Expression {
// add to get_xxx_context // add to get_xxx_context
// child_ctx[x] = function () { ... } // child_ctx[x] = function () { ... }
(template_scope.get_owner(deps[0]) as EachBlock).contexts.push({ (template_scope.get_owner(deps[0]) as EachBlock).contexts.push({
type: 'DestructuredVariable',
key: func_id, key: func_id,
modifier: () => func_expression, modifier: () => func_expression,
default_modifier: node => node default_modifier: node => node

@ -69,7 +69,9 @@ class AwaitBlockBranch extends Wrapper {
this.renderer.add_to_context(this.value, true); this.renderer.add_to_context(this.value, true);
} else { } else {
contexts.forEach(context => { contexts.forEach(context => {
if (context.type === 'DestructuredVariable') {
this.renderer.add_to_context(context.key.name, true); this.renderer.add_to_context(context.key.name, true);
}
}); });
this.value = this.block.parent.get_unique_name('value').name; this.value = this.block.parent.get_unique_name('value').name;
this.value_contexts = contexts; this.value_contexts = contexts;
@ -96,7 +98,14 @@ class AwaitBlockBranch extends Wrapper {
} }
render_get_context() { render_get_context() {
const props = this.is_destructured ? this.value_contexts.map(prop => b`#ctx[${this.block.renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x`#ctx[${this.value_index}]`), name => this.renderer.reference(name))};`) : null; const props = this.is_destructured ? this.value_contexts.map(prop => {
const to_ctx = name => this.renderer.reference(name);
if (prop.type === 'ComputedProperty') {
return prop.declaration(this.block, this.has_consts(this.node) ? this.node.scope : null, '#ctx');
} else {
return b`#ctx[${this.block.renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x`#ctx[${this.value_index}]`), to_ctx)};`;
}
}) : null;
const const_tags_props = this.has_consts(this.node) ? add_const_tags(this.block, this.node.const_tags, '#ctx') : null; const const_tags_props = this.has_consts(this.node) ? add_const_tags(this.block, this.node.const_tags, '#ctx') : null;

@ -86,6 +86,7 @@ export default class EachBlockWrapper extends Wrapper {
block.add_dependencies(dependencies); block.add_dependencies(dependencies);
this.node.contexts.forEach(context => { this.node.contexts.forEach(context => {
if (context.type !== 'DestructuredVariable') return;
renderer.add_to_context(context.key.name, true); renderer.add_to_context(context.key.name, true);
}); });
add_const_tags_context(renderer, this.node.const_tags); add_const_tags_context(renderer, this.node.const_tags);
@ -147,6 +148,7 @@ export default class EachBlockWrapper extends Wrapper {
const store = object.type === 'Identifier' && object.name[0] === '$' ? object.name.slice(1) : null; const store = object.type === 'Identifier' && object.name[0] === '$' ? object.name.slice(1) : null;
node.contexts.forEach(prop => { node.contexts.forEach(prop => {
if (prop.type !== 'DestructuredVariable') return;
this.block.bindings.set(prop.key.name, { this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value, object: this.vars.each_block_value,
property: this.index_name, property: this.index_name,
@ -361,7 +363,15 @@ export default class EachBlockWrapper extends Wrapper {
this.else.fragment.render(this.else.block, null, x`#nodes` as Identifier); this.else.fragment.render(this.else.block, null, x`#nodes` as Identifier);
} }
this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x`list[i]`), name => renderer.context_lookup.has(name) ? x`child_ctx[${renderer.context_lookup.get(name).index}]` : { type: 'Identifier', name })};`); this.context_props = this.node.contexts.map(prop => {
const to_ctx = (name: string) => renderer.context_lookup.has(name) ? x`child_ctx[${renderer.context_lookup.get(name).index}]` : { type: 'Identifier', name } as Node;
if (prop.type === 'DestructuredVariable') {
return b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x`list[i]`), to_ctx)};`;
} else {
return prop.declaration(block, this.node.scope, 'child_ctx');
}
});
if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`); if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`);
if (this.node.has_binding || this.node.has_index_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`); if (this.node.has_binding || this.node.has_index_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`);

@ -1,6 +1,6 @@
import ConstTag from '../../../nodes/ConstTag'; import ConstTag from '../../../nodes/ConstTag';
import Block from '../../Block'; import Block from '../../Block';
import { b, x } from 'code-red'; import { b, Node, x } from 'code-red';
import Renderer from '../../Renderer'; import Renderer from '../../Renderer';
export function add_const_tags(block: Block, const_tags: ConstTag[], ctx: string) { export function add_const_tags(block: Block, const_tags: ConstTag[], ctx: string) {
@ -8,8 +8,14 @@ export function add_const_tags(block: Block, const_tags: ConstTag[], ctx: string
const_tags.forEach((const_tag, i) => { const_tags.forEach((const_tag, i) => {
const name = `#constants_${i}`; const name = `#constants_${i}`;
const_tags_props.push(b`const ${name} = ${const_tag.expression.manipulate(block, ctx)}`); const_tags_props.push(b`const ${name} = ${const_tag.expression.manipulate(block, ctx)}`);
const to_ctx = (name: string) => block.renderer.context_lookup.has(name) ? x`${ctx}[${block.renderer.context_lookup.get(name).index}]` : { type: 'Identifier', name } as Node;
const_tag.contexts.forEach(context => { const_tag.contexts.forEach(context => {
const_tags_props.push(b`${ctx}[${block.renderer.context_lookup.get(context.key.name).index}] = ${context.default_modifier(context.modifier({ type: 'Identifier', name }), name => block.renderer.context_lookup.has(name) ? x`${ctx}[${block.renderer.context_lookup.get(name).index}]` : { type: 'Identifier', name })};`); if (context.type === 'DestructuredVariable') {
const_tags_props.push(b`${ctx}[${block.renderer.context_lookup.get(context.key.name).index}] = ${context.default_modifier(context.modifier({ type: 'Identifier', name }), to_ctx)}`);
} else {
const_tags_props.push(context.declaration(block, const_tag.scope, ctx));
}
}); });
}); });
return const_tags_props; return const_tags_props;
@ -18,6 +24,7 @@ export function add_const_tags(block: Block, const_tags: ConstTag[], ctx: string
export function add_const_tags_context(renderer: Renderer, const_tags: ConstTag[]) { export function add_const_tags_context(renderer: Renderer, const_tags: ConstTag[]) {
const_tags.forEach(const_tag => { const_tags.forEach(const_tag => {
const_tag.contexts.forEach(context => { const_tag.contexts.forEach(context => {
if (context.type !== 'DestructuredVariable') return;
renderer.add_to_context(context.key.name, true); renderer.add_to_context(context.key.name, true);
}); });
}); });

Loading…
Cancel
Save