diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 6e5cf286e6..4d26867565 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -68,8 +68,6 @@ export default class Component { var_lookup: Map = new Map(); imports: ImportDeclaration[] = []; - module_javascript: string; - javascript: string; hoistable_nodes: Set = new Set(); node_for_declaration: Map = new Map(); @@ -530,7 +528,10 @@ export default class Component { } extract_javascript(script) { - const nodes_to_include = script.content.body.filter(node => { + if (!script) return null; + + return script.content.body.filter(node => { + if (!node) return false; if (this.hoistable_nodes.has(node)) return false; if (this.reactive_declaration_nodes.has(node)) return false; if (node.type === 'ImportDeclaration') return false; @@ -538,38 +539,6 @@ export default class Component { return false; return true; }); - - return nodes_to_include; - - if (nodes_to_include.length === 0) return null; - - let a = script.content.start; - while (/\s/.test(this.source[a])) a += 1; - - // let b = a; - - // let result = ''; - - // script.content.body.forEach(node => { - // if ( - // this.hoistable_nodes.has(node) || - // this.reactive_declaration_nodes.has(node) - // ) { - // if (a !== b) result += `[✂${a}-${b}✂]`; - // a = node.end; - // } - - // b = node.end; - // }); - - // while (/\s/.test(this.source[a - 1])) a -= 1; - - // b = script.content.end; - // while (/\s/.test(this.source[b - 1])) b -= 1; - - // if (a < b) result += `[✂${a}-${b}✂]`; - - // return result || null; } walk_module_js() { @@ -625,7 +594,6 @@ export default class Component { this.extract_imports(script.content); this.extract_exports(script.content); - this.module_javascript = this.extract_javascript(script); } walk_instance_js_pre_template() { @@ -728,7 +696,6 @@ export default class Component { this.hoist_instance_declarations(); this.extract_reactive_declarations(); this.extract_reactive_store_references(); - this.javascript = this.extract_javascript(script); } // TODO merge this with other walks that are independent @@ -812,7 +779,7 @@ export default class Component { const variable = this.var_lookup.get(name); if (variable && (variable.subscribable && variable.reassigned)) { - return x`$$subscribe_${name}($$invalidate('${name}', ${value || name}))`; + return x`${`$$subscribe_${name}`}($$invalidate('${name}', ${value || name}))`; } if (name[0] === '$' && name[1] !== '$') { @@ -850,7 +817,7 @@ export default class Component { return Array.from(deps).map(n => x`$$invalidate('${n}', ${n})`); } - rewrite_props(_get_insert: (variable: Var) => string) { + rewrite_props(get_insert: (variable: Var) => Node[]) { // TODO const component = this; @@ -858,7 +825,7 @@ export default class Component { let scope = instance_scope; walk(this.ast.instance.content, { - enter(node) { + enter(node, parent, key, index) { if (/Function/.test(node.type)) { return this.skip(); } @@ -896,9 +863,11 @@ export default class Component { const variable = component.var_lookup.get(name); if (variable.export_name) { - // const insert = variable.subscribable - // ? get_insert(variable) - // : null; + const insert = variable.subscribable + ? get_insert(variable) + : null; + + parent[key].splice(index + 1, 0, insert); declarator.id = { type: 'ObjectPattern', @@ -920,6 +889,9 @@ export default class Component { }; declarator.init = x`$$props`; + } else if (variable.subscribable) { + const insert = get_insert(variable); + parent[key].splice(index + 1, 0, ...insert); } }); } diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 2abfc1a308..b0b4d8bce2 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -163,7 +163,7 @@ export default function dom( const { ctx } = this.$$; const props = ${options.customElement ? `this.attributes` : `options.props || {}`}; ${expected.map(prop => b` - if (ctx.${prop.name} === undefined && !('${prop.export_name}' in props)) { + if (#ctx.${prop.name} === undefined && !('${prop.export_name}' in props)) { @_console.warn("<${component.tag}> was created without expected prop '${prop.export_name}'"); }`)} `; @@ -182,7 +182,7 @@ export default function dom( const writable_vars = component.vars.filter(variable => !variable.module && variable.writable); inject_state = (uses_props || writable_vars.length > 0) ? x` ${$$props} => { - ${uses_props && component.invalidate('$$props', `$$props = @assign(@assign({}, $$props), $$new_props)`)} + ${uses_props && component.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), $$new_props)`)} ${writable_vars.map(prop => b` if ('${prop.name}' in $$props) ${component.invalidate(prop.name, `${prop.name} = ${$$props}.${prop.name}`)}; `)} @@ -226,18 +226,17 @@ export default function dom( component.rewrite_props(({ name, reassigned }) => { const value = `$${name}`; - const callback = `$value => { ${value} = $$value; $$invalidate('${value}', ${value}) }`; - if (reassigned) { - return `$$subscribe_${name}()`; + return b`${`$$subscribe_${name}`}()`; } const component_subscribe = component.helper('component_subscribe'); + const callback = x`$$value => { ${value} = $$value; $$invalidate('${value}', ${value}) }`; - let insert = `${component_subscribe}($$self, ${name}, $${callback})`; + let insert = b`${component_subscribe}($$self, ${name}, $${callback})`; if (component.compile_options.dev) { const validate_store = component.helper('validate_store'); - insert = `${validate_store}(${name}, '${name}'); ${insert}`; + insert = b`${validate_store}(${name}, '${name}'); ${insert}`; } return insert; @@ -254,7 +253,7 @@ export default function dom( ${block.get_contents()} } - ${component.module_javascript} + ${component.extract_javascript(component.ast.module)} ${component.fully_hoisted} `); @@ -283,8 +282,10 @@ export default function dom( filtered_declarations.push(p`$$binding_groups`); } + const instance_javascript = component.extract_javascript(component.ast.instance); + const has_definition = ( - component.javascript || + instance_javascript || filtered_props.length > 0 || uses_props || component.partly_hoisted.length > 0 || @@ -316,7 +317,7 @@ export default function dom( const variable = component.var_lookup.get(store.name.slice(1)); return variable && variable.reassigned; }) - .map(({ name }) => `$$self.$$.on_destroy.push(() => $$unsubscribe_${name.slice(1)}());`); + .map(({ name }) => b`$$self.$$.on_destroy.push(() => ${`$$unsubscribe_${name.slice(1)}`}());`); if (has_definition) { const reactive_declarations: (Node | Node[]) = []; @@ -355,7 +356,9 @@ export default function dom( const store = component.var_lookup.get(name); if (store && store.reassigned) { - return b`let ${$name}, $$unsubscribe_${name} = @noop, $$subscribe_${name} = () => ($$unsubscribe_${name}(), $$unsubscribe_${name} = @subscribe(${name}, $$value => { ${$name} = $$value; $$invalidate('${$name}', ${$name}); }), ${name})`; + const unsubscribe = `$$unsubscribe_${name}`; + const subscribe = `$$subscribe_${name}`; + return b`let ${$name}, ${unsubscribe} = @noop, ${subscribe} = () => (${unsubscribe}(), ${unsubscribe} = @subscribe(${name}, $$value => { ${$name} = $$value; $$invalidate('${$name}', ${$name}); }), ${name})`; } return b`let ${$name};`; @@ -396,7 +399,7 @@ export default function dom( ${resubscribable_reactive_store_unsubscribers} - ${component.javascript} + ${instance_javascript} ${unknown_props_check} diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index a6ee015d35..40a5ace9d4 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -529,7 +529,7 @@ export default class EachBlockWrapper extends Wrapper { `; } else { remove_old_blocks = b` - for (${this.block.has_update_method ? `` : `#i = ${data_length}`}; #i < ${this.block.has_update_method ? view_length : '#old_length'}; #i += 1) { + for (${this.block.has_update_method ? `` : x`#i = ${data_length}`}; #i < ${this.block.has_update_method ? view_length : '#old_length'}; #i += 1) { ${iterations}[#i].d(1); } ${!fixed_length && b`${view_length} = ${data_length};`} diff --git a/src/compiler/compile/render_ssr/index.ts b/src/compiler/compile/render_ssr/index.ts index 81f8c719d3..a5d1a82733 100644 --- a/src/compiler/compile/render_ssr/index.ts +++ b/src/compiler/compile/render_ssr/index.ts @@ -41,24 +41,24 @@ export default function ssr( // TODO remove this, just use component.vars everywhere const props = component.vars.filter(variable => !variable.module && variable.export_name); - if (component.javascript) { - component.rewrite_props(({ name }) => { - const value = `$${name}`; + component.rewrite_props(({ name }) => { + const value = `$${name}`; - const get_store_value = component.helper('get_store_value'); + const get_store_value = component.helper('get_store_value'); - let insert = `${value} = ${get_store_value}(${name})`; - if (component.compile_options.dev) { - const validate_store = component.helper('validate_store'); - insert = `${validate_store}(${name}, '${name}'); ${insert}`; - } + let insert = b`${value} = ${get_store_value}(${name})`; + if (component.compile_options.dev) { + const validate_store = component.helper('validate_store'); + insert = b`${validate_store}(${name}, '${name}'); ${insert}`; + } - return insert; - }); - } + return insert; + }); + + const instance_javascript = component.extract_javascript(component.ast.instance); // TODO only do this for props with a default value - const parent_bindings = component.javascript + const parent_bindings = instance_javascript ? props.map(prop => { return `if ($$props.${prop.export_name} === void 0 && $$bindings.${prop.export_name} && ${prop.name} !== void 0) $$bindings.${prop.export_name}(${prop.name});`; }) @@ -129,7 +129,7 @@ export default function ssr( return name; }) .join(', ')};`, - component.javascript, + instance_javascript, parent_bindings.join('\n'), css.code && `$$result.css.add(#css);`, main @@ -142,7 +142,7 @@ export default function ssr( map: ${css.map ? stringify(css.map.toString()) : 'null'} };`} - ${component.module_javascript} + ${component.extract_javascript(component.ast.module)} ${component.fully_hoisted} diff --git a/src/compiler/compile/utils/invalidate.ts b/src/compiler/compile/utils/invalidate.ts index e41e1e3b56..ab08e8e444 100644 --- a/src/compiler/compile/utils/invalidate.ts +++ b/src/compiler/compile/utils/invalidate.ts @@ -30,21 +30,9 @@ export function invalidate(component: Component, scope: Scope, node: Node, names if (node.type === 'AssignmentExpression' && node.operator === '=' && nodes_match(node.left, node.right) && tail.length === 0) { return component.invalidate(head); } else { + let callee = head[0] === '$' ? `@set_store_value` : `$$invalidate`; - // TODO stores - // if (head[0] === '$') { - // code.prependRight(node.start, `${component.helper('set_store_value')}(${head.slice(1)}, `); - // } else { - // let prefix = `$$invalidate`; - - // const variable = component.var_lookup.get(head); - // if (variable.subscribable && variable.reassigned) { - // prefix = `$$subscribe_${head}($$invalidate`; - // suffix += `)`; - // } - - // code.prependRight(node.start, `${prefix}('${head}', `); - // } + const variable = component.var_lookup.get(head); const extra_args = tail.map(name => component.invalidate(name)); @@ -61,7 +49,14 @@ export function invalidate(component: Component, scope: Scope, node: Node, names }); } - return x`$$invalidate("${head}", ${node}, ${extra_args})`; + let invalidate = x`${callee}("${head}", ${node}, ${extra_args})`; + + if (variable.subscribable && variable.reassigned) { + const subscribe = `$$subscribe_${head}`; + invalidate = x`${subscribe}(${invalidate})}`; + } + + return invalidate; } }