diff --git a/.changeset/dirty-donuts-yell.md b/.changeset/dirty-donuts-yell.md new file mode 100644 index 0000000000..a444313905 --- /dev/null +++ b/.changeset/dirty-donuts-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: replace `$derived.call` with `$derived.by` diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 9ecd141416..891b0956d4 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -208,7 +208,8 @@ const runes = { 'invalid-runes-mode-import': (name) => `${name} cannot be used in runes mode`, 'duplicate-props-rune': () => `Cannot use $props() more than once`, 'invalid-each-assignment': () => - `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. 'array[i] = value' instead of 'entry = value')` + `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. 'array[i] = value' instead of 'entry = value')`, + 'invalid-derived-call': () => `$derived.call(...) has been replaced with $derived.by(...)` }; /** @satisfies {Errors} */ diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index a7d4a9180a..ec0bcd30ae 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -681,7 +681,7 @@ const runes_scope_js_tweaker = { rune !== '$state' && rune !== '$state.frozen' && rune !== '$derived' && - rune !== '$derived.call' + rune !== '$derived.by' ) return; @@ -717,7 +717,7 @@ const runes_scope_tweaker = { rune !== '$state' && rune !== '$state.frozen' && rune !== '$derived' && - rune !== '$derived.call' && + rune !== '$derived.by' && rune !== '$props' ) return; @@ -730,7 +730,7 @@ const runes_scope_tweaker = { ? 'state' : rune === '$state.frozen' ? 'frozen_state' - : rune === '$derived' || rune === '$derived.call' + : rune === '$derived' || rune === '$derived.by' ? 'derived' : path.is_rest ? 'rest_prop' diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 8af0a29402..1a854ce478 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -746,7 +746,7 @@ function validate_call_expression(node, scope, path) { error(node, 'invalid-props-location'); } - if (rune === '$state' || rune === '$derived' || rune === '$derived.call') { + if (rune === '$state' || rune === '$derived' || rune === '$derived.by') { if (parent.type === 'VariableDeclarator') return; if (parent.type === 'PropertyDefinition' && !parent.static && !parent.computed) return; error(node, 'invalid-state-location', rune); @@ -817,7 +817,7 @@ export const validation_runes_js = { const args = /** @type {import('estree').CallExpression} */ (init).arguments; - if ((rune === '$derived' || rune === '$derived.call') && args.length !== 1) { + if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { error(node, 'invalid-rune-args-length', rune, [1]); } else if (rune === '$state' && args.length > 1) { error(node, 'invalid-rune-args-length', rune, [0, 1]); @@ -842,7 +842,7 @@ export const validation_runes_js = { definition.value?.type === 'CallExpression' ) { const rune = get_rune(definition.value, context.state.scope); - if (rune === '$derived' || rune === '$derived.call') { + if (rune === '$derived' || rune === '$derived.by') { private_derived_state.push(definition.key.name); } } @@ -988,7 +988,7 @@ export const validation_runes = merge(validation, a11y_validators, { const args = /** @type {import('estree').CallExpression} */ (init).arguments; // TODO some of this is duplicated with above, seems off - if ((rune === '$derived' || rune === '$derived.call') && args.length !== 1) { + if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { error(node, 'invalid-rune-args-length', rune, [1]); } else if (rune === '$state' && args.length > 1) { error(node, 'invalid-rune-args-length', rune, [0, 1]); 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 e69cb42ae3..12cd8d2052 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 @@ -33,7 +33,7 @@ export const javascript_visitors_runes = { rune === '$state' || rune === '$state.frozen' || rune === '$derived' || - rune === '$derived.call' + rune === '$derived.by' ) { /** @type {import('../types.js').StateField} */ const field = { @@ -42,7 +42,7 @@ export const javascript_visitors_runes = { ? 'state' : rune === '$state.frozen' ? 'frozen_state' - : rune === '$derived.call' + : rune === '$derived.by' ? 'derived_call' : 'derived', // @ts-expect-error this is set in the next pass @@ -289,12 +289,12 @@ export const javascript_visitors_runes = { continue; } - if (rune === '$derived' || rune === '$derived.call') { + if (rune === '$derived' || rune === '$derived.by') { if (declarator.id.type === 'Identifier') { declarations.push( b.declarator( declarator.id, - b.call('$.derived', rune === '$derived.call' ? value : b.thunk(value)) + b.call('$.derived', rune === '$derived.by' ? value : b.thunk(value)) ) ); } else { @@ -307,7 +307,7 @@ export const javascript_visitors_runes = { '$.derived', b.thunk( b.block([ - b.let(declarator.id, rune === '$derived.call' ? b.call(value) : value), + b.let(declarator.id, rune === '$derived.by' ? b.call(value) : value), b.return(b.array(bindings.map((binding) => binding.node))) ]) ) 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 8112a30c1c..2cda4ae8f6 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 @@ -548,7 +548,7 @@ const javascript_visitors_runes = { : /** @type {import('estree').Expression} */ (visit(node.value.arguments[0])) }; } - if (rune === '$derived.call') { + if (rune === '$derived.by') { return { ...node, value: @@ -582,7 +582,7 @@ const javascript_visitors_runes = { ? b.id('undefined') : /** @type {import('estree').Expression} */ (visit(args[0])); - if (rune === '$derived.call') { + if (rune === '$derived.by') { declarations.push( b.declarator( /** @type {import('estree').Pattern} */ (visit(declarator.id)), diff --git a/packages/svelte/src/compiler/phases/constants.js b/packages/svelte/src/compiler/phases/constants.js index 3358070369..eaf01b7f34 100644 --- a/packages/svelte/src/compiler/phases/constants.js +++ b/packages/svelte/src/compiler/phases/constants.js @@ -33,7 +33,7 @@ export const Runes = /** @type {const} */ ([ '$state.frozen', '$props', '$derived', - '$derived.call', + '$derived.by', '$effect', '$effect.pre', '$effect.active', diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index c4fb1ba493..5d46c31871 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -717,6 +717,8 @@ export function get_rune(node, scope) { if (n.type !== 'Identifier') return null; joined = n.name + joined; + + if (joined === '$derived.call') error(node, 'invalid-derived-call'); if (!Runes.includes(/** @type {any} */ (joined))) return null; const binding = scope.get(n.name); diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index 5c810664ba..9a6124f023 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -24,8 +24,7 @@ const runes = { /** @param {string} name */ 'non-state-reference': (name) => `${name} is updated, but is not declared with $state(...). Changing its value will not correctly trigger updates.`, - 'derived-iife': () => - `Use \`$derived.call(() => {...})\` instead of \`$derived((() => {...})());\`` + 'derived-iife': () => `Use \`$derived.by(() => {...})\` instead of \`$derived((() => {...})());\`` }; /** @satisfies {Warnings} */ diff --git a/packages/svelte/src/main/ambient.d.ts b/packages/svelte/src/main/ambient.d.ts index bbc91c2f5b..9d7f1d84c1 100644 --- a/packages/svelte/src/main/ambient.d.ts +++ b/packages/svelte/src/main/ambient.d.ts @@ -62,11 +62,11 @@ declare function $derived(expression: T): T; declare namespace $derived { /** * Sometimes you need to create complex derivations that don't fit inside a short expression. - * In these cases, you can use `$derived.call` which accepts a function as its argument. + * In these cases, you can use `$derived.by` which accepts a function as its argument. * * Example: * ```ts - * let total = $derived.call(() => { + * let total = $derived.by(() => { * let result = 0; * for (const n of numbers) { * result += n; @@ -75,9 +75,9 @@ declare namespace $derived { * }); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$derived-call + * https://svelte-5-preview.vercel.app/docs/runes#$derived-by */ - export function call(fn: () => T): T; + export function by(fn: () => T): T; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-derived-fn/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-derived-fn/main.svelte index 2816780c25..5fa0e97b1b 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-state-derived-fn/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/class-state-derived-fn/main.svelte @@ -1,7 +1,7 @@ diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c6d0ad3d5a..314ec9dd7f 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2501,11 +2501,11 @@ declare function $derived(expression: T): T; declare namespace $derived { /** * Sometimes you need to create complex derivations that don't fit inside a short expression. - * In these cases, you can use `$derived.call` which accepts a function as its argument. + * In these cases, you can use `$derived.by` which accepts a function as its argument. * * Example: * ```ts - * let total = $derived.call(() => { + * let total = $derived.by(() => { * let result = 0; * for (const n of numbers) { * result += n; @@ -2514,9 +2514,9 @@ declare namespace $derived { * }); * ``` * - * https://svelte-5-preview.vercel.app/docs/runes#$derived-call + * https://svelte-5-preview.vercel.app/docs/runes#$derived-by */ - export function call(fn: () => T): T; + export function by(fn: () => T): T; } /** diff --git a/sites/svelte-5-preview/src/lib/CodeMirror.svelte b/sites/svelte-5-preview/src/lib/CodeMirror.svelte index 9a2438be02..3f29dfe176 100644 --- a/sites/svelte-5-preview/src/lib/CodeMirror.svelte +++ b/sites/svelte-5-preview/src/lib/CodeMirror.svelte @@ -208,8 +208,8 @@ { label: '$state', type: 'keyword', boost: 10 }, { label: '$props', type: 'keyword', boost: 9 }, { label: '$derived', type: 'keyword', boost: 8 }, - snip('$derived.call(() => {\n\t${}\n});', { - label: '$derived.call', + snip('$derived.by(() => {\n\t${}\n});', { + label: '$derived.by', type: 'keyword', boost: 7 }), diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index 7cccfeb23f..379336a993 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -134,14 +134,14 @@ If the value of a reactive variable is being computed it should be replaced with ``` ...`double` will be calculated first despite the source order. In runes mode, `triple` cannot reference `double` before it has been declared. -## `$derived.call` +## `$derived.by` -Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use `$derived.call` which accepts a function as its argument. +Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use `$derived.by` which accepts a function as its argument. ```svelte