docs: make legacy.componentApi more visible (#11924)

* 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

* clarify

* flesh out message, put error behind a function

* make it work with HMR

* fix

* fix

* this makes it work. i don't fully understand why

* changeset

* changeset

* Delete .changeset/twelve-foxes-press.md

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/11944/head
Simon H 4 weeks ago committed by GitHub
parent 48e620515d
commit ba017db89d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: make `legacy.componentApi` option more visible

@ -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%

@ -452,7 +452,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',
@ -463,15 +463,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. ` +
'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) {

@ -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} */
@ -31,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);
});

@ -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;

@ -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

@ -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';

@ -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

Loading…
Cancel
Save