From e0af98ba51a200ad1a4ad17b2bbef5dc7d339a72 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:25:10 -0800 Subject: [PATCH] perf: hoist variables which are not mutated or reassigned --- .changeset/twelve-peaches-jam.md | 5 +++++ .../client/visitors/javascript-runes.js | 20 ++++++++++++++++++- .../samples/hoist-unmodified-var/_config.js | 3 +++ .../_expected/client/index.svelte.js | 19 ++++++++++++++++++ .../_expected/server/index.svelte.js | 12 +++++++++++ .../samples/hoist-unmodified-var/index.svelte | 7 +++++++ 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 .changeset/twelve-peaches-jam.md create mode 100644 packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte diff --git a/.changeset/twelve-peaches-jam.md b/.changeset/twelve-peaches-jam.md new file mode 100644 index 0000000000..db5cf85bb5 --- /dev/null +++ b/.changeset/twelve-peaches-jam.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: hoist variables which are not mutated or reassigned diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 7f656dc690..eb8452bbf3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -151,12 +151,30 @@ export const javascript_visitors_runes = { return { ...node, body }; }, - VariableDeclaration(node, { state, visit }) { + VariableDeclaration(node, { path, state, visit }) { const declarations = []; for (const declarator of node.declarations) { const init = unwrap_ts_expression(declarator.init); const rune = get_rune(init, state.scope); + + if (!rune && init != null && declarator.id.type === 'Identifier') { + const is_top_level = path.at(-1)?.type === 'Program'; + const binding = state.scope.owner(declarator.id.name)?.declarations.get(declarator.id.name); + // 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 + if ( + is_top_level && + binding && + !binding.mutated && + !binding.reassigned && + binding?.initial?.type === 'Literal' + ) { + state.hoisted.push(b.declaration('const', declarator.id, init)); + continue; + } + } + if (!rune || rune === '$effect.active' || rune === '$effect.root' || rune === '$inspect') { if (init != null && is_hoistable_function(init)) { const hoistable_function = visit(init); diff --git a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_config.js b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); 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 new file mode 100644 index 0000000000..48d21db804 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/client/index.svelte.js @@ -0,0 +1,19 @@ +// index.svelte (Svelte VERSION) +// Note: compiler output will change before 5.0 is released! +import "svelte/internal/disclose-version"; +import * as $ from "svelte/internal"; + +const count = 0; +var frag = $.template(`

`); + +export default function Hoist_unmodified_var($$anchor, $$props) { + $.push($$props, true); + + /* Init */ + var p = $.open($$anchor, true, frag); + var text = $.child(p); + + text.nodeValue = $.stringify(count); + $.close($$anchor, p); + $.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 new file mode 100644 index 0000000000..b9ae604e22 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/_expected/server/index.svelte.js @@ -0,0 +1,12 @@ +// index.svelte (Svelte VERSION) +// Note: compiler output will change before 5.0 is released! +import * as $ from "svelte/internal/server"; + +export default function Hoist_unmodified_var($$payload, $$props) { + $.push(true); + + let count = 0; + + $$payload.out += `

${$.escape_text(count)}

`; + $.pop(); +} diff --git a/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte new file mode 100644 index 0000000000..fffe06f9af --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/hoist-unmodified-var/index.svelte @@ -0,0 +1,7 @@ + + + + +

{count}