From fe7d0d371be0d1e9223be0459b71783e374ce3f8 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sat, 14 Sep 2019 22:12:32 -0400 Subject: [PATCH] ... --- src/compiler/compile/Component.ts | 2 +- src/compiler/compile/render_dom/index.ts | 79 ++++++++++--------- .../render_dom/wrappers/Element/Attribute.ts | 3 +- .../compile/render_dom/wrappers/IfBlock.ts | 19 ++--- .../wrappers/InlineComponent/index.ts | 58 ++++++++------ .../render_dom/wrappers/shared/bind_this.ts | 12 +-- .../compile/utils/flatten_reference.ts | 2 +- src/compiler/compile/utils/snip.ts | 2 +- 8 files changed, 98 insertions(+), 79 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 4262b07fdd..59f2e2a33e 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -838,7 +838,7 @@ export default class Component { } if (value) { - return `$$invalidate('${name}', ${value})`; + return x`$$invalidate('${name}', ${value})`; } // if this is a reactive declaration, invalidate dependencies recursively diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index fedbbbf463..ac9d33f95a 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -1,4 +1,4 @@ -import { b, x } from 'code-red'; +import { b, x, p } from 'code-red'; import { stringify, escape } from '../utils/stringify'; import Component from '../Component'; import Renderer from './Renderer'; @@ -8,7 +8,7 @@ import add_to_set from '../utils/add_to_set'; import { extract_names } from '../utils/scope'; import { invalidate } from '../utils/invalidate'; import Block from './Block'; -import { ClassDeclaration, FunctionExpression } from 'estree'; +import { ClassDeclaration, FunctionExpression, Node } from 'estree'; export default function dom( component: Component, @@ -82,12 +82,12 @@ export default function dom( const set = (uses_props || writable_props.length > 0 || component.slots.size > 0) ? x` ${$$props} => { - ${uses_props && component.invalidate('$$props', b`$$props = @assign(@assign({}, $$props), $$new_props)`)} + ${uses_props && component.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)} ${writable_props.map(prop => b`if ('${prop.export_name}' in ${$$props}) ${component.invalidate(prop.name, `${prop.name} = ${$$props}.${prop.export_name}`)};` )} ${component.slots.size > 0 && - b`if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', `$$scope = ${$$props}.$$scope`)};`} + b`if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', x`$$scope = ${$$props}.$$scope`)};`} } ` : null; @@ -261,9 +261,9 @@ export default function dom( const filtered_declarations = component.vars .filter(v => ((v.referenced || v.export_name) && !v.hoistable)) - .map(v => v.name); + .map(v => p`${v.name}`); - if (uses_props) filtered_declarations.push(`$$props: $$props = ${component.helper('exclude_internal_props')}($$props)`); + if (uses_props) filtered_declarations.push(p`$$props: $$props = ${component.helper('exclude_internal_props')}($$props)`); const filtered_props = props.filter(prop => { const variable = component.var_lookup.get(prop.name); @@ -276,11 +276,11 @@ export default function dom( const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$'); if (component.slots.size > 0) { - filtered_declarations.push('$$slots', '$$scope'); + filtered_declarations.push(p`$$slots`, p`$$scope`); } if (renderer.binding_groups.length > 0) { - filtered_declarations.push(`$$binding_groups`); + filtered_declarations.push(p`$$binding_groups`); } const has_definition = ( @@ -319,31 +319,30 @@ export default function dom( .map(({ name }) => `$$self.$$.on_destroy.push(() => $$unsubscribe_${name.slice(1)}());`); if (has_definition) { - const reactive_declarations = []; + const reactive_declarations: (Node | Node[]) = []; const fixed_reactive_declarations = []; // not really 'reactive' but whatever - component.reactive_declarations - .forEach(_d => { - // const dependencies = Array.from(d.dependencies); - // const uses_props = !!dependencies.find(n => n === '$$props'); - - // const condition = !uses_props && dependencies - // .filter(n => { - // const variable = component.var_lookup.get(n); - // return variable && (variable.writable || variable.mutated); - // }) - // .map(n => `$$dirty.${n}`).join(' || '); - - throw new Error(`bad`); - // let snippet = `[✂${d.node.body.start}-${d.node.end}✂]`; - // if (condition) snippet = `if (${condition}) { ${snippet} }`; - - // if (condition || uses_props) { - // reactive_declarations.push(snippet); - // } else { - // fixed_reactive_declarations.push(snippet); - // } - }); + component.reactive_declarations.forEach(d => { + const dependencies = Array.from(d.dependencies); + const uses_props = !!dependencies.find(n => n === '$$props'); + + const condition = !uses_props && dependencies + .filter(n => { + const variable = component.var_lookup.get(n); + return variable && (variable.writable || variable.mutated); + }) + .map(n => x`$$dirty.${n}`) + .reduce((lhs, rhs) => x`${lhs} || ${rhs}`); + + let statement = d.node; + if (condition) statement = b`if (${condition}) { ${statement} }`[0]; + + if (condition || uses_props) { + reactive_declarations.push(statement); + } else { + fixed_reactive_declarations.push(statement); + } + }); const injected = Array.from(component.injected_reactive_declaration_vars).filter(name => { const variable = component.var_lookup.get(name); @@ -365,7 +364,7 @@ export default function dom( let unknown_props_check; if (component.compile_options.dev && !component.var_lookup.has('$$props') && writable_props.length) { unknown_props_check = b` - const writable_props = [${writable_props.map(prop => `'${prop.export_name}'`).join(', ')}]; + const writable_props = [${writable_props.map(prop => `'${prop.export_name}'`)}]; @_Object.keys($$props).forEach(key => { if (!writable_props.includes(key) && !key.startsWith('$$')) @_console.warn(\`<${component.tag}> was created with unknown prop '\${key}'\`); }); @@ -374,13 +373,17 @@ export default function dom( const return_value = { type: 'ObjectExpression', - properties: filtered_declarations.map(name => { - const id = { type: 'Identifier', name }; + properties: filtered_declarations + }; + + const reactive_dependencies = { + type: 'ObjectPattern', + properties: Array.from(all_reactive_dependencies).map(name => { return { type: 'Property', - key: id, - value: id, - kind: 'init' + kind: 'init', + key: { type: 'Identifier', name }, + value: { type: 'Literal', value: 1 } }; }) }; @@ -412,7 +415,7 @@ export default function dom( ${injected.length && `let ${injected.join(', ')};`} ${reactive_declarations.length > 0 && b` - $$self.$$.update = ($$dirty = { ${Array.from(all_reactive_dependencies).map(n => `${n}: 1`).join(', ')} }) => { + $$self.$$.update = ($$dirty = ${reactive_dependencies}) => { ${reactive_declarations} }; `} diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index fbe273046f..3c28820a9e 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -7,6 +7,7 @@ import { b, x } from 'code-red'; import Expression from '../../../nodes/shared/Expression'; import Text from '../../../nodes/Text'; import { changed } from '../shared/changed'; +import { Literal } from 'estree'; export default class AttributeWrapper { node: Attribute; @@ -173,7 +174,7 @@ export default class AttributeWrapper { ? b`@set_input_type(${element.var}, ${value});` : property_name ? b`${element.var}.${property_name} = ${value};` - : b`${method}(${element.var}, "${name}", ${value});` + : b`${method}(${element.var}, "${name}", ${value.type === 'Literal' && (value as Literal).value === true ? x`""` : value});` ); block.chunks.hydrate.push(statement); diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.ts b/src/compiler/compile/render_dom/wrappers/IfBlock.ts index 595a04c324..22aa83a5d7 100644 --- a/src/compiler/compile/render_dom/wrappers/IfBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/IfBlock.ts @@ -341,8 +341,8 @@ export default class IfBlockWrapper extends Wrapper { const if_blocks = block.get_unique_name(`if_blocks`); const if_current_block_type_index = has_else - ? '' - : `if (~${current_block_type_index}) `; + ? nodes => nodes + : nodes => b`if (~${current_block_type_index}) { ${nodes} }`; block.add_variable(current_block_type_index); block.add_variable(name); @@ -350,7 +350,7 @@ export default class IfBlockWrapper extends Wrapper { /* eslint-disable @typescript-eslint/indent,indent */ block.chunks.init.push(b` const ${if_block_creators} = [ - ${this.branches.map(branch => branch.block.name).join(',\n')} + ${this.branches.map(branch => branch.block.name)} ]; const ${if_blocks} = []; @@ -393,9 +393,10 @@ export default class IfBlockWrapper extends Wrapper { const initial_mount_node = parent_node || '#target'; const anchor_node = parent_node ? 'null' : 'anchor'; - throw new Error(`womp womp`); block.chunks.mount.push( - b`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].m(${initial_mount_node}, ${anchor_node});` + if_current_block_type_index( + b`${if_blocks}[${current_block_type_index}].m(${initial_mount_node}, ${anchor_node});` + ) ); if (this.needs_update) { @@ -442,7 +443,7 @@ export default class IfBlockWrapper extends Wrapper { let ${previous_block_index} = ${current_block_type_index}; ${current_block_type_index} = ${select_block_type}(#changed, #ctx); if (${current_block_type_index} === ${previous_block_index}) { - ${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(#changed, #ctx); + ${if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].p(#changed, #ctx);`)} } else { ${change_block} } @@ -460,9 +461,9 @@ export default class IfBlockWrapper extends Wrapper { block.chunks.update.push(b`${name}.p(#changed, #ctx);`); } - block.chunks.destroy.push(b` - ${if_current_block_type_index}${if_blocks}[${current_block_type_index}].d(${detaching}); - `); + block.chunks.destroy.push( + if_current_block_type_index(b`${if_blocks}[${current_block_type_index}].d(${detaching});`) + ); } render_simple( diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index c07858314b..7f63716d46 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -3,7 +3,7 @@ import Renderer from '../../Renderer'; import Block from '../../Block'; import InlineComponent from '../../../nodes/InlineComponent'; import FragmentWrapper from '../Fragment'; -import { quote_name_if_necessary, quote_prop_if_necessary, sanitize } from '../../../../utils/names'; +import { quote_name_if_necessary, sanitize } from '../../../../utils/names'; import add_to_set from '../../../utils/add_to_set'; import { b, x } from 'code-red'; import Attribute from '../../../nodes/Attribute'; @@ -15,7 +15,7 @@ import TemplateScope from '../../../nodes/shared/TemplateScope'; import is_dynamic from '../shared/is_dynamic'; import bind_this from '../shared/bind_this'; import { changed } from '../shared/changed'; -import { Node, Identifier } from 'estree'; +import { Node, Identifier, Expression } from 'estree'; export default class InlineComponentWrapper extends Wrapper { var: Identifier; @@ -124,7 +124,7 @@ export default class InlineComponentWrapper extends Wrapper { const uses_spread = !!this.node.attributes.find(a => a.is_spread); - let attribute_object; + let attribute_object = x`{}`; if (this.node.attributes.length > 0 || this.node.bindings.length > 0 || this.slots.size > 0) { if (!uses_spread && this.node.bindings.length === 0) { @@ -165,7 +165,7 @@ export default class InlineComponentWrapper extends Wrapper { value: attr.get_value(block) } }).concat(initial_props.properties) - }; + } as Expression; } component_opts.properties.push({ @@ -175,13 +175,13 @@ export default class InlineComponentWrapper extends Wrapper { value: attribute_object }); } else { + props = block.get_unique_name(`${name.name}_props`); component_opts.properties.push({ type: 'Property', kind: 'init', key: { type: 'Identifier', name: 'props' }, value: props }); - props = block.get_unique_name(`${name}_props`); } } @@ -241,7 +241,7 @@ export default class InlineComponentWrapper extends Wrapper { const { name, dependencies } = attr; const condition = dependencies.size > 0 && (dependencies.size !== all_dependencies.size) - ? `(${Array.from(dependencies).map(d => `changed.${d}`).join(' || ')})` + ? changed(Array.from(dependencies)) : null; if (attr.is_spread) { @@ -254,16 +254,16 @@ export default class InlineComponentWrapper extends Wrapper { } changes.push(condition ? `${condition} && ${value_object}` : value_object); } else { - const obj = `{ ${quote_name_if_necessary(name)}: ${attr.get_value(block)} }`; + const obj = x`{ ${quote_name_if_necessary(name)}: ${attr.get_value(block)} }`; initial_props.push(obj); - changes.push(condition ? `${condition} && ${obj}` : `${levels}[${i}]`); + changes.push(condition ? x`${condition} && ${obj}` : x`${levels}[${i}]`); } }); block.chunks.init.push(b` const ${levels} = [ - ${initial_props.join(',\n')} + ${initial_props} ]; `); @@ -278,7 +278,7 @@ export default class InlineComponentWrapper extends Wrapper { updates.push(b` const ${name_changes} = ${condition} ? @get_spread_update(${levels}, [ - ${changes.join(',\n')} + ${changes} ]) : {} `); } else { @@ -293,7 +293,7 @@ export default class InlineComponentWrapper extends Wrapper { const condition = changed(dependencies); updates.push(b` - if (${condition}) ${name_changes}${quote_prop_if_necessary(attribute.name)} = ${attribute.get_value(block)}; + if (${condition}) ${name_changes}.${attribute.name} = ${attribute.get_value(block)}; `); } }); @@ -301,7 +301,10 @@ export default class InlineComponentWrapper extends Wrapper { } if (non_let_dependencies.length > 0) { - updates.push(b`if (${changed(non_let_dependencies)} ${name_changes}.$$scope = { changed: #changed, ctx: #ctx };`); + updates.push(b` + if (${changed(non_let_dependencies)}) { + ${name_changes}.$$scope = { changed: #changed, ctx: #ctx }; + }`); } const munged_bindings = this.node.bindings.map(binding => { @@ -311,7 +314,7 @@ export default class InlineComponentWrapper extends Wrapper { return bind_this(component, block, binding, this.var); } - const id = component.get_unique_name(`${this.var}_${binding.name}_binding`); + const id = component.get_unique_name(`${this.var.name}_${binding.name}_binding`); component.add_var({ name: id.name, @@ -326,13 +329,13 @@ export default class InlineComponentWrapper extends Wrapper { statements.push(b` if (${snippet} !== void 0) { - ${props}${quote_prop_if_necessary(binding.name)} = ${snippet}; + ${props}.${binding.name} = ${snippet}; }` ); updates.push(b` - if (!${updating} && ${[...binding.expression.dependencies].map((dependency: string) => `changed.${dependency}`).join(' || ')}) { - ${name_changes}${quote_prop_if_necessary(binding.name)} = ${snippet}; + if (!${updating} && ${changed(Array.from(binding.expression.dependencies))}) { + ${name_changes}.${binding.name} = ${snippet}; } `); @@ -353,11 +356,22 @@ export default class InlineComponentWrapper extends Wrapper { const value = block.get_unique_name('value'); const args: any[] = [value]; if (contextual_dependencies.length > 0) { - args.push(x`{ ${contextual_dependencies.join(', ')} }`); + args.push({ + type: 'ObjectPattern', + properties: contextual_dependencies.map(name => { + const id = { type: 'Identifier', name }; + return { + type: 'Property', + kind: 'init', + key: id, + value: id + }; + }) + }); block.chunks.init.push(b` - function ${name}(${value}) { - #ctx.${name}.call(null, ${value}, #ctx); + function ${id}(${value}) { + #ctx.${id}.call(null, ${value}, #ctx); ${updating} = true; @add_flush_callback(() => ${updating} = false); } @@ -367,7 +381,7 @@ export default class InlineComponentWrapper extends Wrapper { } else { block.chunks.init.push(b` function ${id}(${value}) { - #ctx.${name}.call(null, ${value}); + #ctx.${id}.call(null, ${value}); ${updating} = true; @add_flush_callback(() => ${updating} = false); } @@ -375,7 +389,7 @@ export default class InlineComponentWrapper extends Wrapper { } const body = b` - function ${id}(${args.join(', ')}) { + function ${id}(${args}) { ${lhs} = ${value}; ${component.invalidate(dependencies[0])}; } @@ -383,7 +397,7 @@ export default class InlineComponentWrapper extends Wrapper { component.partly_hoisted.push(body); - return `@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`; + return b`@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`; }); const munged_handlers = this.node.handlers.map(handler => { diff --git a/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts b/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts index 382fedf212..2a4fab1473 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/bind_this.ts @@ -44,7 +44,7 @@ export default function bind_this(component: Component, block: Block, binding: B if (contextual_dependencies.length) { component.partly_hoisted.push(b` - function ${fn}(${['$$value', ...contextual_dependencies].join(', ')}) { + function ${fn}(${['$$value', ...contextual_dependencies]}) { if (${lhs} === $$value) return; @binding_callbacks[$$value ? 'unshift' : 'push'](() => { ${body} @@ -59,12 +59,12 @@ export default function bind_this(component: Component, block: Block, binding: B block.add_variable(id, x`#ctx.${id}`); } - const assign = block.get_unique_name(`assign_${variable}`); - const unassign = block.get_unique_name(`unassign_${variable}`); + const assign = block.get_unique_name(`assign_${variable.name}`); + const unassign = block.get_unique_name(`unassign_${variable.name}`); block.chunks.init.push(b` - const ${assign} = () => #ctx.${fn}(${[variable].concat(args).join(', ')}); - const ${unassign} = () => #ctx.${fn}(${['null'].concat(args).join(', ')}); + const ${assign} = () => #ctx.${fn}(${[variable].concat(args)}); + const ${unassign} = () => #ctx.${fn}(${['null'].concat(args)}); `); const condition = Array.from(contextual_dependencies).map(name => `${name} !== #ctx.${name}`).join(' || '); @@ -75,7 +75,7 @@ export default function bind_this(component: Component, block: Block, binding: B block.chunks.update.push(b` if (${condition}) { ${unassign}(); - ${args.map(a => `${a} = #ctx.${a}`).join(', ')}; + ${args.map(a => b`${a} = #ctx.${a}`)}; ${assign}(); }` ); diff --git a/src/compiler/compile/utils/flatten_reference.ts b/src/compiler/compile/utils/flatten_reference.ts index 1a1d13d0a6..9ea42fd00a 100644 --- a/src/compiler/compile/utils/flatten_reference.ts +++ b/src/compiler/compile/utils/flatten_reference.ts @@ -3,7 +3,7 @@ import { Node, Identifier } from 'estree'; export default function flatten_reference(node: Node) { // TODO temporary (#3539) if ((node as any).type === 'Expression') { - throw new Error('bad'); + throw new Error('flatten_reference bad'); } const nodes = []; diff --git a/src/compiler/compile/utils/snip.ts b/src/compiler/compile/utils/snip.ts index 7a2e12f8e5..87163b18d9 100644 --- a/src/compiler/compile/utils/snip.ts +++ b/src/compiler/compile/utils/snip.ts @@ -1,4 +1,4 @@ export function snip(expression) { - throw new Error(`bad`); + throw new Error(`snip bad`); return `[✂${expression.node.start}-${expression.node.end}✂]`; } \ No newline at end of file