Skip to content

Commit

Permalink
feat: migrate Component to ComponentExports<typeof Component> in …
Browse files Browse the repository at this point in the history
…TS (#13656)

Closes #13491

---------

Co-authored-by: Simon Holthausen <[email protected]>
  • Loading branch information
paoloricciuti and dummdidumm authored Oct 31, 2024
1 parent d93ad3b commit 4715dfa
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-parrots-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: migrate `Component` to `ComponentExports<typeof Component>` in TS
37 changes: 32 additions & 5 deletions packages/svelte/src/compiler/migrate/index.js
Original file line number Diff line number Diff line change
@@ -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' */
Expand Down Expand Up @@ -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;
Expand All @@ -416,11 +418,12 @@ export function migrate(source, { filename, use_ts } = {}) {
* derived_components: Map<string, string>;
* derived_labeled_statements: Set<LabeledStatement>;
* has_svelte_self: boolean;
* migrate_prop_component_type?: boolean;
* uses_ts: boolean;
* }} State
*/

/** @type {Visitors<SvelteNode, State>} */
/** @type {Visitors<ASTNode, State>} */
const instance_script = {
_(node, { state, next }) {
// @ts-expect-error
Expand All @@ -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<typeof ${state.str.original.substring(node.start, node.end)}>`
);
}
}
},
Identifier(node, { state, path, next }) {
handle_identifier(node, state, path);
next();
},
ImportDeclaration(node, { state }) {
state.props_insertion_point = node.end ?? state.props_insertion_point;
Expand Down Expand Up @@ -503,6 +525,8 @@ const instance_script = {
return;
}

next();

let nr_of_props = 0;

for (const declarator of node.declarations) {
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions packages/svelte/tests/migrate/samples/component-type/input.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import Component from "./Component.svelte";
import type ComponentType from "./Component.svelte";
export let my_comp: Component;
export let my_component_type: ComponentType;
export function enhance(comp: Component, comp_type: ComponentType){
}
let comp: Component | ComponentType | undefined = undefined;
export const the_comp: Component | ComponentType = comp;
</script>

<Component bind:this={comp} />
22 changes: 22 additions & 0 deletions packages/svelte/tests/migrate/samples/component-type/output.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
import Component from "./Component.svelte";
import type ComponentType from "./Component.svelte";
interface Props {
my_comp: import('svelte').ComponentExports<typeof Component>;
my_component_type: import('svelte').ComponentExports<typeof ComponentType>;
}
let { my_comp, my_component_type }: Props = $props();
export function enhance(comp: import('svelte').ComponentExports<typeof Component>, comp_type: import('svelte').ComponentExports<typeof ComponentType>){
}
let comp: import('svelte').ComponentExports<typeof Component> | import('svelte').ComponentExports<typeof ComponentType> | undefined = $state(undefined);
export const the_comp: import('svelte').ComponentExports<typeof Component> | import('svelte').ComponentExports<typeof ComponentType> = comp;
</script>

<Component bind:this={comp} />

0 comments on commit 4715dfa

Please sign in to comment.