From 768251ec5e42547fc985308925bef47d9f0da902 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 18 Dec 2024 19:56:31 +0000 Subject: [PATCH 1/3] feat: add $effect.active rune --- .changeset/eighty-hairs-remain.md | 5 ++++ .../2-analyze/visitors/CallExpression.js | 1 + .../phases/2-analyze/visitors/Identifier.js | 4 ---- .../client/visitors/CallExpression.js | 3 +++ .../client/visitors/VariableDeclaration.js | 1 + .../server/visitors/CallExpression.js | 2 +- .../server/visitors/VariableDeclaration.js | 8 ++++++- packages/svelte/src/internal/client/index.js | 1 + .../src/internal/client/reactivity/effects.js | 8 +++++++ packages/svelte/src/utils.js | 1 + .../samples/effect-active-rune/_config.js | 8 ------- .../samples/effect-active-rune/main.svelte.js | 1 - .../samples/effect-active/_config.js | 19 +++++++++++++++ .../samples/effect-active/main.svelte | 19 +++++++++++++++ packages/svelte/types/index.d.ts | 24 ++++++++++--------- 15 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 .changeset/eighty-hairs-remain.md delete mode 100644 packages/svelte/tests/compiler-errors/samples/effect-active-rune/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/effect-active-rune/main.svelte.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-active/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-active/main.svelte diff --git a/.changeset/eighty-hairs-remain.md b/.changeset/eighty-hairs-remain.md new file mode 100644 index 000000000000..55cd8e1fcd74 --- /dev/null +++ b/.changeset/eighty-hairs-remain.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: add $effect.active rune diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 6a301726b106..b4a69e5a24c9 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -109,6 +109,7 @@ export function CallExpression(node, context) { break; + case '$effect.active': case '$effect.tracking': if (node.arguments.length !== 0) { e.rune_invalid_arguments(node, rune); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index 79dccd5a7cf5..4de72a4217c1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -53,10 +53,6 @@ export function Identifier(node, context) { parent = /** @type {Expression} */ (context.path[--i]); if (!is_rune(name)) { - if (name === '$effect.active') { - e.rune_renamed(parent, '$effect.active', '$effect.tracking'); - } - if (name === '$state.frozen') { e.rune_renamed(parent, '$state.frozen', '$state.raw'); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index 7a3057451aa1..9f1e355703cb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -17,6 +17,9 @@ export function CallExpression(node, context) { case '$effect.tracking': return b.call('$.effect_tracking'); + case '$effect.active': + return b.call('$.effect_active'); + case '$state.snapshot': return b.call( '$.snapshot', 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 afb90bbec7f9..dea8924e4730 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 @@ -25,6 +25,7 @@ export function VariableDeclaration(node, context) { if ( !rune || rune === '$effect.tracking' || + rune === '$effect.active' || rune === '$effect.root' || rune === '$inspect' || rune === '$inspect.trace' || diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index 386c6b6ff393..ee2b18c889c3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -16,7 +16,7 @@ export function CallExpression(node, context) { return b.id('undefined'); } - if (rune === '$effect.tracking') { + if (rune === '$effect.tracking' || rune === '$effect.active') { return b.literal(false); } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index 31de811ac76f..b3e875b7d084 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -19,7 +19,13 @@ export function VariableDeclaration(node, context) { for (const declarator of node.declarations) { const init = declarator.init; const rune = get_rune(init, context.state.scope); - if (!rune || rune === '$effect.tracking' || rune === '$inspect' || rune === '$effect.root') { + if ( + !rune || + rune === '$effect.tracking' || + rune === '$effect.active' || + rune === '$inspect' || + rune === '$effect.root' + ) { declarations.push(/** @type {VariableDeclarator} */ (context.visit(declarator))); continue; } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index f22c33babc52..8113bfd0dd18 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -100,6 +100,7 @@ export { export { derived, derived_safe_equal } from './reactivity/deriveds.js'; export { effect_tracking, + effect_active, effect_root, legacy_pre_effect, legacy_pre_effect_reset, diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index bf890627f7e0..adbaa1dbe481 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -173,6 +173,14 @@ export function effect_tracking() { return !skip_reaction; } +/** + * Internal representation of `$effect.active()` + * @returns {boolean} + */ +export function effect_active() { + return active_effect !== null; +} + /** * @param {() => void} fn */ diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index e42721b4f4b4..b5e0c0496e24 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -428,6 +428,7 @@ const RUNES = /** @type {const} */ ([ '$effect', '$effect.pre', '$effect.tracking', + '$effect.active', '$effect.root', '$inspect', '$inspect().with', diff --git a/packages/svelte/tests/compiler-errors/samples/effect-active-rune/_config.js b/packages/svelte/tests/compiler-errors/samples/effect-active-rune/_config.js deleted file mode 100644 index e12e4046f36d..000000000000 --- a/packages/svelte/tests/compiler-errors/samples/effect-active-rune/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'rune_renamed', - message: '`$effect.active` is now `$effect.tracking`' - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/effect-active-rune/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/effect-active-rune/main.svelte.js deleted file mode 100644 index c33c104aac31..000000000000 --- a/packages/svelte/tests/compiler-errors/samples/effect-active-rune/main.svelte.js +++ /dev/null @@ -1 +0,0 @@ -$effect.active(); 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 000000000000..8629829c6451 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-active/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; + +export default test({ + ssrHtml: ` +

false

+

false

+

false

+

false

+

false

+ `, + + html: ` +

false

+

true

+

true

+

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 000000000000..bf488752db67 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-active/main.svelte @@ -0,0 +1,19 @@ + + + + +

{mod}

+

{foo}

+

{bar}

+

{(bar, $effect.active())}

+

{untrack(() => (bar, $effect.active()))}

diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 671f68bff72a..5310cf22ecd1 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1876,10 +1876,10 @@ declare module 'svelte/motion' { * const tween = Tween.of(() => number); * * ``` - * + * */ static of(fn: () => U, options?: TweenedOptions | undefined): Tween; - + constructor(value: T, options?: TweenedOptions); /** * Sets `tween.target` to `value` and returns a `Promise` that resolves if and when `tween.current` catches up to it. @@ -1898,21 +1898,21 @@ declare module 'svelte/motion' { declare module 'svelte/reactivity' { export class SvelteDate extends Date { - + constructor(...params: any[]); #private; } export class SvelteSet extends Set { - + constructor(value?: Iterable | null | undefined); - + add(value: T): this; #private; } export class SvelteMap extends Map { - + constructor(value?: Iterable | null | undefined); - + set(key: K, value: V): this; #private; } @@ -1922,7 +1922,7 @@ declare module 'svelte/reactivity' { } const REPLACE: unique symbol; export class SvelteURLSearchParams extends URLSearchParams { - + [REPLACE](params: URLSearchParams): void; #private; } @@ -1994,7 +1994,7 @@ declare module 'svelte/reactivity' { */ export function createSubscriber(start: (update: () => void) => (() => void) | void): () => void; class ReactiveValue { - + constructor(fn: () => T, onsubscribe: (update: () => void) => void); get current(): T; #private; @@ -2059,7 +2059,7 @@ declare module 'svelte/reactivity/window' { get current(): number | undefined; }; class ReactiveValue { - + constructor(fn: () => T, onsubscribe: (update: () => void) => void); get current(): T; #private; @@ -2935,6 +2935,8 @@ declare namespace $effect { */ export function tracking(): boolean; + export function active(): boolean; + /** * The `$effect.root` rune is an advanced feature that creates a non-tracked scope that doesn't auto-cleanup. This is useful for * nested effects that you want to manually control. This rune also allows for creation of effects outside of the component @@ -3069,4 +3071,4 @@ declare namespace $inspect { */ declare function $host(): El; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file +//# sourceMappingURL=index.d.ts.map From 823f7ff3098618aada7f6bc3fc21f7035b67c197 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 18 Dec 2024 19:59:00 +0000 Subject: [PATCH 2/3] fix types --- packages/svelte/types/index.d.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 5310cf22ecd1..671f68bff72a 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1876,10 +1876,10 @@ declare module 'svelte/motion' { * const tween = Tween.of(() => number); * * ``` - * + * */ static of(fn: () => U, options?: TweenedOptions | undefined): Tween; - + constructor(value: T, options?: TweenedOptions); /** * Sets `tween.target` to `value` and returns a `Promise` that resolves if and when `tween.current` catches up to it. @@ -1898,21 +1898,21 @@ declare module 'svelte/motion' { declare module 'svelte/reactivity' { export class SvelteDate extends Date { - + constructor(...params: any[]); #private; } export class SvelteSet extends Set { - + constructor(value?: Iterable | null | undefined); - + add(value: T): this; #private; } export class SvelteMap extends Map { - + constructor(value?: Iterable | null | undefined); - + set(key: K, value: V): this; #private; } @@ -1922,7 +1922,7 @@ declare module 'svelte/reactivity' { } const REPLACE: unique symbol; export class SvelteURLSearchParams extends URLSearchParams { - + [REPLACE](params: URLSearchParams): void; #private; } @@ -1994,7 +1994,7 @@ declare module 'svelte/reactivity' { */ export function createSubscriber(start: (update: () => void) => (() => void) | void): () => void; class ReactiveValue { - + constructor(fn: () => T, onsubscribe: (update: () => void) => void); get current(): T; #private; @@ -2059,7 +2059,7 @@ declare module 'svelte/reactivity/window' { get current(): number | undefined; }; class ReactiveValue { - + constructor(fn: () => T, onsubscribe: (update: () => void) => void); get current(): T; #private; @@ -2935,8 +2935,6 @@ declare namespace $effect { */ export function tracking(): boolean; - export function active(): boolean; - /** * The `$effect.root` rune is an advanced feature that creates a non-tracked scope that doesn't auto-cleanup. This is useful for * nested effects that you want to manually control. This rune also allows for creation of effects outside of the component @@ -3071,4 +3069,4 @@ declare namespace $inspect { */ declare function $host(): El; -//# sourceMappingURL=index.d.ts.map +//# sourceMappingURL=index.d.ts.map \ No newline at end of file From 0767c325bf5defc639f21f6007fcb930936d57e2 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 18 Dec 2024 20:06:49 +0000 Subject: [PATCH 3/3] tune --- packages/svelte/src/internal/client/reactivity/effects.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index adbaa1dbe481..7d393ae1b587 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -178,7 +178,12 @@ export function effect_tracking() { * @returns {boolean} */ export function effect_active() { - return active_effect !== null; + if (is_destroying_effect) { + return false; + } + return ( + active_effect !== null || (active_reaction !== null && (active_reaction.f & UNOWNED) === 0) + ); } /**