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 1116afc330..20cec7a7c1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -65,18 +65,20 @@ export function serialize_get_binding(node, state) { return binding.expression; } - if (binding.kind === 'prop' && binding.node.name === '$$props') { - // Special case for $$props which only exists in the old world - return node; - } + if (binding.kind === 'prop') { + if (binding.node.name === '$$props') { + // Special case for $$props which only exists in the old world + // TODO this probably shouldn't have a 'prop' binding kind + return node; + } - if ( - binding.kind === 'prop' && - !(state.analysis.immutable ? binding.reassigned : binding.mutated) && - !binding.initial && - !state.analysis.accessors - ) { - return b.call(node); + if ( + !state.analysis.accessors && + !(state.analysis.immutable ? binding.reassigned : binding.mutated) && + !binding.initial + ) { + return b.member(b.id('$$props'), node); + } } if (binding.kind === 'legacy_reactive_import') { @@ -343,67 +345,53 @@ export function serialize_hoistable_params(node, context) { } /** - * - * @param {import('#compiler').Binding} binding * @param {import('./types').ComponentClientTransformState} state * @param {string} name - * @param {import('estree').Expression | null} [default_value] + * @param {import('estree').Expression | null} [initial] * @returns */ -export function get_props_method(binding, state, name, default_value) { +export function get_prop_source(state, name, initial) { /** @type {import('estree').Expression[]} */ const args = [b.id('$$props'), b.literal(name)]; - // Use $.prop_source in the following cases: - // - accessors/mutated: needs to be able to set the prop value from within - // - default value: we set the fallback value only initially, and it's not possible to know this timing in $.prop - const needs_source = - default_value || - state.analysis.accessors || - (state.analysis.immutable ? binding.reassigned : binding.mutated); - - if (needs_source) { - let flags = 0; + let flags = 0; - /** @type {import('estree').Expression | undefined} */ - let arg; + if (state.analysis.immutable) { + flags |= PROPS_IS_IMMUTABLE; + } - if (state.analysis.immutable) { - flags |= PROPS_IS_IMMUTABLE; - } + if (state.analysis.runes) { + flags |= PROPS_IS_RUNES; + } - if (state.analysis.runes) { - flags |= PROPS_IS_RUNES; - } + /** @type {import('estree').Expression | undefined} */ + let arg; - if (default_value) { - // To avoid eagerly evaluating the right-hand-side, we wrap it in a thunk if necessary - if (is_simple_expression(default_value)) { - arg = default_value; + if (initial) { + // To avoid eagerly evaluating the right-hand-side, we wrap it in a thunk if necessary + if (is_simple_expression(initial)) { + arg = initial; + } else { + if ( + initial.type === 'CallExpression' && + initial.callee.type === 'Identifier' && + initial.arguments.length === 0 + ) { + arg = initial.callee; } else { - if ( - default_value.type === 'CallExpression' && - default_value.callee.type === 'Identifier' && - default_value.arguments.length === 0 - ) { - arg = default_value.callee; - } else { - arg = b.thunk(default_value); - } - - flags |= PROPS_CALL_DEFAULT_VALUE; + arg = b.thunk(initial); } - } - if (flags || arg) { - args.push(b.literal(flags)); - if (arg) args.push(arg); + flags |= PROPS_CALL_DEFAULT_VALUE; } + } - return b.call('$.prop_source', ...args); + if (flags || arg) { + args.push(b.literal(flags)); + if (arg) args.push(arg); } - return b.call('$.prop', ...args); + return b.call('$.prop_source', ...args); } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js index bfc7998b86..ab2e4e3aaf 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-legacy.js @@ -1,7 +1,7 @@ import { is_hoistable_function } from '../../utils.js'; import * as b from '../../../../utils/builders.js'; import { extract_paths } from '../../../../utils/ast.js'; -import { create_state_declarators, get_props_method, serialize_get_binding } from '../utils.js'; +import { create_state_declarators, get_prop_source, serialize_get_binding } from '../utils.js'; /** @type {import('../types.js').ComponentVisitors} */ export const javascript_visitors_legacy = { @@ -54,8 +54,8 @@ export const javascript_visitors_legacy = { declarations.push( b.declarator( path.node, - binding.kind === 'prop' || binding.kind === 'rest_prop' - ? get_props_method(binding, state, binding.prop_alias ?? name, value) + binding.kind === 'prop' + ? get_prop_source(state, binding.prop_alias ?? name, value) : value ) ); @@ -67,17 +67,23 @@ export const javascript_visitors_legacy = { state.scope.get(declarator.id.name) ); - declarations.push( - b.declarator( - declarator.id, - get_props_method( - binding, - state, - binding.prop_alias ?? declarator.id.name, - declarator.init && /** @type {import('estree').Expression} */ (visit(declarator.init)) + if ( + state.analysis.accessors || + (state.analysis.immutable ? binding.reassigned : binding.mutated) || + declarator.init + ) { + declarations.push( + b.declarator( + declarator.id, + get_prop_source( + state, + binding.prop_alias ?? declarator.id.name, + declarator.init && + /** @type {import('estree').Expression} */ (visit(declarator.init)) + ) ) - ) - ); + ); + } continue; } 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 e84efe1582..3bc15943b5 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 @@ -2,7 +2,7 @@ import { get_rune } from '../../../scope.js'; import { is_hoistable_function } from '../../utils.js'; import * as b from '../../../../utils/builders.js'; import * as assert from '../../../../utils/assert.js'; -import { create_state_declarators, get_props_method, should_proxy } from '../utils.js'; +import { create_state_declarators, get_prop_source, should_proxy } from '../utils.js'; import { unwrap_ts_expression } from '../../../../utils/ast.js'; /** @type {import('../types.js').ComponentVisitors} */ @@ -165,36 +165,27 @@ export const javascript_visitors_runes = { for (const property of declarator.id.properties) { if (property.type === 'Property') { - assert.ok(property.key.type === 'Identifier' || property.key.type === 'Literal'); - let name; - if (property.key.type === 'Identifier') { - name = property.key.name; - } else if (property.key.type === 'Literal') { - name = /** @type {string} */ (property.key.value).toString(); - } else { - throw new Error('unreachable'); - } + const key = /** @type {import('estree').Identifier | import('estree').Literal} */ ( + property.key + ); + const name = key.type === 'Identifier' ? key.name : /** @type {string} */ (key.value); seen.push(name); - if (property.value.type === 'Identifier') { - const binding = /** @type {import('#compiler').Binding} */ ( - state.scope.get(property.value.name) - ); - declarations.push( - b.declarator(property.value, get_props_method(binding, state, name)) - ); - } else if (property.value.type === 'AssignmentPattern') { - assert.equal(property.value.left.type, 'Identifier'); - const binding = /** @type {import('#compiler').Binding} */ ( - state.scope.get(property.value.left.name) - ); - declarations.push( - b.declarator( - property.value.left, - get_props_method(binding, state, name, property.value.right) - ) - ); + let id = property.value; + let initial = undefined; + + if (property.value.type === 'AssignmentPattern') { + id = property.value.left; + initial = property.value.right; + } + + assert.equal(id.type, 'Identifier'); + + const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); + + if (binding.reassigned || state.analysis.accessors || initial) { + declarations.push(b.declarator(id, get_prop_source(state, name, initial))); } } else { // RestElement diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1729556b13..1d5630d1d2 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1490,17 +1490,6 @@ export function prop_source(props, key, flags, default_value) { return /** @type {import('./types.js').Signal} */ (source_signal); } -/** - * If the prop is readonly and has no fallback value, we can use this function, else we need to use `prop_source`. - * @param {Record} props - * @param {string} key - * @returns {any} - */ -export function prop(props, key) { - // TODO skip this, and rewrite as `$$props.foo` - return () => props[key]; -} - /** * @param {boolean} immutable * @param {unknown} a diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js index a94a0de180..a48a454923 100644 --- a/packages/svelte/src/internal/index.js +++ b/packages/svelte/src/internal/index.js @@ -7,7 +7,6 @@ export { source, mutable_source, derived, - prop, prop_source, user_effect, render_effect,