diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 86aa472dcd..a3450bdb26 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -543,12 +543,22 @@ export function should_proxy_or_freeze(node) { * @returns {boolean} */ export function can_inline_variable(binding, name) { + return can_hoist_declaration(binding, name) || (!!binding && !binding.scope.has_parent()); +} + +/** + * @param {import('#compiler').Binding | undefined} binding + * @param {string} name + * @returns {boolean} + */ +export function can_hoist_declaration(binding, name) { return ( !!binding && binding.kind === 'normal' && binding.scope.is_top_level && - // TODO: allow object expressions that are not passed to functions or components as props - // and expressions as long as they do not reference non-hoistable variables + // For now we just allow primitives for simplicity. We could allow object expressions that are + // not passed to functions or components as props and expressions as long as they do not + // reference functions, constructors, non-hoistable variables, etc. binding.initial?.type === 'Literal' && // Checking that it's not mutated or reassigned is a bit simplistic // If it's not state and thus not reactive, we could hoist the variable and mutation @@ -558,16 +568,6 @@ export function can_inline_variable(binding, name) { // Avoid conflicts. It would be nice to rename the variable, but keeping it simple for now !binding.scope.declared_in_outer_scope(name) && !GlobalBindings.has(name) - ); -} - -/** - * @param {import('#compiler').Binding | undefined} binding - * @param {string} name - * @returns {boolean} - */ -export function can_hoist_declaration(binding, name) { - return ( - can_inline_variable(binding, name) && !!binding && binding.scope.has_parent() // i.e. not when context="module" + && binding.scope.has_parent() // i.e. not when context="module" ); } diff --git a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/client/index.svelte.js index 46e589c5c9..16368c4715 100644 --- a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/client/index.svelte.js @@ -4,11 +4,12 @@ import "svelte/internal/disclose-version"; const o = 'o'; const d = 'd'; +const url = new URL('foobar.png', 'https://www.example.com/').href; import * as $ from "svelte/internal"; const boolean = false; -var frag = $.template(`

boolean is ${$.stringify(boolean)} and autocapitalize is w${$.stringify(o)}r${$.stringify(d)}s

`); +var frag = $.template(`

boolean is ${$.stringify(boolean)} and autocapitalize is w${$.stringify(o)}r${$.stringify(d)}s

example`, true); export default function Hoist_unmodified_var($$anchor, $$props) { $.push($$props, true); @@ -18,12 +19,14 @@ export default function Hoist_unmodified_var($$anchor, $$props) { value += 'd'; /* Init */ - var p = $.open($$anchor, true, frag); + var fragment = $.open_frag($$anchor, true, frag); + var node = $.child_frag(fragment); - $.attr(p, "itemid", `w${$.stringify(o)}r${$.stringify(value)}s`); + $.attr(node, "itemid", `w${$.stringify(o)}r${$.stringify(value)}s`); - var text = $.child(p); + var text = $.child(node); + var img = $.sibling($.sibling(node)); - $.close($$anchor, p); + $.close_frag($$anchor, fragment); $.pop(); } diff --git a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/server/index.svelte.js index 20d3af1f0c..90c098232d 100644 --- a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/server/index.svelte.js @@ -4,6 +4,7 @@ import * as $ from "svelte/internal/server"; const o = 'o'; const d = 'd'; +const url = new URL('foobar.png', 'https://www.example.com/').href; export default function Hoist_unmodified_var($$payload, $$props) { $.push(true); @@ -12,6 +13,6 @@ export default function Hoist_unmodified_var($$payload, $$props) { let value = 'd'; value += 'd'; - $$payload.out += `boolean is ${$.escape(boolean)} and autocapitalize is w${$.escape(o)}r${$.escape(d)}s

`; + $$payload.out += `boolean is ${$.escape(boolean)} and autocapitalize is w${$.escape(o)}r${$.escape(d)}s

`; $.pop(); -} \ No newline at end of file +} diff --git a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte index 261937285c..ba4d51328e 100644 --- a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte @@ -3,6 +3,7 @@

boolean is {boolean} and autocapitalize is w{o}r{d}s

+ +example