From 8be7dd558b519530dc3e4dba83372168e18f98cf Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 30 Jul 2024 23:54:58 +0200 Subject: [PATCH] feat: make `` unnecessary in runes mode (#12646) * feat: make `` unnecessary in runes mode In Svelte 4, writing `` meant that the component instance is static. If you made the variable `Component` a reactive state variable and updated the component value, the component would not be reinstantiated with the new value - you had to use `` for that. One reason was that having a dynamic component was more overhead, which is no longer the case in Svelte 5. We can therefore reduce the potential API surface area (by maybe deprecating `` in the future) by allowing Svelte to recognize when a component variable is potentially dynamic. It turned out that this was already mostly the case. This PR fixes one case where it wasn't, and fixes another where this was wrongfully applied in legacy mode. * we already have this function * add interactive demos * changeset --------- Co-authored-by: Rich Harris --- .changeset/warm-planets-cry.md | 5 +++ .../src/compiler/phases/2-analyze/index.js | 5 ++- .../3-transform/client/visitors/template.js | 11 +++++- .../dynamic-component/Component1.svelte | 1 + .../dynamic-component/Component2.svelte | 1 + .../samples/dynamic-component/_config.js | 14 +++++++ .../samples/dynamic-component/main.svelte | 16 ++++++++ .../samples/component-dynamic/_config.js | 3 -- .../samples/component-dynamic/input.svelte | 30 --------------- .../samples/component-dynamic/warnings.json | 38 ------------------- .../03-appendix/02-breaking-changes.md | 24 ++++++++++++ 11 files changed, 75 insertions(+), 73 deletions(-) create mode 100644 .changeset/warm-planets-cry.md create mode 100644 packages/svelte/tests/runtime-runes/samples/dynamic-component/Component1.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/dynamic-component/Component2.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/dynamic-component/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/dynamic-component/main.svelte delete mode 100644 packages/svelte/tests/validator/samples/component-dynamic/_config.js delete mode 100644 packages/svelte/tests/validator/samples/component-dynamic/input.svelte delete mode 100644 packages/svelte/tests/validator/samples/component-dynamic/warnings.json diff --git a/.changeset/warm-planets-cry.md b/.changeset/warm-planets-cry.md new file mode 100644 index 0000000000..ee7e92d95f --- /dev/null +++ b/.changeset/warm-planets-cry.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +feat: make `` unnecessary in runes mode diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 4e5a03089c..d797703e92 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1551,7 +1551,10 @@ const common_visitors = { node.name.includes('.') ? node.name.slice(0, node.name.indexOf('.')) : node.name ); - node.metadata.dynamic = binding !== null && binding.kind !== 'normal'; + node.metadata.dynamic = + context.state.analysis.runes && // Svelte 4 required you to use svelte:component to switch components + binding !== null && + (binding.kind !== 'normal' || node.name.includes('.')); }, RenderTag(node, context) { context.next({ ...context.state, render_tag: node }); 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 dcfa23a99d..fb995b4e90 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 @@ -933,7 +933,16 @@ function serialize_inline_component(node, component_name, context, anchor = cont /** @param {Expression} node_id */ let fn = (node_id) => { - return b.call(component_name, node_id, props_expression); + return b.call( + // TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components + // will be handled separately through the `$.component` function, and then the component name will + // always be referenced through just the identifier here. + node.type === 'SvelteComponent' + ? component_name + : /** @type {Expression} */ (context.visit(b.member_id(component_name))), + node_id, + props_expression + ); }; if (bind_this !== null) { diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component/Component1.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-component/Component1.svelte new file mode 100644 index 0000000000..32751f7c94 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component/Component1.svelte @@ -0,0 +1 @@ +Component1 diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component/Component2.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-component/Component2.svelte new file mode 100644 index 0000000000..aab0454088 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component/Component2.svelte @@ -0,0 +1 @@ +Component2 diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-component/_config.js new file mode 100644 index 0000000000..f6471f510b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component/_config.js @@ -0,0 +1,14 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + html: ' Component1 Component1', + async test({ assert, target }) { + const btn = target.querySelector('button'); + + btn?.click(); + flushSync(); + + assert.htmlEqual(target.innerHTML, ' Component2 Component2'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-component/main.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-component/main.svelte new file mode 100644 index 0000000000..2b2d7c4b72 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-component/main.svelte @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/svelte/tests/validator/samples/component-dynamic/_config.js b/packages/svelte/tests/validator/samples/component-dynamic/_config.js deleted file mode 100644 index 64fdc120d6..0000000000 --- a/packages/svelte/tests/validator/samples/component-dynamic/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -import { test } from '../../test'; - -export default test({ skip: true }); diff --git a/packages/svelte/tests/validator/samples/component-dynamic/input.svelte b/packages/svelte/tests/validator/samples/component-dynamic/input.svelte deleted file mode 100644 index 7f52353e36..0000000000 --- a/packages/svelte/tests/validator/samples/component-dynamic/input.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - -
- - -
- - -
-
diff --git a/packages/svelte/tests/validator/samples/component-dynamic/warnings.json b/packages/svelte/tests/validator/samples/component-dynamic/warnings.json deleted file mode 100644 index 49fb9e4334..0000000000 --- a/packages/svelte/tests/validator/samples/component-dynamic/warnings.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "code": "reactive_component", - "message": " will not be reactive if Let changes. Use if you want this reactivity.", - "end": { - "column": 7, - "line": 15 - }, - "start": { - "column": 0, - "line": 15 - } - }, - { - "message": " will not be reactive if ExportLet changes. Use if you want this reactivity.", - "code": "reactive_component", - "end": { - "column": 13, - "line": 16 - }, - "start": { - "column": 0, - "line": 16 - } - }, - { - "message": " will not be reactive if Reactive changes. Use if you want this reactivity.", - "code": "reactive_component", - "end": { - "column": 12, - "line": 17 - }, - "start": { - "column": 0, - "line": 17 - } - } -] diff --git a/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md b/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md index 029b2ba688..95469ccbaf 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md @@ -198,6 +198,30 @@ In Svelte 4, doing the following triggered reactivity: This is because the Svelte compiler treated the assignment to `foo.value` as an instruction to update anything that referenced `foo`. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define `value` as a reactive `$state` field on the `Foo` class. Wrapping `new Foo()` with `$state(...)` will have no effect — only vanilla objects and arrays are made deeply reactive. +### `` is no longer necessary + +In Svelte 4, components are _static_ — if you render ``, and the value of `Thing` changes, [nothing happens](https://svelte.dev/repl/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you must use ``. + +This is [no longer true in Svelte 5](/#H4sIAAAAAAAAE4WQwU7DMAyGX8VESANpXe8lq9Q8AzfGobQujZQmWeJOQlXenaQB1sM0bnG-379_e2GDVOhZ9bYw3U7IKtZYy_aMvmwq_AUVYay9mV2XfrjvnLRUn_SJ5GSNI2hgcGaC3aFsDrlh97LB4g-LLY4ChQSvo9SfcIRHTy3h03NEvLzO0Nyjwo7gQ-q-urRqxuOy9oQ1AjeWpNHwQ5pQN7zMf7e4CLXY8Dhpdc-THooCaESP0DoEPM8ydqEmKIqkzUnL9MxrVJ2JG-qkoFH631xREg82mV4OEntWkZsx7K_3vXtdm_LbuwbiHwNx2-A9fANfmchv7QEAAA==): + +```svelte + + + + + + + +``` + ## Other breaking changes ### Stricter `@const` assignment validation