Skip to content

Commit ba017db

Browse files
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 <[email protected]>
1 parent 48e6205 commit ba017db

File tree

8 files changed

+53
-14
lines changed

8 files changed

+53
-14
lines changed

.changeset/flat-ghosts-fly.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: make `legacy.componentApi` option more visible

packages/svelte/messages/client-errors/errors.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
> %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
1616
17+
## component_api_invalid_new
18+
19+
> 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
20+
1721
## each_key_duplicate
1822

1923
> Keyed each block has duplicate key at indexes %a% and %b%

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ export function client_component(source, analysis, options) {
452452
body.unshift(b.imports([['createClassComponent', '$$_createClassComponent']], 'svelte/legacy'));
453453
component_block.body.unshift(
454454
b.if(
455-
b.binary('===', b.id('new.target'), b.id(analysis.name)),
455+
b.id('new.target'),
456456
b.return(
457457
b.call(
458458
'$$_createClassComponent',
@@ -463,15 +463,7 @@ export function client_component(source, analysis, options) {
463463
)
464464
);
465465
} else if (options.dev) {
466-
component_block.body.unshift(
467-
b.if(
468-
b.binary('===', b.id('new.target'), b.id(analysis.name)),
469-
b.throw_error(
470-
`Instantiating a component with \`new\` is no longer valid in Svelte 5. ` +
471-
'See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more information'
472-
)
473-
)
474-
);
466+
component_block.body.unshift(b.stmt(b.call('$.check_target', b.id('new.target'))));
475467
}
476468

477469
if (state.events.size > 0) {

packages/svelte/src/internal/client/dev/hmr.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { block, branch, destroy_effect } from '../reactivity/effects.js';
22
import { set_should_intro } from '../render.js';
33
import { get } from '../runtime.js';
4+
import { check_target } from './legacy.js';
45

56
/**
67
* @template {(anchor: Comment, props: any) => any} Component
@@ -11,7 +12,7 @@ export function hmr(source) {
1112
* @param {Comment} anchor
1213
* @param {any} props
1314
*/
14-
return (anchor, props) => {
15+
return function (anchor, props) {
1516
let instance = {};
1617

1718
/** @type {import("#client").Effect} */
@@ -31,7 +32,10 @@ export function hmr(source) {
3132
// preserve getters/setters
3233
Object.defineProperties(
3334
instance,
34-
Object.getOwnPropertyDescriptors(component(anchor, props))
35+
Object.getOwnPropertyDescriptors(
36+
// @ts-expect-error
37+
new.target ? new component(anchor, props) : component(anchor, props)
38+
)
3539
);
3640
set_should_intro(true);
3741
});

packages/svelte/src/internal/client/dev/legacy.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import * as e from '../errors.js';
22
import { current_component_context } from '../runtime.js';
33
import { get_component } from './ownership.js';
44

5+
/** @param {Function & { filename: string }} target */
6+
export function check_target(target) {
7+
if (target) {
8+
e.component_api_invalid_new(target.filename ?? 'a component', target.name);
9+
}
10+
}
11+
512
export function legacy_api() {
613
const component = current_component_context?.function;
714

packages/svelte/src/internal/client/errors.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,24 @@ export function component_api_changed(parent, method, component) {
7575
}
7676
}
7777

78+
/**
79+
* 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
80+
* @param {string} component
81+
* @param {string} name
82+
* @returns {never}
83+
*/
84+
export function component_api_invalid_new(component, name) {
85+
if (DEV) {
86+
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`}`);
87+
88+
error.name = 'Svelte error';
89+
throw error;
90+
} else {
91+
// TODO print a link to the documentation
92+
throw new Error("component_api_invalid_new");
93+
}
94+
}
95+
7896
/**
7997
* Keyed each block has duplicate key `%value%` at indexes %a% and %b%
8098
* @param {string} a

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export {
77
mark_module_end,
88
add_owner_effect
99
} from './dev/ownership.js';
10-
export { legacy_api } from './dev/legacy.js';
10+
export { check_target, legacy_api } from './dev/legacy.js';
1111
export { inspect } from './dev/inspect.js';
1212
export { await_block as await } from './dom/blocks/await.js';
1313
export { if_block as if } from './dom/blocks/if.js';

sites/svelte-5-preview/src/routes/docs/content/03-appendix/02-breaking-changes.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,16 @@ import App from './App.svelte'
7070
export default app;
7171
```
7272

73-
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`.
73+
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`.
74+
75+
```js
76+
/// svelte.config.js
77+
export default {
78+
compilerOptions: {
79+
legacy: { componentApi: true }
80+
}
81+
};
82+
```
7483

7584
### Server API changes
7685

0 commit comments

Comments
 (0)