diff --git a/.changeset/hoist-rest-excludes.md b/.changeset/hoist-rest-excludes.md new file mode 100644 index 0000000000..52efb06092 --- /dev/null +++ b/.changeset/hoist-rest-excludes.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: hoist `rest_props` exclude list as a module-scope `Set` diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index 72685a8e83..b9f4690179 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -49,8 +49,13 @@ export function VariableDeclaration(node, context) { } if (declarator.id.type === 'Identifier') { + const exclude_id = context.state.scope.root.unique('rest_excludes'); + context.state.hoisted.push( + b.var(exclude_id, b.new('Set', b.array(seen.map((name) => b.literal(name))))) + ); + /** @type {Expression[]} */ - const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; + const args = [b.id('$$props'), exclude_id]; if (dev) { // include rest name, so we can provide informative error messages @@ -95,8 +100,13 @@ export function VariableDeclaration(node, context) { } } else { // RestElement + const exclude_id = context.state.scope.root.unique('rest_excludes'); + context.state.hoisted.push( + b.var(exclude_id, b.new('Set', b.array(seen.map((name) => b.literal(name))))) + ); + /** @type {Expression[]} */ - const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; + const args = [b.id('$$props'), exclude_id]; if (dev) { // include rest name, so we can provide informative error messages diff --git a/packages/svelte/src/compiler/utils/builders.js b/packages/svelte/src/compiler/utils/builders.js index 7508caf3e7..1f48f7fd8b 100644 --- a/packages/svelte/src/compiler/utils/builders.js +++ b/packages/svelte/src/compiler/utils/builders.js @@ -686,7 +686,8 @@ export { if_builder as if, this_instance as this, null_instance as null, - debugger_builder as debugger + debugger_builder as debugger, + new_builder as new }; /** diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 5626639a84..c2f3698809 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -49,11 +49,11 @@ export function update_pre_prop(fn, d = 1) { /** * The proxy handler for rest props (i.e. `const { x, ...rest } = $props()`). * Is passed the full `$$props` object and excludes the named props. - * @type {ProxyHandler<{ props: Record, exclude: Array, name?: string }>}} + * @type {ProxyHandler<{ props: Record, exclude: Set, name?: string }>}} */ const rest_props_handler = { get(target, key) { - if (target.exclude.includes(key)) return; + if (target.exclude.has(key)) return; return target.props[key]; }, set(target, key) { @@ -65,7 +65,7 @@ const rest_props_handler = { return false; }, getOwnPropertyDescriptor(target, key) { - if (target.exclude.includes(key)) return; + if (target.exclude.has(key)) return; if (key in target.props) { return { enumerable: true, @@ -75,17 +75,17 @@ const rest_props_handler = { } }, has(target, key) { - if (target.exclude.includes(key)) return false; + if (target.exclude.has(key)) return false; return key in target.props; }, ownKeys(target) { - return Reflect.ownKeys(target.props).filter((key) => !target.exclude.includes(key)); + return Reflect.ownKeys(target.props).filter((key) => !target.exclude.has(key)); } }; /** * @param {Record} props - * @param {string[]} exclude + * @param {Set} exclude * @param {string} [name] * @returns {Record} */ diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js index 5a46b9bbef..7df616f694 100644 --- a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js @@ -1,10 +1,12 @@ import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; +var rest_excludes = new Set(['$$slots', '$$events', '$$legacy']); + export default function Props_identifier($$anchor, $$props) { $.push($$props, true); - let props = $.rest_props($$props, ['$$slots', '$$events', '$$legacy']); + let props = $.rest_props($$props, rest_excludes); $$props.a; props[a];