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 // TODO move this? it is specific to render-dom
render(block: Block) { 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); // this.component.add_reference(this.handler_name);
return `ctx.${this.handler_name}`; return `ctx.${this.handler_name}`;

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

@ -77,12 +77,12 @@ export default function dom(
const set = (uses_props || writable_props.length > 0 || component.slots.size > 0) const set = (uses_props || writable_props.length > 0 || component.slots.size > 0)
? x` ? x`
${$$props} => { ${$$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 => ${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 && ${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; : null;
@ -370,7 +370,7 @@ export default function dom(
${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')} ${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};`} ${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 anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const update_mount_node = this.get_update_mount_node(anchor); 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 info = block.get_unique_name(`info`);
const promise = block.get_unique_name(`promise`); 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; while (renderer.component.source[c] !== 'e') c += 1;
// renderer.component.code.overwrite(c, c + 4, 'length'); // 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`); const iterations = block.get_unique_name(`${this.var}_blocks`);
this.vars = { 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.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;`); 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};`); 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 // DRY it out if that's possible without introducing crazy indirection
if (this.node.chunks.length === 1) { if (this.node.chunks.length === 1) {
// single {tag} — may be a non-string // 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 { } else {
// '{foo} {bar}' — treat as string concatenation // '{foo} {bar}' — treat as string concatenation
const prefix = this.node.chunks[0].type === 'Text' ? '' : `"" + `; const prefix = this.node.chunks[0].type === 'Text' ? '' : `"" + `;
@ -207,10 +207,7 @@ export default class AttributeWrapper {
return stringify(chunk.data); return stringify(chunk.data);
} }
const rendered = chunk.render(); return chunk.manipulate();
return chunk.get_precedence() <= 13
? `(${rendered})`
: rendered;
}); });
} }
@ -223,7 +220,7 @@ export default class AttributeWrapper {
return `="${value.map(chunk => { return `="${value.map(chunk => {
return chunk.type === 'Text' return chunk.type === 'Text'
? chunk.data.replace(/"/g, '\\"') ? chunk.data.replace(/"/g, '\\"')
: `\${${chunk.render()}}`; : `\${${chunk.manipulate()}}`;
}).join('')}"`; }).join('')}"`;
} }
} }

@ -64,7 +64,7 @@ export default class BindingWrapper {
// view to model // view to model
this.handler = get_event_handler(this, parent.renderer, block, this.object, contextless_snippet); 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; this.is_readonly = this.node.is_readonly;

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

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

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

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

@ -21,9 +21,9 @@ export default class Tag extends Wrapper {
update: ((value: Node) => Node) update: ((value: Node) => Node)
) { ) {
const dependencies = this.node.expression.dynamic_dependencies(); 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; 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 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; let dependencies;
if (expression) { if (expression) {
snippet = expression.render(block); snippet = expression.manipulate(block);
dependencies = expression.dynamic_dependencies(); dependencies = expression.dynamic_dependencies();
} }

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

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