From 81ee9ad7f6f7daa3d62c9883cd77f17c1c3c2a7b Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 6 Jun 2024 10:16:20 +0200 Subject: [PATCH 01/10] docs: make legacy.componentApi more visible People didn't know that this exists, so we should make it more visible through having it be part of the error message, and calling it out in the docs with more details --- .../phases/3-transform/client/transform-client.js | 2 +- .../docs/content/03-appendix/02-breaking-changes.md | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 11cabf0ee58e..68bb5367bf0e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -508,7 +508,7 @@ export function client_component(source, analysis, options) { b.if( b.binary('===', b.id('new.target'), b.id(analysis.name)), b.throw_error( - `Instantiating a component with \`new\` is no longer valid in Svelte 5. ` + + `Instantiating a component with \`new\` is no longer valid in Svelte 5. If this component is not under your control, set the legacy.componentApi compiler option. ` + 'See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information' ) ) 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 81b28ce81716..5b08103d8050 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 @@ -70,7 +70,16 @@ import App from './App.svelte' export default app; ``` -If this component is not under your control, you can use the `legacy.componentApi` compiler option for auto-applied backwards compatibility (note that this adds a bit of overhead to each component). This will also add `$set` and `$on` methods for all component instances you get through `bind:this`. +If this component is not under your control, you can use the `legacy.componentApi` compiler option for auto-applied backwards compatibility, which means code using `new Component(...)` keeps working without adjustments (note that this adds a bit of overhead to each component). This will also add `$set` and `$on` methods for all component instances you get through `bind:this`. + +```js +/// svelte.config.js +export default { + compilerOptions: { + legacy: { componentApi: true } + } +}; +``` ### Server API changes From 131cafbc3c29687a1a9aa171a4d4c013cfd61c08 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 6 Jun 2024 10:19:08 +0200 Subject: [PATCH 02/10] clarify --- .../src/compiler/phases/3-transform/client/transform-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 68bb5367bf0e..08cdcfe8cb35 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -508,7 +508,7 @@ export function client_component(source, analysis, options) { b.if( b.binary('===', b.id('new.target'), b.id(analysis.name)), b.throw_error( - `Instantiating a component with \`new\` is no longer valid in Svelte 5. If this component is not under your control, set the legacy.componentApi compiler option. ` + + `Instantiating a component with \`new\` is no longer valid in Svelte 5. If this component is not under your control, set the legacy.componentApi compiler option to keep it working. ` + 'See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information' ) ) From 7b2fc9c06014902ac7dd74cc22cc495bfad95606 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 10:25:11 -0400 Subject: [PATCH 03/10] flesh out message, put error behind a function --- .../svelte/messages/client-errors/errors.md | 4 ++++ .../3-transform/client/transform-client.js | 10 +--------- .../svelte/src/internal/client/dev/legacy.js | 7 +++++++ packages/svelte/src/internal/client/errors.js | 18 ++++++++++++++++++ packages/svelte/src/internal/client/index.js | 2 +- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index bb8773a3bd6e..843e9edfdae5 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -14,6 +14,10 @@ > %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information +## component_api_invalid_new + +> Attempted to instantiate `%component%` with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information + ## each_key_duplicate > Keyed each block has duplicate key at indexes %a% and %b% diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 08cdcfe8cb35..d8d7fc649ab3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -504,15 +504,7 @@ export function client_component(source, analysis, options) { ) ); } else if (options.dev) { - component_block.body.unshift( - b.if( - b.binary('===', b.id('new.target'), b.id(analysis.name)), - b.throw_error( - `Instantiating a component with \`new\` is no longer valid in Svelte 5. If this component is not under your control, set the legacy.componentApi compiler option to keep it working. ` + - 'See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information' - ) - ) - ); + component_block.body.unshift(b.stmt(b.call('$.check_target', b.id('new.target')))); } if (state.events.size > 0) { diff --git a/packages/svelte/src/internal/client/dev/legacy.js b/packages/svelte/src/internal/client/dev/legacy.js index 758e1a95b5c6..1262c62db02e 100644 --- a/packages/svelte/src/internal/client/dev/legacy.js +++ b/packages/svelte/src/internal/client/dev/legacy.js @@ -2,6 +2,13 @@ import * as e from '../errors.js'; import { current_component_context } from '../runtime.js'; import { get_component } from './ownership.js'; +/** @param {Function & { filename: string }} target */ +export function check_target(target) { + if (target) { + e.component_api_invalid_new(target.filename ?? 'a component', target.name); + } +} + export function legacy_api() { const component = current_component_context?.function; diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 2ead3963c569..72924a8e1e93 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -75,6 +75,24 @@ export function component_api_changed(parent, method, component) { } } +/** + * Attempted to instantiate `%component%` with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information + * @param {string} component + * @param {string} name + * @returns {never} + */ +export function component_api_invalid_new(component, name) { + if (DEV) { + const error = new Error(`${"component_api_invalid_new"}\n${`Attempted to instantiate \`${component}\` with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`legacy.componentApi\` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`}`); + + error.name = 'Svelte error'; + throw error; + } else { + // TODO print a link to the documentation + throw new Error("component_api_invalid_new"); + } +} + /** * Keyed each block has duplicate key `%value%` at indexes %a% and %b% * @param {string} a diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 65983da3e2f2..1194570c871f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -7,7 +7,7 @@ export { mark_module_end, add_owner_effect } from './dev/ownership.js'; -export { legacy_api } from './dev/legacy.js'; +export { check_target, legacy_api } from './dev/legacy.js'; export { inspect } from './dev/inspect.js'; export { await_block as await } from './dom/blocks/await.js'; export { if_block as if } from './dom/blocks/if.js'; From ab9c640cd9eb3022b550fe9e1bbaeb43e48a8dbf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 10:32:31 -0400 Subject: [PATCH 04/10] make it work with HMR --- packages/svelte/src/internal/client/dev/hmr.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dev/hmr.js b/packages/svelte/src/internal/client/dev/hmr.js index 0f79607707fe..b37a1052e836 100644 --- a/packages/svelte/src/internal/client/dev/hmr.js +++ b/packages/svelte/src/internal/client/dev/hmr.js @@ -1,6 +1,7 @@ import { block, branch, destroy_effect } from '../reactivity/effects.js'; import { set_should_intro } from '../render.js'; import { get } from '../runtime.js'; +import { check_target } from './legacy.js'; /** * @template {(anchor: Comment, props: any) => any} Component @@ -11,7 +12,7 @@ export function hmr(source) { * @param {Comment} anchor * @param {any} props */ - return (anchor, props) => { + return function (anchor, props) { let instance = {}; /** @type {import("#client").Effect} */ @@ -20,6 +21,9 @@ export function hmr(source) { block(() => { const component = get(source); + // @ts-expect-error + check_target(new.target && component); + if (effect) { // @ts-ignore for (var k in instance) delete instance[k]; From a90e5a6c89927e3acfabd6ac747005e245d952ea Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 10:34:59 -0400 Subject: [PATCH 05/10] fix --- packages/svelte/messages/client-errors/errors.md | 2 +- packages/svelte/src/internal/client/errors.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 843e9edfdae5..ec543765cfc9 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -16,7 +16,7 @@ ## component_api_invalid_new -> Attempted to instantiate `%component%` with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information +> Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information ## each_key_duplicate diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 72924a8e1e93..981f40a218cf 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -76,14 +76,14 @@ export function component_api_changed(parent, method, component) { } /** - * Attempted to instantiate `%component%` with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information + * Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `legacy.componentApi` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information * @param {string} component * @param {string} name * @returns {never} */ export function component_api_invalid_new(component, name) { if (DEV) { - const error = new Error(`${"component_api_invalid_new"}\n${`Attempted to instantiate \`${component}\` with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`legacy.componentApi\` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`}`); + const error = new Error(`${"component_api_invalid_new"}\n${`Attempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`legacy.componentApi\` compiler option to keep it working. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information`}`); error.name = 'Svelte error'; throw error; From ec70713976fab05461c225dc2163277efe09df30 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 11:04:08 -0400 Subject: [PATCH 06/10] fix --- packages/svelte/src/internal/client/dev/hmr.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/internal/client/dev/hmr.js b/packages/svelte/src/internal/client/dev/hmr.js index b37a1052e836..9ba2a6989784 100644 --- a/packages/svelte/src/internal/client/dev/hmr.js +++ b/packages/svelte/src/internal/client/dev/hmr.js @@ -21,9 +21,6 @@ export function hmr(source) { block(() => { const component = get(source); - // @ts-expect-error - check_target(new.target && component); - if (effect) { // @ts-ignore for (var k in instance) delete instance[k]; @@ -35,7 +32,10 @@ export function hmr(source) { // preserve getters/setters Object.defineProperties( instance, - Object.getOwnPropertyDescriptors(component(anchor, props)) + Object.getOwnPropertyDescriptors( + // @ts-expect-error + new.target ? new component(anchor, props) : component(anchor, props) + ) ); set_should_intro(true); }); From 7c024677de8ec37d6f57847ddebc2080dc73bcee Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 11:55:29 -0400 Subject: [PATCH 07/10] this makes it work. i don't fully understand why --- .../src/compiler/phases/3-transform/client/transform-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index d8d7fc649ab3..05a3074a3aec 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -493,7 +493,7 @@ export function client_component(source, analysis, options) { body.unshift(b.imports([['createClassComponent', '$$_createClassComponent']], 'svelte/legacy')); component_block.body.unshift( b.if( - b.binary('===', b.id('new.target'), b.id(analysis.name)), + b.id('new.target'), b.return( b.call( '$$_createClassComponent', From 1ab3c3b7fca1a220ed21547d265d14ec6238bf44 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 13:13:02 -0400 Subject: [PATCH 08/10] changeset --- .changeset/twelve-foxes-press.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twelve-foxes-press.md diff --git a/.changeset/twelve-foxes-press.md b/.changeset/twelve-foxes-press.md new file mode 100644 index 000000000000..c3df233244db --- /dev/null +++ b/.changeset/twelve-foxes-press.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: make `legacy.componentApi` option more visible From e45c89c23c618733de3341aa0d383c5948d26fa4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 13:14:18 -0400 Subject: [PATCH 09/10] changeset --- .changeset/flat-ghosts-fly.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/flat-ghosts-fly.md diff --git a/.changeset/flat-ghosts-fly.md b/.changeset/flat-ghosts-fly.md new file mode 100644 index 000000000000..bcd10f706691 --- /dev/null +++ b/.changeset/flat-ghosts-fly.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make `legacy.componentApi` option more visible From 9847e1a78108d01e67285df4242295031e902780 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 6 Jun 2024 13:32:48 -0400 Subject: [PATCH 10/10] Delete .changeset/twelve-foxes-press.md --- .changeset/twelve-foxes-press.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/twelve-foxes-press.md diff --git a/.changeset/twelve-foxes-press.md b/.changeset/twelve-foxes-press.md deleted file mode 100644 index c3df233244db..000000000000 --- a/.changeset/twelve-foxes-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: make `legacy.componentApi` option more visible