diff --git a/src/compiler/compile/nodes/Attribute.ts b/src/compiler/compile/nodes/Attribute.ts index ef413c31a2..94dea766ad 100644 --- a/src/compiler/compile/nodes/Attribute.ts +++ b/src/compiler/compile/nodes/Attribute.ts @@ -5,27 +5,28 @@ import Node from './shared/Node'; import Element from './Element'; import Text from './Text'; import Expression from './shared/Expression'; +import TemplateScope from './shared/TemplateScope'; export default class Attribute extends Node { type: 'Attribute'; start: number; end: number; + scope: TemplateScope; component: Component; parent: Element; name: string; is_spread: boolean; is_true: boolean; - is_dynamic: boolean; is_static: boolean; is_synthetic: boolean; - should_cache: boolean; expression?: Expression; chunks: Array; dependencies: Set; constructor(component, parent, scope, info) { super(component, parent, scope, info); + this.scope = scope; if (info.type === 'Spread') { this.name = null; @@ -37,9 +38,7 @@ export default class Attribute extends Node { this.dependencies = this.expression.dependencies; this.chunks = null; - this.is_dynamic = true; // TODO not necessarily this.is_static = false; - this.should_cache = false; // TODO does this mean anything here? } else { @@ -62,22 +61,13 @@ export default class Attribute extends Node { add_to_set(this.dependencies, expression.dependencies); return expression; }); - - this.is_dynamic = this.dependencies.size > 0; - - this.should_cache = this.is_dynamic - ? this.chunks.length === 1 - // @ts-ignore todo: probably error - ? this.chunks[0].node.type !== 'Identifier' || scope.names.has(this.chunks[0].node.name) - : true - : false; } } get_dependencies() { if (this.is_spread) return this.expression.dynamic_dependencies(); - const dependencies = new Set(); + const dependencies: Set = new Set(); this.chunks.forEach(chunk => { if (chunk.type === 'Expression') { add_to_set(dependencies, chunk.dynamic_dependencies()); @@ -113,7 +103,7 @@ export default class Attribute extends Node { } get_static_value() { - if (this.is_spread || this.is_dynamic) return null; + if (this.is_spread || this.dependencies.size > 0) return null; return this.is_true ? true @@ -122,4 +112,13 @@ export default class Attribute extends Node { ? (this.chunks[0] as Text).data : ''; } + + should_cache() { + return this.is_static + ? false + : this.chunks.length === 1 + // @ts-ignore todo: probably error + ? this.chunks[0].node.type !== 'Identifier' || this.scope.names.has(this.chunks[0].node.name) + : true; + } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index 3ced97dcf7..8c92fdfcdf 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -70,7 +70,8 @@ export default class AttributeWrapper { const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input'; - if (this.node.is_dynamic) { + const dependencies = this.node.get_dependencies(); + if (dependencies.length > 0) { let value; // TODO some of this code is repeated in Tag.ts — would be good to @@ -92,7 +93,7 @@ export default class AttributeWrapper { const is_select_value_attribute = name === 'value' && element.node.name === 'select'; - const should_cache = (this.node.should_cache || is_select_value_attribute); + const should_cache = (this.node.should_cache() || is_select_value_attribute); const last = should_cache && block.get_unique_name( `${element.var}_${name.replace(/[^a-zA-Z_$]/g, '_')}_value` @@ -147,25 +148,21 @@ export default class AttributeWrapper { updater = `${method}(${element.var}, "${name}", ${should_cache ? last : value});`; } - // only add an update if mutations are involved (or it's a select?) - const dependencies = this.node.get_dependencies(); - if (dependencies.length > 0 || is_select_value_attribute) { - const changed_check = ( - (block.has_outros ? `!#current || ` : '') + - dependencies.map(dependency => `changed.${dependency}`).join(' || ') - ); + const changed_check = ( + (block.has_outros ? `!#current || ` : '') + + dependencies.map(dependency => `changed.${dependency}`).join(' || ') + ); - const update_cached_value = `${last} !== (${last} = ${value})`; + const update_cached_value = `${last} !== (${last} = ${value})`; - const condition = should_cache - ? (dependencies.length ? `(${changed_check}) && ${update_cached_value}` : update_cached_value) - : changed_check; + const condition = should_cache + ? (dependencies.length ? `(${changed_check}) && ${update_cached_value}` : update_cached_value) + : changed_check; - block.builders.update.add_conditional( - condition, - updater - ); - } + block.builders.update.add_conditional( + condition, + updater + ); } else { const value = this.node.get_value(block); @@ -189,7 +186,7 @@ export default class AttributeWrapper { const update_value = `${element.var}.value = ${element.var}.__value;`; block.builders.hydrate.add_line(update_value); - if (this.node.is_dynamic) block.builders.update.add_line(update_value); + if (this.node.get_dependencies().length > 0) block.builders.update.add_line(update_value); } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 9e267b9ad0..540e0b67fd 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -555,8 +555,9 @@ export default class ElementWrapper extends Wrapper { add_attributes(block: Block) { // Get all the class dependencies first this.attributes.forEach((attribute) => { - if (attribute.node.name === 'class' && attribute.node.is_dynamic) { - this.class_dependencies.push(...attribute.node.dependencies); + if (attribute.node.name === 'class') { + const dependencies = attribute.node.get_dependencies(); + this.class_dependencies.push(...dependencies); } }); diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index fb3a4186db..b50f1d1bd1 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -168,7 +168,9 @@ export default class InlineComponentWrapper extends Wrapper { const non_let_dependencies = Array.from(fragment_dependencies).filter(name => !this.node.scope.is_let(name)); - if (!uses_spread && (this.node.attributes.filter(a => a.is_dynamic).length || this.node.bindings.length || non_let_dependencies.length > 0)) { + const dynamic_attributes = this.node.attributes.filter(a => a.get_dependencies().length > 0); + + if (!uses_spread && (dynamic_attributes.length > 0 || this.node.bindings.length > 0 || non_let_dependencies.length > 0)) { updates.push(`var ${name_changes} = {};`); } @@ -225,19 +227,16 @@ export default class InlineComponentWrapper extends Wrapper { ]) : {}` : '{}'}; `); } else { - this.node.attributes - .filter((attribute: Attribute) => attribute.is_dynamic) - .forEach((attribute: Attribute) => { - if (attribute.dependencies.size > 0) { - /* eslint-disable @typescript-eslint/indent,indent */ - updates.push(deindent` - if (${[...attribute.dependencies] - .map(dependency => `changed.${dependency}`) - .join(' || ')}) ${name_changes}${quote_prop_if_necessary(attribute.name)} = ${attribute.get_value(block)}; - `); - /* eslint-enable @typescript-eslint/indent,indent */ - } - }); + dynamic_attributes.forEach((attribute: Attribute) => { + const dependencies = attribute.get_dependencies(); + if (dependencies.length > 0) { + const condition = dependencies.map(dependency => `changed.${dependency}`).join(' || '); + + updates.push(deindent` + if (${condition}) ${name_changes}${quote_prop_if_necessary(attribute.name)} = ${attribute.get_value(block)}; + `); + } + }); } } diff --git a/test/js/samples/component-static-var/expected.js b/test/js/samples/component-static-var/expected.js new file mode 100644 index 0000000000..a41178a441 --- /dev/null +++ b/test/js/samples/component-static-var/expected.js @@ -0,0 +1,112 @@ +/* generated by Svelte vX.Y.Z */ +import { + SvelteComponent, + destroy_component, + detach, + element, + init, + insert, + listen, + mount_component, + safe_not_equal, + set_input_value, + space, + transition_in, + transition_out +} from "svelte/internal"; +import Foo from "./Foo.svelte"; +import Bar from "./Bar.svelte"; + +function create_fragment(ctx) { + var t0, t1, input, current, dispose; + + var foo = new Foo({ props: { x: y } }); + + var bar = new Bar({ props: { x: ctx.z } }); + + return { + c() { + foo.$$.fragment.c(); + t0 = space(); + bar.$$.fragment.c(); + t1 = space(); + input = element("input"); + dispose = listen(input, "input", ctx.input_input_handler); + }, + + m(target, anchor) { + mount_component(foo, target, anchor); + insert(target, t0, anchor); + mount_component(bar, target, anchor); + insert(target, t1, anchor); + insert(target, input, anchor); + + set_input_value(input, ctx.z); + + current = true; + }, + + p(changed, ctx) { + var bar_changes = {}; + if (changed.z) bar_changes.x = ctx.z; + bar.$set(bar_changes); + + if (changed.z && (input.value !== ctx.z)) set_input_value(input, ctx.z); + }, + + i(local) { + if (current) return; + transition_in(foo.$$.fragment, local); + + transition_in(bar.$$.fragment, local); + + current = true; + }, + + o(local) { + transition_out(foo.$$.fragment, local); + transition_out(bar.$$.fragment, local); + current = false; + }, + + d(detaching) { + destroy_component(foo, detaching); + + if (detaching) { + detach(t0); + } + + destroy_component(bar, detaching); + + if (detaching) { + detach(t1); + detach(input); + } + + dispose(); + } + }; +} + +let y = 1; + +function instance($$self, $$props, $$invalidate) { + + let z = 2; + + function input_input_handler() { + z = this.value; + $$invalidate('z', z); + } + + return { z, input_input_handler }; +} + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, []); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/component-static-var/input.svelte b/test/js/samples/component-static-var/input.svelte new file mode 100644 index 0000000000..a9621b9726 --- /dev/null +++ b/test/js/samples/component-static-var/input.svelte @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file