Skip to content

Commit a3f74b7

Browse files
committed
tighten up validation etc
1 parent b99c8f4 commit a3f74b7

File tree

4 files changed

+28
-18
lines changed

4 files changed

+28
-18
lines changed

packages/svelte/src/compiler/errors.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,9 @@ const runes = {
171171
`$props() assignment must not contain nested properties or computed keys`,
172172
'invalid-props-location': () =>
173173
`$props() can only be used at the top level of components as a variable declaration initializer`,
174-
'invalid-derived-location': () =>
175-
`$derived() can only be used as a variable declaration initializer or a class field`,
176-
'invalid-state-location': () =>
177-
`$state() can only be used as a variable declaration initializer or a class field`,
174+
/** @param {string} rune */
175+
'invalid-state-location': (rune) =>
176+
`${rune}(...) can only be used as a variable declaration initializer or a class field`,
178177
'invalid-effect-location': () => `$effect() can only be used as an expression statement`,
179178
/**
180179
* @param {boolean} is_binding

packages/svelte/src/compiler/phases/2-analyze/validation.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ function validate_call_expression(node, scope, path) {
718718
if (rune === '$state' || rune === '$derived' || rune === '$derived.call') {
719719
if (parent.type === 'VariableDeclarator') return;
720720
if (parent.type === 'PropertyDefinition' && !parent.static && !parent.computed) return;
721-
error(node, rune === '$derived' ? 'invalid-derived-location' : 'invalid-state-location');
721+
error(node, 'invalid-state-location', rune);
722722
}
723723

724724
if (rune === '$effect' || rune === '$effect.pre') {
@@ -786,10 +786,10 @@ export const validation_runes_js = {
786786

787787
const args = /** @type {import('estree').CallExpression} */ (init).arguments;
788788

789-
if (rune === '$derived' && args.length !== 1) {
790-
error(node, 'invalid-rune-args-length', '$derived', [1]);
789+
if ((rune === '$derived' || rune === '$derived.call') && args.length !== 1) {
790+
error(node, 'invalid-rune-args-length', rune, [1]);
791791
} else if (rune === '$state' && args.length > 1) {
792-
error(node, 'invalid-rune-args-length', '$state', [0, 1]);
792+
error(node, 'invalid-rune-args-length', rune, [0, 1]);
793793
} else if (rune === '$props') {
794794
error(node, 'invalid-props-location');
795795
}
@@ -811,7 +811,7 @@ export const validation_runes_js = {
811811
definition.value?.type === 'CallExpression'
812812
) {
813813
const rune = get_rune(definition.value, context.state.scope);
814-
if (rune === '$derived') {
814+
if (rune === '$derived' || rune === '$derived.call') {
815815
private_derived_state.push(definition.key.name);
816816
}
817817
}
@@ -938,25 +938,33 @@ export const validation_runes = merge(validation, a11y_validators, {
938938
context.type === 'Identifier' &&
939939
(context.name === '$state' || context.name === '$derived')
940940
) {
941-
error(
942-
node,
943-
context.name === '$derived' ? 'invalid-derived-location' : 'invalid-state-location'
944-
);
941+
error(node, 'invalid-state-location', context.name);
945942
}
946943
next({ ...state });
947944
},
948-
VariableDeclarator(node, { state }) {
945+
VariableDeclarator(node, { state, path }) {
949946
const init = unwrap_ts_expression(node.init);
950947
const rune = get_rune(init, state.scope);
951948

952949
if (rune === null) return;
953950

954951
const args = /** @type {import('estree').CallExpression} */ (init).arguments;
955952

956-
if (rune === '$derived' && args.length !== 1) {
957-
error(node, 'invalid-rune-args-length', '$derived', [1]);
953+
if (rune === '$derived') {
954+
const arg = args[0];
955+
if (
956+
arg.type === 'CallExpression' &&
957+
(arg.callee.type === 'ArrowFunctionExpression' || arg.callee.type === 'FunctionExpression')
958+
) {
959+
warn(state.analysis.warnings, node, path, 'derived-iife');
960+
}
961+
}
962+
963+
// TODO some of this is duplicated with above, seems off
964+
if ((rune === '$derived' || rune === '$derived.call') && args.length !== 1) {
965+
error(node, 'invalid-rune-args-length', rune, [1]);
958966
} else if (rune === '$state' && args.length > 1) {
959-
error(node, 'invalid-rune-args-length', '$state', [0, 1]);
967+
error(node, 'invalid-rune-args-length', rune, [0, 1]);
960968
} else if (rune === '$props') {
961969
if (state.has_props_rune) {
962970
error(node, 'duplicate-props-rune');

packages/svelte/src/compiler/utils/builders.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ export function thunk(expression) {
395395
expression.type === 'CallExpression' &&
396396
expression.callee.type !== 'Super' &&
397397
expression.callee.type !== 'MemberExpression' &&
398+
expression.callee.type !== 'CallExpression' &&
398399
expression.arguments.length === 0
399400
) {
400401
return expression.callee;

packages/svelte/src/compiler/warnings.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ const runes = {
2323
`Referencing a local variable with a $ prefix will create a store subscription. Please rename ${name} to avoid the ambiguity.`,
2424
/** @param {string} name */
2525
'non-state-reference': (name) =>
26-
`${name} is updated, but is not declared with $state(...). Changing its value will not correctly trigger updates.`
26+
`${name} is updated, but is not declared with $state(...). Changing its value will not correctly trigger updates.`,
27+
'derived-iife': () =>
28+
`Use \`$derived.call(() => {...})\` instead of \`$derived((() => {...})());\``
2729
};
2830

2931
/** @satisfies {Warnings} */

0 commit comments

Comments
 (0)