bit more progress

pull/3539/head
Richard Harris 6 years ago
parent 50280232e8
commit 9d947292a1

@ -63,7 +63,7 @@ export default class EventHandler extends Node {
// TODO move this? it is specific to render-dom
render(block: Block) {
if (this.expression) return this.expression.render(block);
if (this.expression) this.expression.manipulate(block);
// this.component.add_reference(this.handler_name);
return `ctx.${this.handler_name}`;

@ -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;
}

@ -77,12 +77,12 @@ export default function dom(
const set = (uses_props || writable_props.length > 0 || component.slots.size > 0)
? x`
${$$props} => {
${uses_props && component.invalidate('$$props', `$$props = @assign(@assign({}, $$props), $$new_props)`)}
${uses_props && component.invalidate('$$props', b`$$props = @assign(@assign({}, $$props), $$new_props)`)}
${writable_props.map(prop =>
`if ('${prop.export_name}' in ${$$props}) ${component.invalidate(prop.name, `${prop.name} = ${$$props}.${prop.export_name}`)};`
b`if ('${prop.export_name}' in ${$$props}) ${component.invalidate(prop.name, `${prop.name} = ${$$props}.${prop.export_name}`)};`
)}
${component.slots.size > 0 &&
`if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', `$$scope = ${$$props}.$$scope`)};`}
b`if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', `$$scope = ${$$props}.$$scope`)};`}
}
`
: null;
@ -370,7 +370,7 @@ export default function dom(
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}
${set && `$$self.$set = ${set};`}
${set && b`$$self.$set = ${set};`}
${capture_state && `$$self.$capture_state = ${capture_state};`}

@ -127,7 +127,7 @@ export default class AwaitBlockWrapper extends Wrapper {
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const update_mount_node = this.get_update_mount_node(anchor);
const snippet = this.node.expression.render(block);
const snippet = this.node.expression.manipulate(block);
const info = block.get_unique_name(`info`);
const promise = block.get_unique_name(`promise`);

@ -111,7 +111,7 @@ export default class EachBlockWrapper extends Wrapper {
while (renderer.component.source[c] !== 'e') c += 1;
// renderer.component.code.overwrite(c, c + 4, 'length');
const each_block_value = renderer.component.get_unique_name(`${this.var}_value`);
const each_block_value = renderer.component.get_unique_name(`${this.var.name}_value`);
const iterations = block.get_unique_name(`${this.var}_blocks`);
this.vars = {
@ -190,7 +190,7 @@ export default class EachBlockWrapper extends Wrapper {
if (this.node.has_binding) this.context_props.push(`child_ctx.${this.vars.each_block_value} = list;`);
if (this.node.has_binding || this.node.index) this.context_props.push(`child_ctx.${this.index_name} = i;`);
const snippet = this.node.expression.render(block);
const snippet = this.node.expression.manipulate(block);
block.chunks.init.push(b`let ${this.vars.each_block_value} = ${snippet};`);

@ -78,7 +78,7 @@ export default class AttributeWrapper {
// DRY it out if that's possible without introducing crazy indirection
if (this.node.chunks.length === 1) {
// single {tag} — may be a non-string
value = (this.node.chunks[0] as Expression).render(block);
value = (this.node.chunks[0] as Expression).manipulate(block);
} else {
// '{foo} {bar}' — treat as string concatenation
const prefix = this.node.chunks[0].type === 'Text' ? '' : `"" + `;
@ -207,10 +207,7 @@ export default class AttributeWrapper {
return stringify(chunk.data);
}
const rendered = chunk.render();
return chunk.get_precedence() <= 13
? `(${rendered})`
: rendered;
return chunk.manipulate();
});
}
@ -223,7 +220,7 @@ export default class AttributeWrapper {
return `="${value.map(chunk => {
return chunk.type === 'Text'
? chunk.data.replace(/"/g, '\\"')
: `\${${chunk.render()}}`;
: `\${${chunk.manipulate()}}`;
}).join('')}"`;
}
}

@ -64,7 +64,7 @@ export default class BindingWrapper {
// view to model
this.handler = get_event_handler(this, parent.renderer, block, this.object, contextless_snippet);
this.snippet = this.node.expression.render(block);
this.snippet = this.node.expression.manipulate(block);
this.is_readonly = this.node.is_readonly;

@ -35,10 +35,10 @@ export default class StyleAttributeWrapper extends AttributeWrapper {
if (chunk.type === 'Text') {
return stringify(chunk.data);
} else {
const snippet = chunk.render();
const snippet = chunk.manipulate();
add_to_set(prop_dependencies, chunk.dynamic_dependencies());
return chunk.get_precedence() <= 13 ? `(${snippet})` : snippet;
return snippet;
}
})
.join(' + ');

@ -601,7 +601,7 @@ export default class ElementWrapper extends Wrapper {
: null;
if (attr.is_spread) {
const snippet = attr.expression.render(block);
const snippet = attr.expression.manipulate(block);
initial_props.push(snippet);
@ -654,7 +654,7 @@ export default class ElementWrapper extends Wrapper {
// bidirectional transition
const name = block.get_unique_name(`${this.var}_transition`);
const snippet = intro.expression
? intro.expression.render(block)
? intro.expression.manipulate(block)
: '{}';
block.add_variable(name);
@ -700,7 +700,7 @@ export default class ElementWrapper extends Wrapper {
if (intro) {
block.add_variable(intro_name);
const snippet = intro.expression
? intro.expression.render(block)
? intro.expression.manipulate(block)
: '{}';
const fn = component.qualify(intro.name);
@ -742,7 +742,7 @@ export default class ElementWrapper extends Wrapper {
if (outro) {
block.add_variable(outro_name);
const snippet = outro.expression
? outro.expression.render(block)
? outro.expression.manipulate(block)
: '{}';
const fn = component.qualify(outro.name);
@ -796,7 +796,7 @@ export default class ElementWrapper extends Wrapper {
${outro && `@add_transform(${this.var}, ${rect});`}
`);
const params = this.node.animation.expression ? this.node.animation.expression.render(block) : '{}';
const params = this.node.animation.expression ? this.node.animation.expression.manipulate(block) : '{}';
const name = component.qualify(this.node.animation.name);
@ -816,7 +816,7 @@ export default class ElementWrapper extends Wrapper {
let snippet;
let dependencies;
if (expression) {
snippet = expression.render(block);
snippet = expression.manipulate(block);
dependencies = expression.dependencies;
} else {
snippet = `${quote_prop_if_necessary(name)}`;

@ -200,7 +200,7 @@ export default class InlineComponentWrapper extends Wrapper {
: null;
if (attr.is_spread) {
const value = attr.expression.render(block);
const value = attr.expression.manipulate(block);
initial_props.push(value);
let value_object = value;
@ -271,7 +271,7 @@ export default class InlineComponentWrapper extends Wrapper {
const updating = block.get_unique_name(`updating_${binding.name}`);
block.add_variable(updating);
const snippet = binding.expression.render(block);
const snippet = binding.expression.manipulate(block);
statements.push(b`
if (${snippet} !== void 0) {
@ -346,7 +346,7 @@ export default class InlineComponentWrapper extends Wrapper {
const switch_value = block.get_unique_name('switch_value');
const switch_props = block.get_unique_name('switch_props');
const snippet = this.node.expression.render(block);
const snippet = this.node.expression.manipulate(block);
block.chunks.init.push(b`
var ${switch_value} = ${snippet};

@ -35,7 +35,7 @@ export default class TitleWrapper extends Wrapper {
// single {tag} — may be a non-string
// @ts-ignore todo: check this
const { expression } = this.node.children[0];
value = expression.render(block);
value = expression.manipulate(block);
add_to_set(all_dependencies, expression.dependencies);
} else {
// '{foo} {bar}' — treat as string concatenation
@ -47,7 +47,7 @@ export default class TitleWrapper extends Wrapper {
return stringify(chunk.data);
} else {
// @ts-ignore todo: check this
const snippet = chunk.expression.render(block);
const snippet = chunk.expression.manipulate(block);
// @ts-ignore todo: check this
chunk.expression.dependencies.forEach(d => {
all_dependencies.add(d);

@ -21,9 +21,9 @@ export default class Tag extends Wrapper {
update: ((value: Node) => Node)
) {
const dependencies = this.node.expression.dynamic_dependencies();
const snippet = this.node.expression.node;
const snippet = this.node.expression.manipulate(block);
const value = this.node.should_cache && block.get_unique_name(`${this.var}_value`);
const value = this.node.should_cache && block.get_unique_name(`${this.var.name}_value`);
const content = this.node.should_cache ? value : snippet;
if (this.node.should_cache) block.add_variable(value, snippet); // TODO may need to coerce snippet to string

@ -15,7 +15,7 @@ export default function add_actions(
let dependencies;
if (expression) {
snippet = expression.render(block);
snippet = expression.manipulate(block);
dependencies = expression.dynamic_dependencies();
}

@ -1,6 +1,3 @@
export default {
solo: 1,
show: 1,
html: '<h1>Hello world!</h1>'
html: '<h1>1 + 2 = 3</h1>'
};

@ -1,5 +1,6 @@
<script>
export let name = 'world';
let a = 1;
let b = 2;
</script>
<h1>Hello {name}!</h1>
<p>{a} + {b} = {a + b}</p>
Loading…
Cancel
Save