feat: warn on obvious legacy component instantiation (#12648)

Adds a compiler warning that warns about legacy component instantiation (i.e. using `new Component(..)`). This won't catch all cases, but the most obvious ones which probably make up ~80%
animation-params
Simon H 3 months ago committed by GitHub
parent 85f2f161ed
commit 363535c849
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -6,6 +6,10 @@
> Component has unused export property '%name%'. If it is for external reference only, please consider using `export const %name%`
## legacy_component_creation
> Svelte 5 components are no longer classes. Instantiate them using `mount` or `hydrate` (imported from 'svelte') instead.
## non_reactive_update
> `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates

@ -1,4 +1,4 @@
/** @import { AssignmentExpression, CallExpression, Expression, Identifier, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { AssignmentExpression, CallExpression, Expression, ImportDeclaration, Identifier, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { Attribute, Component, ElementLike, Fragment, RegularElement, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TransitionDirective } from '#compiler' */
/** @import { NodeLike } from '../../errors.js' */
/** @import { AnalysisState, Context, Visitors } from './types.js' */
@ -362,6 +362,32 @@ function validate_block_not_empty(node, context) {
* @type {Visitors}
*/
const validation = {
ExpressionStatement(node, { state }) {
if (
node.expression.type === 'NewExpression' &&
node.expression.callee.type === 'Identifier' &&
node.expression.arguments.length === 1 &&
node.expression.arguments[0].type === 'ObjectExpression' &&
node.expression.arguments[0].properties.some(
(p) => p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'target'
)
) {
const binding = state.scope.get(node.expression.callee.name);
if (binding?.kind === 'normal' && binding.declaration_kind === 'import') {
const declaration = /** @type {ImportDeclaration} */ (binding.initial);
// Theoretically someone could import a class from a `.svelte.js` module, but that's too rare to worry about
if (
/** @type {string} */ (declaration.source.value)?.endsWith('.svelte') &&
declaration.specifiers.find(
(s) => s.local.name === binding.node.name && s.type === 'ImportDefaultSpecifier'
)
) {
w.legacy_component_creation(node.expression);
}
}
}
},
MemberExpression(node, context) {
if (node.object.type === 'Identifier' && node.property.type === 'Identifier') {
const binding = context.state.scope.get(node.object.name);

@ -96,6 +96,7 @@ export const codes = [
"options_renamed_ssr_dom",
"derived_iife",
"export_let_unused",
"legacy_component_creation",
"non_reactive_update",
"perf_avoid_inline_class",
"perf_avoid_nested_class",
@ -586,6 +587,14 @@ export function export_let_unused(node, name) {
w(node, "export_let_unused", `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\``);
}
/**
* Svelte 5 components are no longer classes. Instantiate them using `mount` or `hydrate` (imported from 'svelte') instead.
* @param {null | NodeLike} node
*/
export function legacy_component_creation(node) {
w(node, "legacy_component_creation", "Svelte 5 components are no longer classes. Instantiate them using `mount` or `hydrate` (imported from 'svelte') instead.");
}
/**
* `%name%` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates
* @param {null | NodeLike} node

@ -0,0 +1,13 @@
<script>
import Foo, { Bar } from './Somewhere.svelte';
import Baz from './somewhereelse';
let Buzz;
new Foo({ target: null });
new Foo({}); // also a false negative to be really sure we don't get false positives
new Foo();
new Bar();
new Baz();
new Buzz();
</script>

@ -0,0 +1,14 @@
[
{
"code": "legacy_component_creation",
"message": "Svelte 5 components are no longer classes. Instantiate them using `mount` or `hydrate` (imported from 'svelte') instead.",
"start": {
"column": 1,
"line": 7
},
"end": {
"column": 26,
"line": 7
}
}
]
Loading…
Cancel
Save