diff --git a/.changeset/popular-mangos-rest.md b/.changeset/popular-mangos-rest.md new file mode 100644 index 0000000000..4293bf3ba0 --- /dev/null +++ b/.changeset/popular-mangos-rest.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: add $effect.active rune diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index d87f4219a3..4147afa697 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -504,6 +504,17 @@ function validate_call_expression(node, scope, path) { } 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 (parent.type !== 'ExpressionStatement') { error(node, 'invalid-effect-location'); } 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 b59526c948..4182d400eb 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) { + if (!rune || rune === '$effect') { if (init != null && is_hoistable_function(init)) { const hoistable_function = visit(init); state.hoisted.push( @@ -291,5 +291,19 @@ export const javascript_visitors_runes = { } context.next(); + }, + 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' + ) { + return b.call('$.effect_active'); + } + next(); } }; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 09adde9d27..8bea4b1d24 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -35,6 +35,7 @@ import { EACH_KEYED } from '../../../../../constants.js'; import { regex_is_valid_identifier } from '../../../patterns.js'; +import { javascript_visitors_runes } from './javascript-runes.js'; /** * Serializes each style directive into something like `$.style(element, style_property, value)` @@ -2921,5 +2922,6 @@ export const template_visitors = { ...context.state, node: b.id('$.document') }); - } + }, + CallExpression: javascript_visitors_runes.CallExpression }; 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 e008f7c4c9..cba81fa1ae 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) { + if (!rune || rune === '$effect') { declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); continue; } @@ -604,6 +604,20 @@ const javascript_visitors_runes = { } } context.next(); + }, + 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' + ) { + return b.literal(false); + } + next(); } }; @@ -1579,7 +1593,9 @@ const template_visitors = { b.stmt(b.call('$.head', b.id('$$payload'), b.arrow([b.id('$$payload')], b.block(body)))) ) ); - } + }, + // @ts-ignore: need to extract this out somehow + CallExpression: javascript_visitors_runes.CallExpression }; /** diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a082adda2e..57ee1ac24f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1143,6 +1143,13 @@ function internal_create_effect(type, init, sync, block, schedule) { return signal; } +/** + * @returns {boolean} + */ +export function effect_active() { + return current_effect ? (current_effect.f & MANAGED) === 0 : false; +} + /** * @param {() => void | (() => void)} init * @returns {import('./types.js').EffectSignal} diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js index a0796cd37d..be6459b7df 100644 --- a/packages/svelte/src/internal/index.js +++ b/packages/svelte/src/internal/index.js @@ -35,7 +35,8 @@ export { onDestroy, pop, push, - reactive_import + reactive_import, + effect_active } from './client/runtime.js'; export * from './client/validate.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/effect-active/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-active/_config.js new file mode 100644 index 0000000000..69ba5793b8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-active/_config.js @@ -0,0 +1,15 @@ +import { test } from '../../test'; + +export default test({ + ssrHtml: ` +
false
+false
+false
+ `, + + html: ` +false
+true
+true
+ ` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-active/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-active/main.svelte new file mode 100644 index 0000000000..a65a355eef --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-active/main.svelte @@ -0,0 +1,11 @@ + + +{foo}
+{bar}
+{$effect.active()}