diff --git a/.changeset/new-parrots-pay.md b/.changeset/new-parrots-pay.md new file mode 100644 index 000000000000..3dbcc9580ae8 --- /dev/null +++ b/.changeset/new-parrots-pay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: migrate `Component` to `ComponentExports` in TS diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index ee5abc8853c8..3e47bf816714 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1,5 +1,5 @@ /** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement, ExpressionStatement } from 'estree' */ -/** @import { Visitors } from 'zimmerframe' */ +/** @import { Visitors, Context } from 'zimmerframe' */ /** @import { ComponentAnalysis } from '../phases/types.js' */ /** @import { Scope, ScopeRoot } from '../phases/scope.js' */ /** @import { AST, Binding, SvelteNode, ValidatedCompileOptions } from '#compiler' */ @@ -398,6 +398,8 @@ export function migrate(source, { filename, use_ts } = {}) { } } +/** @typedef {SvelteNode | { type: "TSTypeReference", typeName: Identifier, start: number, end: number }} ASTNode */ + /** * @typedef {{ * scope: Scope; @@ -416,11 +418,12 @@ export function migrate(source, { filename, use_ts } = {}) { * derived_components: Map; * derived_labeled_statements: Set; * has_svelte_self: boolean; + * migrate_prop_component_type?: boolean; * uses_ts: boolean; * }} State */ -/** @type {Visitors} */ +/** @type {Visitors} */ const instance_script = { _(node, { state, next }) { // @ts-expect-error @@ -437,8 +440,27 @@ const instance_script = { } next(); }, - Identifier(node, { state, path }) { + TSTypeReference(node, { state, path }) { + if (state.analysis.runes) return; + if (node.typeName.type === 'Identifier') { + const binding = state.scope.get(node.typeName.name); + if ( + binding && + binding.declaration_kind === 'import' && + binding.initial?.type === 'ImportDeclaration' && + binding.initial.source.value?.toString().endsWith('.svelte') + ) { + state.str.overwrite( + node.start, + node.end, + `import('svelte').ComponentExports` + ); + } + } + }, + Identifier(node, { state, path, next }) { handle_identifier(node, state, path); + next(); }, ImportDeclaration(node, { state }) { state.props_insertion_point = node.end ?? state.props_insertion_point; @@ -503,6 +525,8 @@ const instance_script = { return; } + next(); + let nr_of_props = 0; for (const declarator of node.declarations) { @@ -1409,7 +1433,7 @@ function migrate_slot_usage(node, path, state) { /** * @param {VariableDeclarator} declarator * @param {State} state - * @param {SvelteNode[]} path + * @param {ASTNode[]} path */ function extract_type_and_comment(declarator, state, path) { const str = state.str; @@ -1432,7 +1456,10 @@ function extract_type_and_comment(declarator, state, path) { while (str.original[start] === ' ') { start++; } - return { type: str.original.substring(start, declarator.id.typeAnnotation.end), comment }; + return { + type: str.snip(start, declarator.id.typeAnnotation.end).toString(), + comment + }; } let cleaned_comment_arr = comment diff --git a/packages/svelte/tests/migrate/samples/component-type/input.svelte b/packages/svelte/tests/migrate/samples/component-type/input.svelte new file mode 100644 index 000000000000..7184f9f4d1b5 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/component-type/input.svelte @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/component-type/output.svelte b/packages/svelte/tests/migrate/samples/component-type/output.svelte new file mode 100644 index 000000000000..ab774e56ad63 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/component-type/output.svelte @@ -0,0 +1,22 @@ + + + \ No newline at end of file