|
|
|
@ -1,7 +1,6 @@
|
|
|
|
|
import Component from '../../Component';
|
|
|
|
|
import { walk } from 'estree-walker';
|
|
|
|
|
import is_reference from 'is-reference';
|
|
|
|
|
// import { b } from 'code-red';
|
|
|
|
|
import flatten_reference from '../../utils/flatten_reference';
|
|
|
|
|
import { create_scopes, Scope, extract_names } from '../../utils/scope';
|
|
|
|
|
import { Node } from '../../../interfaces';
|
|
|
|
@ -12,7 +11,8 @@ import get_object from '../../utils/get_object';
|
|
|
|
|
import Block from '../../render_dom/Block';
|
|
|
|
|
import { INode } from '../interfaces';
|
|
|
|
|
import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic';
|
|
|
|
|
// import { invalidate } from '../../utils/invalidate';
|
|
|
|
|
import { x } from 'code-red';
|
|
|
|
|
import { invalidate } from '../../utils/invalidate';
|
|
|
|
|
|
|
|
|
|
const binary_operators: Record<string, number> = {
|
|
|
|
|
'**': 15,
|
|
|
|
@ -82,8 +82,6 @@ export default class Expression {
|
|
|
|
|
declarations: Node[] = [];
|
|
|
|
|
uses_context = false;
|
|
|
|
|
|
|
|
|
|
rendered: string;
|
|
|
|
|
|
|
|
|
|
// todo: owner type
|
|
|
|
|
constructor(component: Component, owner: Owner, template_scope: TemplateScope, info, lazy?: boolean) {
|
|
|
|
|
// TODO revert to direct property access in prod?
|
|
|
|
@ -224,86 +222,85 @@ export default class Expression {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO move this into a render-dom wrapper?
|
|
|
|
|
render(_block?: Block) {
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
|
|
// if (this.rendered) return this.rendered;
|
|
|
|
|
|
|
|
|
|
// const {
|
|
|
|
|
// component,
|
|
|
|
|
// declarations,
|
|
|
|
|
// scope_map: map,
|
|
|
|
|
// template_scope,
|
|
|
|
|
// owner,
|
|
|
|
|
// is_synthetic
|
|
|
|
|
// } = this;
|
|
|
|
|
// let scope = this.scope;
|
|
|
|
|
|
|
|
|
|
// let function_expression;
|
|
|
|
|
|
|
|
|
|
// let dependencies: Set<string>;
|
|
|
|
|
// let contextual_dependencies: Set<string>;
|
|
|
|
|
|
|
|
|
|
// // rewrite code as appropriate
|
|
|
|
|
// walk(this.node, {
|
|
|
|
|
// enter(node: any, parent: any, key: string) {
|
|
|
|
|
// // don't manipulate shorthand props twice
|
|
|
|
|
// if (key === 'value' && parent.shorthand) return;
|
|
|
|
|
|
|
|
|
|
// if (map.has(node)) {
|
|
|
|
|
// scope = map.get(node);
|
|
|
|
|
// }
|
|
|
|
|
manipulate(block?: Block) {
|
|
|
|
|
const {
|
|
|
|
|
component,
|
|
|
|
|
declarations,
|
|
|
|
|
scope_map: map,
|
|
|
|
|
template_scope,
|
|
|
|
|
is_synthetic
|
|
|
|
|
} = this;
|
|
|
|
|
let scope = this.scope;
|
|
|
|
|
|
|
|
|
|
// if (is_reference(node, parent)) {
|
|
|
|
|
// const { name, nodes } = flatten_reference(node);
|
|
|
|
|
let function_expression;
|
|
|
|
|
|
|
|
|
|
// if (scope.has(name)) return;
|
|
|
|
|
// if (globals.has(name) && !component.var_lookup.has(name)) return;
|
|
|
|
|
let dependencies: Set<string>;
|
|
|
|
|
let contextual_dependencies: Set<string>;
|
|
|
|
|
|
|
|
|
|
// if (function_expression) {
|
|
|
|
|
// if (template_scope.names.has(name)) {
|
|
|
|
|
// contextual_dependencies.add(name);
|
|
|
|
|
// TODO this feels a lil messy
|
|
|
|
|
let return_value = this.node;
|
|
|
|
|
|
|
|
|
|
// template_scope.dependencies_for_name.get(name).forEach(dependency => {
|
|
|
|
|
// dependencies.add(dependency);
|
|
|
|
|
// });
|
|
|
|
|
// } else {
|
|
|
|
|
// dependencies.add(name);
|
|
|
|
|
// component.add_reference(name); // TODO is this redundant/misplaced?
|
|
|
|
|
// }
|
|
|
|
|
// } else if (!is_synthetic && is_contextual(component, template_scope, name)) {
|
|
|
|
|
// code.prependRight(node.start, key === 'key' && parent.shorthand
|
|
|
|
|
// ? `${name}: ctx.`
|
|
|
|
|
// : 'ctx.');
|
|
|
|
|
// }
|
|
|
|
|
walk(this.node, {
|
|
|
|
|
enter(node: any, parent: any, key: string, index: number) {
|
|
|
|
|
// don't manipulate shorthand props twice
|
|
|
|
|
if (key === 'value' && parent.shorthand) return;
|
|
|
|
|
|
|
|
|
|
// if (node.type === 'MemberExpression') {
|
|
|
|
|
// nodes.forEach(node => {
|
|
|
|
|
// code.addSourcemapLocation(node.start);
|
|
|
|
|
// code.addSourcemapLocation(node.end);
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
if (map.has(node)) {
|
|
|
|
|
scope = map.get(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this.skip();
|
|
|
|
|
// }
|
|
|
|
|
if (is_reference(node, parent)) {
|
|
|
|
|
const { name } = flatten_reference(node);
|
|
|
|
|
|
|
|
|
|
// if (!function_expression) {
|
|
|
|
|
// if (node.type === 'AssignmentExpression') {
|
|
|
|
|
// // TODO should this be a warning/error? `<p>{foo = 1}</p>`
|
|
|
|
|
// }
|
|
|
|
|
if (scope.has(name)) return;
|
|
|
|
|
if (globals.has(name) && !component.var_lookup.has(name)) return;
|
|
|
|
|
|
|
|
|
|
// if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
|
|
|
// function_expression = node;
|
|
|
|
|
// dependencies = new Set();
|
|
|
|
|
// contextual_dependencies = new Set();
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// },
|
|
|
|
|
if (function_expression) {
|
|
|
|
|
if (template_scope.names.has(name)) {
|
|
|
|
|
contextual_dependencies.add(name);
|
|
|
|
|
|
|
|
|
|
template_scope.dependencies_for_name.get(name).forEach(dependency => {
|
|
|
|
|
dependencies.add(dependency);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
dependencies.add(name);
|
|
|
|
|
component.add_reference(name); // TODO is this redundant/misplaced?
|
|
|
|
|
}
|
|
|
|
|
} else if (!is_synthetic && is_contextual(component, template_scope, name)) {
|
|
|
|
|
const replaced = x`ctx.${node}`;
|
|
|
|
|
|
|
|
|
|
if (parent) {
|
|
|
|
|
if (index !== null) {
|
|
|
|
|
// TODO can this happen?
|
|
|
|
|
parent[key][index] = replaced;
|
|
|
|
|
} else {
|
|
|
|
|
parent[key] = replaced;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return_value = replaced;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.skip();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!function_expression) {
|
|
|
|
|
if (node.type === 'AssignmentExpression') {
|
|
|
|
|
// TODO should this be a warning/error? `<p>{foo = 1}</p>`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
|
|
|
function_expression = node;
|
|
|
|
|
dependencies = new Set();
|
|
|
|
|
contextual_dependencies = new Set();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// leave(node: Node, parent: Node) {
|
|
|
|
|
// if (map.has(node)) scope = scope.parent;
|
|
|
|
|
leave(node: Node, _parent: Node) {
|
|
|
|
|
if (map.has(node)) scope = scope.parent;
|
|
|
|
|
|
|
|
|
|
// if (node === function_expression) {
|
|
|
|
|
if (node === function_expression) {
|
|
|
|
|
// const id = component.get_unique_name(
|
|
|
|
|
// sanitize(get_function_name(node, owner))
|
|
|
|
|
// );
|
|
|
|
@ -328,7 +325,8 @@ export default class Expression {
|
|
|
|
|
// if (dependencies.size === 0 && contextual_dependencies.size === 0) {
|
|
|
|
|
// // we can hoist this out of the component completely
|
|
|
|
|
// component.fully_hoisted.push(fn);
|
|
|
|
|
// code.overwrite(node.start, node.end, id.name);
|
|
|
|
|
|
|
|
|
|
// // code.overwrite(node.start, node.end, id.name);
|
|
|
|
|
|
|
|
|
|
// component.add_var({
|
|
|
|
|
// name: id.name,
|
|
|
|
@ -372,45 +370,43 @@ export default class Expression {
|
|
|
|
|
// code.prependRight(node.start, ': ');
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// function_expression = null;
|
|
|
|
|
// dependencies = null;
|
|
|
|
|
// contextual_dependencies = null;
|
|
|
|
|
// }
|
|
|
|
|
function_expression = null;
|
|
|
|
|
dependencies = null;
|
|
|
|
|
contextual_dependencies = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
|
|
|
|
|
// const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
|
|
|
|
|
|
|
|
|
|
// // normally (`a = 1`, `b.c = 2`), there'll be a single name
|
|
|
|
|
// // (a or b). In destructuring cases (`[d, e] = [e, d]`) there
|
|
|
|
|
// // may be more, in which case we need to tack the extra ones
|
|
|
|
|
// // onto the initial function call
|
|
|
|
|
// const names = new Set(extract_names(assignee));
|
|
|
|
|
|
|
|
|
|
// const traced: Set<string> = new Set();
|
|
|
|
|
// names.forEach(name => {
|
|
|
|
|
// const dependencies = template_scope.dependencies_for_name.get(name);
|
|
|
|
|
// if (dependencies) {
|
|
|
|
|
// dependencies.forEach(name => traced.add(name));
|
|
|
|
|
// } else {
|
|
|
|
|
// traced.add(name);
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
|
|
|
|
|
const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument;
|
|
|
|
|
|
|
|
|
|
// invalidate(component, scope, code, node, traced);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
// normally (`a = 1`, `b.c = 2`), there'll be a single name
|
|
|
|
|
// (a or b). In destructuring cases (`[d, e] = [e, d]`) there
|
|
|
|
|
// may be more, in which case we need to tack the extra ones
|
|
|
|
|
// onto the initial function call
|
|
|
|
|
const names = new Set(extract_names(assignee));
|
|
|
|
|
|
|
|
|
|
// if (declarations.length > 0) {
|
|
|
|
|
// block.maintain_context = true;
|
|
|
|
|
// declarations.forEach(declaration => {
|
|
|
|
|
// block.chunks.init.push(declaration);
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
const traced: Set<string> = new Set();
|
|
|
|
|
names.forEach(name => {
|
|
|
|
|
const dependencies = template_scope.dependencies_for_name.get(name);
|
|
|
|
|
if (dependencies) {
|
|
|
|
|
dependencies.forEach(name => traced.add(name));
|
|
|
|
|
} else {
|
|
|
|
|
traced.add(name);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
invalidate(component, scope, node, traced);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
throw new Error(`bad`);
|
|
|
|
|
if (declarations.length > 0) {
|
|
|
|
|
block.maintain_context = true;
|
|
|
|
|
declarations.forEach(declaration => {
|
|
|
|
|
block.chunks.init.push(declaration);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.rendered = `[✂${this.node.start}-${this.node.end}✂]`;
|
|
|
|
|
return return_value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -426,17 +422,17 @@ export default class Expression {
|
|
|
|
|
// return 'func';
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// function is_contextual(component: Component, scope: TemplateScope, name: string) {
|
|
|
|
|
// if (name === '$$props') return true;
|
|
|
|
|
function is_contextual(component: Component, scope: TemplateScope, name: string) {
|
|
|
|
|
if (name === '$$props') return true;
|
|
|
|
|
|
|
|
|
|
// // if it's a name below root scope, it's contextual
|
|
|
|
|
// if (!scope.is_top_level(name)) return true;
|
|
|
|
|
// if it's a name below root scope, it's contextual
|
|
|
|
|
if (!scope.is_top_level(name)) return true;
|
|
|
|
|
|
|
|
|
|
// const variable = component.var_lookup.get(name);
|
|
|
|
|
const variable = component.var_lookup.get(name);
|
|
|
|
|
|
|
|
|
|
// // hoistables, module declarations, and imports are non-contextual
|
|
|
|
|
// if (!variable || variable.hoistable) return false;
|
|
|
|
|
// hoistables, module declarations, and imports are non-contextual
|
|
|
|
|
if (!variable || variable.hoistable) return false;
|
|
|
|
|
|
|
|
|
|
// // assume contextual
|
|
|
|
|
// return true;
|
|
|
|
|
// }
|
|
|
|
|
// assume contextual
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|