Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/red-feet-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

feat: allow state/derived/props to be explicitly exported from components
8 changes: 2 additions & 6 deletions packages/svelte/src/compiler/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,8 @@ const runes = {
'invalid-legacy-export': () => `Cannot use \`export let\` in runes mode — use $props instead`,
/** @param {string} rune */
'invalid-rune-usage': (rune) => `Cannot use ${rune} rune in non-runes mode`,
'invalid-state-export': () =>
`Cannot export state if it is reassigned. Either export a function returning the state value or only mutate the state value's properties`,
'invalid-derived-export': () =>
`Cannot export derived state. To expose the current derived value, export a function returning its value`,
'invalid-prop-export': () =>
`Cannot export properties. To expose the current value of a property, export a function returning its value`,
'invalid-state-export': () => `Cannot export state from a module if it is reassigned`,
'invalid-derived-export': () => `Cannot export derived state from a module`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good now, though I slightly prefered the changed error messages because they gave you instructions on how to resolve the error. Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merged the changes from both

'invalid-props-id': () => `$props() can only be used with an object destructuring pattern`,
'invalid-props-pattern': () =>
`$props() assignment must not contain nested properties or computed keys`,
Expand Down
21 changes: 2 additions & 19 deletions packages/svelte/src/compiler/phases/2-analyze/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -733,10 +733,6 @@ function validate_export(node, scope, name) {
const binding = scope.get(name);
if (!binding) return;

if (binding.kind === 'prop') {
error(node, 'invalid-prop-export');
}

if (binding.kind === 'derived') {
error(node, 'invalid-derived-export');
}
Expand Down Expand Up @@ -964,25 +960,12 @@ export const validation_runes = merge(validation, a11y_validators, {
if (node.label.name !== '$' || path.at(-1)?.type !== 'Program') return;
error(node, 'invalid-legacy-reactive-statement');
},
ExportNamedDeclaration(node, { state, next }) {
ExportNamedDeclaration(node, { state }) {
if (node.declaration?.type !== 'VariableDeclaration') return;

// visit children, so bindings are correctly initialised
next();

for (const declarator of node.declaration.declarations) {
for (const id of extract_identifiers(declarator.id)) {
validate_export(node, state.scope, id.name);
}
}

if (state.analysis.instance.scope !== state.scope) return;
if (node.declaration.kind !== 'let') return;
if (state.analysis.instance.scope !== state.scope) return;
error(node, 'invalid-legacy-export');
},
ExportSpecifier(node, { state }) {
validate_export(node, state.scope, node.local.name);
},
CallExpression(node, { state, path }) {
validate_call_expression(node, state.scope, path);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export function get_assignment_value(node, { state, visit }) {
*/
export function is_state_source(binding, state) {
return (
(binding.kind === 'state' || binding.kind === 'frozen_state') &&
(binding.kind === 'state' ||
binding.kind === 'frozen_state' ||
binding.kind === 'derived' ||
binding.kind === 'prop') &&
(!state.analysis.immutable || binding.reassigned || state.analysis.accessors)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { test } from '../../test';
export default test({
error: {
code: 'invalid-derived-export',
message:
'Cannot export derived state. To expose the current derived value, export a function returning its value'
message: 'Cannot export derived state from a module',
position: [24, 66]
}
});

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { test } from '../../test';
export default test({
error: {
code: 'invalid-state-export',
message:
"Cannot export state if it is reassigned. Either export a function returning the state value or only mutate the state value's properties",
message: 'Cannot export state from a module if it is reassigned',
position: [46, 86]
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { test } from '../../test';
export default test({
error: {
code: 'invalid-state-export',
message:
"Cannot export state if it is reassigned. Either export a function returning the state value or only mutate the state value's properties",
message: 'Cannot export state from a module if it is reassigned',
position: [28, 53]
}
});

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export default test({

btn?.click();
await Promise.resolve();
assert.htmlEqual(target.innerHTML, '0 1 <button>0 / 1</button>');
assert.htmlEqual(target.innerHTML, '1 2 <button>1 / 2</button>');
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
</script>

<Sub bind:this={sub} />
<button on:click={() => sub.increment()}>{sub?.count1.value} / {sub?.count2.value}</button>
<button on:click={() => sub.increment()}>{sub?.count} / {sub?.doubled}</button>
13 changes: 13 additions & 0 deletions packages/svelte/tests/runtime-runes/samples/exports-3/sub.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
let count = $state(0);
let doubled = $derived(count * 2);

export { count, doubled };

export function increment() {
count += 1;
}
</script>

{count}
{doubled}
15 changes: 0 additions & 15 deletions packages/svelte/tests/runtime-runes/samples/exports3/sub.svelte

This file was deleted.