diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 4147afa697..805f673974 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -503,24 +503,19 @@ function validate_call_expression(node, scope, path) { error(node, rune === '$derived' ? 'invalid-derived-location' : 'invalid-state-location'); } - if (rune === '$effect') { - const callee = node.callee; - if ( - callee.type === 'MemberExpression' && - callee.property.type === 'Identifier' && - callee.property.name === 'active' - ) { - if (node.arguments.length !== 0) { - error(node, 'invalid-rune-args-length', '$effect.active', [0]); - } - return; - } + if (rune === '$effect' || rune === '$effect.pre') { if (parent.type !== 'ExpressionStatement') { error(node, 'invalid-effect-location'); } if (node.arguments.length !== 1) { - error(node, 'invalid-rune-args-length', '$effect', [1]); + error(node, 'invalid-rune-args-length', rune, [1]); + } + } + + if (rune === '$effect.active') { + if (node.arguments.length !== 0) { + error(node, 'invalid-rune-args-length', '$effect.active', [0]); } } } 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 4182d400eb..1aa78b14af 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 @@ -135,7 +135,7 @@ export const javascript_visitors_runes = { for (const declarator of node.declarations) { const init = declarator.init; const rune = get_rune(init, state.scope); - if (!rune || rune === '$effect') { + if (!rune || rune === '$effect.active') { if (init != null && is_hoistable_function(init)) { const hoistable_function = visit(init); state.hoisted.push( @@ -294,16 +294,11 @@ export const javascript_visitors_runes = { }, CallExpression(node, { state, next }) { const rune = get_rune(node, state.scope); - const callee = node.callee; - - if ( - rune === '$effect' && - callee.type === 'MemberExpression' && - callee.property.type === 'Identifier' && - callee.property.name === 'active' - ) { + + if (rune === '$effect.active') { return b.call('$.effect_active'); } + next(); } }; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index cba81fa1ae..78356d8f5f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -550,7 +550,7 @@ const javascript_visitors_runes = { for (const declarator of node.declarations) { const rune = get_rune(declarator.init, state.scope); - if (!rune || rune === '$effect') { + if (!rune || rune === '$effect.active') { declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); continue; } @@ -607,16 +607,11 @@ const javascript_visitors_runes = { }, CallExpression(node, { state, next }) { const rune = get_rune(node, state.scope); - const callee = node.callee; - if ( - rune === '$effect' && - callee.type === 'MemberExpression' && - callee.property.type === 'Identifier' && - callee.property.name === 'active' - ) { + if (rune === '$effect.active') { return b.literal(false); } + next(); } }; diff --git a/packages/svelte/src/compiler/phases/constants.js b/packages/svelte/src/compiler/phases/constants.js index b0fbe2ecd8..394168ea53 100644 --- a/packages/svelte/src/compiler/phases/constants.js +++ b/packages/svelte/src/compiler/phases/constants.js @@ -110,7 +110,7 @@ export const ElementBindings = [ 'indeterminate' ]; -export const Runes = ['$state', '$props', '$derived', '$effect']; +export const Runes = ['$state', '$props', '$derived', '$effect', '$effect.pre', '$effect.active']; /** * Whitespace inside one of these elements will not result in diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 8d7cb526d4..302e182c95 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -668,15 +668,31 @@ export function set_scope(scopes) { /** * Returns the name of the rune if the given expression is a `CallExpression` using a rune. - * @param {import('estree').Expression | null | undefined} node + * @param {import('estree').Node | null | undefined} node * @param {Scope} scope */ export function get_rune(node, scope) { - const callee = get_callee_name(node); - if (callee === null || !Runes.includes(callee)) return null; + if (!node) return null; + if (node.type !== 'CallExpression') return null; - const binding = scope.get(callee); + let n = node.callee; + + let joined = ''; + + while (n.type === 'MemberExpression') { + if (n.computed) return null; + if (n.property.type !== 'Identifier') return null; + joined = '.' + n.property.name + joined; + n = n.object; + } + + if (n.type !== 'Identifier') return null; + + joined = n.name + joined; + if (!Runes.includes(joined)) return null; + + const binding = scope.get(n.name); if (binding !== null) return null; // rune name, but references a variable or store - return callee; + return joined; }