Skip to content

Commit 8a06d05

Browse files
authored
fix: do not add jsdoc if no types found (#13738)
fixes #13417 fixes #13724
1 parent be02b7e commit 8a06d05

File tree

8 files changed

+61
-48
lines changed

8 files changed

+61
-48
lines changed

.changeset/smart-carrots-sit.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: do not add jsdoc if no types found

packages/svelte/src/compiler/migrate/index.js

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export function migrate(source, { filename } = {}) {
8181
props: [],
8282
props_insertion_point: parsed.instance?.content.start ?? 0,
8383
has_props_rune: false,
84+
has_type_or_fallback: false,
8485
end: source.length,
8586
names: {
8687
props: analysis.root.unique('props').name,
@@ -202,30 +203,39 @@ export function migrate(source, { filename } = {}) {
202203
);
203204
const type_name = state.scope.root.unique('Props').name;
204205
let type = '';
205-
if (uses_ts) {
206-
type = `interface ${type_name} {${newline_separator}${state.props
207-
.map((prop) => {
208-
const comment = prop.comment ? `${prop.comment}${newline_separator}` : '';
209-
return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};`;
210-
})
211-
.join(newline_separator)}`;
212-
if (analysis.uses_props || analysis.uses_rest_props) {
213-
type += `${state.props.length > 0 ? newline_separator : ''}[key: string]: any`;
206+
207+
// 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)
208+
if (state.has_type_or_fallback || state.props.every((prop) => prop.slot_name)) {
209+
if (uses_ts) {
210+
type = `interface ${type_name} {${newline_separator}${state.props
211+
.map((prop) => {
212+
const comment = prop.comment ? `${prop.comment}${newline_separator}` : '';
213+
return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};`;
214+
})
215+
.join(newline_separator)}`;
216+
if (analysis.uses_props || analysis.uses_rest_props) {
217+
type += `${state.props.length > 0 ? newline_separator : ''}[key: string]: any`;
218+
}
219+
type += `\n${indent}}`;
220+
} else {
221+
type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props
222+
.map((prop) => {
223+
return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}`;
224+
})
225+
.join(``)}\n${indent} */`;
214226
}
215-
type += `\n${indent}}`;
216-
} else {
217-
type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props
218-
.map((prop) => {
219-
return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}`;
220-
})
221-
.join(``)}\n${indent} */`;
222227
}
228+
223229
let props_declaration = `let {${props_separator}${props}${has_many_props ? `\n${indent}` : ' '}}`;
224230
if (uses_ts) {
225-
props_declaration = `${type}\n\n${indent}${props_declaration}`;
231+
if (type) {
232+
props_declaration = `${type}\n\n${indent}${props_declaration}`;
233+
}
226234
props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`;
227235
} else {
228-
props_declaration = `${type && state.props.length > 0 ? `${type}\n\n${indent}` : ''}/** @type {${state.props.length > 0 ? type_name : ''}${analysis.uses_props || analysis.uses_rest_props ? `${state.props.length > 0 ? ' & ' : ''}{ [key: string]: any }` : ''}} */\n${indent}${props_declaration}`;
236+
if (type) {
237+
props_declaration = `${state.props.length > 0 ? `${type}\n\n${indent}` : ''}/** @type {${state.props.length > 0 ? type_name : ''}${analysis.uses_props || analysis.uses_rest_props ? `${state.props.length > 0 ? ' & ' : ''}{ [key: string]: any }` : ''}} */\n${indent}${props_declaration}`;
238+
}
229239
props_declaration = `${props_declaration} = $props();`;
230240
}
231241

@@ -326,6 +336,7 @@ export function migrate(source, { filename } = {}) {
326336
* props: Array<{ local: string; exported: string; init: string; bindable: boolean; slot_name?: string; optional: boolean; type: string; comment?: string; type_only?: boolean; needs_refine_type?: boolean; }>;
327337
* props_insertion_point: number;
328338
* has_props_rune: boolean;
339+
* has_type_or_fallback: boolean;
329340
* end: number;
330341
* names: Record<string, string>;
331342
* legacy_imports: Set<string>;
@@ -517,7 +528,7 @@ const instance_script = {
517528
: '',
518529
optional: !!declarator.init,
519530
bindable: binding.updated,
520-
...extract_type_and_comment(declarator, state.str, path)
531+
...extract_type_and_comment(declarator, state, path)
521532
});
522533
}
523534

@@ -1253,10 +1264,11 @@ function migrate_slot_usage(node, path, state) {
12531264

12541265
/**
12551266
* @param {VariableDeclarator} declarator
1256-
* @param {MagicString} str
1267+
* @param {State} state
12571268
* @param {SvelteNode[]} path
12581269
*/
1259-
function extract_type_and_comment(declarator, str, path) {
1270+
function extract_type_and_comment(declarator, state, path) {
1271+
const str = state.str;
12601272
const parent = path.at(-1);
12611273

12621274
// Try to find jsdoc above the declaration
@@ -1271,6 +1283,7 @@ function extract_type_and_comment(declarator, str, path) {
12711283
}
12721284

12731285
if (declarator.id.typeAnnotation) {
1286+
state.has_type_or_fallback = true;
12741287
let start = declarator.id.typeAnnotation.start + 1; // skip the colon
12751288
while (str.original[start] === ' ') {
12761289
start++;
@@ -1300,6 +1313,7 @@ function extract_type_and_comment(declarator, str, path) {
13001313

13011314
// try to find a comment with a type annotation, hinting at jsdoc
13021315
if (parent?.type === 'ExportNamedDeclaration' && comment_node) {
1316+
state.has_type_or_fallback = true;
13031317
const match = /@type {(.+)}/.exec(comment_node.value);
13041318
if (match) {
13051319
return { type: match[1], comment };
@@ -1308,6 +1322,7 @@ function extract_type_and_comment(declarator, str, path) {
13081322

13091323
// try to infer it from the init
13101324
if (declarator.init?.type === 'Literal') {
1325+
state.has_type_or_fallback = true; // only assume type if it's trivial to infer - else someone would've added a type annotation
13111326
const type = typeof declarator.init.value;
13121327
if (type === 'string' || type === 'number' || type === 'boolean') {
13131328
return { type, comment };
@@ -1533,6 +1548,8 @@ function handle_identifier(node, state, path) {
15331548
parent.type === 'TSInterfaceDeclaration' ? parent.body.body : parent.typeAnnotation?.members;
15341549
if (Array.isArray(members)) {
15351550
if (node.name === '$$Props') {
1551+
state.has_type_or_fallback = true;
1552+
15361553
for (const member of members) {
15371554
const prop = state.props.find((prop) => prop.exported === member.key.name);
15381555

packages/svelte/tests/migrate/samples/$$slots-used-as-variable/output.svelte

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
<script>
2-
/**
3-
* @typedef {Object} Props
4-
* @property {import('svelte').Snippet} [message]
5-
* @property {any} [showMessage]
6-
* @property {import('svelte').Snippet<[any]>} [title]
7-
* @property {import('svelte').Snippet<[any]>} [extra]
8-
*/
9-
10-
/** @type {Props} */
112
let {
123
message,
134
showMessage = message,

packages/svelte/tests/migrate/samples/css-ignore/output.svelte

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
<script>
2-
/**
3-
* @typedef {Object} Props
4-
* @property {any} name
5-
*/
6-
7-
/** @type {Props} */
82
let { name } = $props();
93
</script>
104

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
/** @type {string} */
3+
export let foo;
4+
</script>
5+
6+
<button {foo} {...$$restProps}>click me</button>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
3+
/**
4+
* @typedef {Object} Props
5+
* @property {string} foo
6+
*/
7+
8+
/** @type {Props & { [key: string]: any }} */
9+
let { foo, ...rest } = $props();
10+
</script>
11+
12+
<button {foo} {...rest}>click me</button>

packages/svelte/tests/migrate/samples/props-rest-props/output.svelte

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
<script>
2-
/**
3-
* @typedef {Object} Props
4-
* @property {any} foo
5-
*/
6-
7-
/** @type {Props & { [key: string]: any }} */
82
let { foo, ...rest } = $props();
93
</script>
104

packages/svelte/tests/migrate/samples/reactive-statements-reorder-not-deleting-additions/output.svelte

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33
44
55
import { blah } from './blah.js'
6-
/**
7-
* @typedef {Object} Props
8-
* @property {any} data
9-
*/
10-
11-
/** @type {Props} */
126
let { data } = $props();
137
148
let bar = $state()

0 commit comments

Comments
 (0)