diff --git a/.changeset/chatty-feet-unite.md b/.changeset/chatty-feet-unite.md new file mode 100644 index 000000000000..6a1468971634 --- /dev/null +++ b/.changeset/chatty-feet-unite.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly migrate ts with inferred type comments diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index c95b0734f97a..057020d730c8 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -114,7 +114,10 @@ export function migrate(source, { filename } = {}) { script_insertions: new Set(), derived_components: new Map(), derived_labeled_statements: new Set(), - has_svelte_self: false + has_svelte_self: false, + uses_ts: !!parsed.instance?.attributes.some( + (attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts' + ) }; if (parsed.module) { @@ -207,15 +210,12 @@ export function migrate(source, { filename } = {}) { // some render tags or forwarded event attributes to add str.appendRight(insertion_point, ` ${props},`); } else { - const uses_ts = parsed.instance?.attributes.some( - (attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts' - ); const type_name = state.scope.root.unique('Props').name; let type = ''; // Try to infer when we don't want to add types (e.g. user doesn't use types, or this is a zero-types +page.svelte) if (state.has_type_or_fallback || state.props.every((prop) => prop.slot_name)) { - if (uses_ts) { + if (state.uses_ts) { type = `interface ${type_name} {${newline_separator}${state.props .map((prop) => { const comment = prop.comment ? `${prop.comment}${newline_separator}` : ''; @@ -236,7 +236,7 @@ export function migrate(source, { filename } = {}) { } let props_declaration = `let {${props_separator}${props}${has_many_props ? `\n${indent}` : ' '}}`; - if (uses_ts) { + if (state.uses_ts) { if (type) { props_declaration = `${type}\n\n${indent}${props_declaration}`; } @@ -355,6 +355,7 @@ export function migrate(source, { filename } = {}) { * derived_components: Map; * derived_labeled_statements: Set; * has_svelte_self: boolean; + * uses_ts: boolean; * }} State */ @@ -1319,7 +1320,7 @@ function extract_type_and_comment(declarator, state, path) { return { type: str.original.substring(start, declarator.id.typeAnnotation.end), comment }; } - let cleaned_comment = comment + let cleaned_comment_arr = comment ?.split('\n') .map((line) => line @@ -1334,9 +1335,9 @@ function extract_type_and_comment(declarator, state, path) { .replace(/^\*\s*/g, '') ) .filter(Boolean); - const first_at_comment = cleaned_comment?.findIndex((line) => line.startsWith('@')); - comment = cleaned_comment - ?.slice(0, first_at_comment !== -1 ? first_at_comment : cleaned_comment.length) + const first_at_comment = cleaned_comment_arr?.findIndex((line) => line.startsWith('@')); + let cleaned_comment = cleaned_comment_arr + ?.slice(0, first_at_comment !== -1 ? first_at_comment : cleaned_comment_arr.length) .join('\n'); // try to find a comment with a type annotation, hinting at jsdoc @@ -1344,7 +1345,7 @@ function extract_type_and_comment(declarator, state, path) { state.has_type_or_fallback = true; const match = /@type {(.+)}/.exec(comment_node.value); if (match) { - return { type: match[1], comment }; + return { type: match[1], comment: cleaned_comment }; } } @@ -1353,11 +1354,11 @@ function extract_type_and_comment(declarator, state, path) { state.has_type_or_fallback = true; // only assume type if it's trivial to infer - else someone would've added a type annotation const type = typeof declarator.init.value; if (type === 'string' || type === 'number' || type === 'boolean') { - return { type, comment }; + return { type, comment: state.uses_ts ? comment : cleaned_comment }; } } - return { type: 'any', comment }; + return { type: 'any', comment: state.uses_ts ? comment : cleaned_comment }; } // Ensure modifiers are applied in the same order as Svelte 4 diff --git a/packages/svelte/tests/migrate/samples/props-ts/input.svelte b/packages/svelte/tests/migrate/samples/props-ts/input.svelte index 513e5f635854..34007c08b767 100644 --- a/packages/svelte/tests/migrate/samples/props-ts/input.svelte +++ b/packages/svelte/tests/migrate/samples/props-ts/input.svelte @@ -4,6 +4,8 @@ export let optional = 'foo'; export let binding: string; export let bindingOptional: string | undefined = 'bar'; + /** this should stay a comment */ + export let no_type_but_comment = 0; {readonly} diff --git a/packages/svelte/tests/migrate/samples/props-ts/output.svelte b/packages/svelte/tests/migrate/samples/props-ts/output.svelte index 9f9d4b503fab..36a0b0f4fb58 100644 --- a/packages/svelte/tests/migrate/samples/props-ts/output.svelte +++ b/packages/svelte/tests/migrate/samples/props-ts/output.svelte @@ -1,18 +1,22 @@