From 8a06d051e81492ff0d2ea2b9e6b54ac1c9d9bf87 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:18:32 +0200 Subject: [PATCH] fix: do not add jsdoc if no types found (#13738) fixes #13417 fixes #13724 --- .changeset/smart-carrots-sit.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 59 ++++++++++++------- .../$$slots-used-as-variable/output.svelte | 9 --- .../migrate/samples/css-ignore/output.svelte | 6 -- .../props-rest-props-jsdoc/input.svelte | 6 ++ .../props-rest-props-jsdoc/output.svelte | 12 ++++ .../samples/props-rest-props/output.svelte | 6 -- .../output.svelte | 6 -- 8 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 .changeset/smart-carrots-sit.md create mode 100644 packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/output.svelte diff --git a/.changeset/smart-carrots-sit.md b/.changeset/smart-carrots-sit.md new file mode 100644 index 0000000000..1bf6fcddd1 --- /dev/null +++ b/.changeset/smart-carrots-sit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: do not add jsdoc if no types found diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index f2b6977ea1..c72988582d 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -81,6 +81,7 @@ export function migrate(source, { filename } = {}) { props: [], props_insertion_point: parsed.instance?.content.start ?? 0, has_props_rune: false, + has_type_or_fallback: false, end: source.length, names: { props: analysis.root.unique('props').name, @@ -202,30 +203,39 @@ export function migrate(source, { filename } = {}) { ); const type_name = state.scope.root.unique('Props').name; let type = ''; - if (uses_ts) { - type = `interface ${type_name} {${newline_separator}${state.props - .map((prop) => { - const comment = prop.comment ? `${prop.comment}${newline_separator}` : ''; - return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};`; - }) - .join(newline_separator)}`; - if (analysis.uses_props || analysis.uses_rest_props) { - type += `${state.props.length > 0 ? newline_separator : ''}[key: string]: any`; + + // 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) { + type = `interface ${type_name} {${newline_separator}${state.props + .map((prop) => { + const comment = prop.comment ? `${prop.comment}${newline_separator}` : ''; + return `${comment}${prop.exported}${prop.optional ? '?' : ''}: ${prop.type};`; + }) + .join(newline_separator)}`; + if (analysis.uses_props || analysis.uses_rest_props) { + type += `${state.props.length > 0 ? newline_separator : ''}[key: string]: any`; + } + type += `\n${indent}}`; + } else { + type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props + .map((prop) => { + return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}`; + }) + .join(``)}\n${indent} */`; } - type += `\n${indent}}`; - } else { - type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props - .map((prop) => { - return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}`; - }) - .join(``)}\n${indent} */`; } + let props_declaration = `let {${props_separator}${props}${has_many_props ? `\n${indent}` : ' '}}`; if (uses_ts) { - props_declaration = `${type}\n\n${indent}${props_declaration}`; + if (type) { + props_declaration = `${type}\n\n${indent}${props_declaration}`; + } props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`; } else { - 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}`; + if (type) { + 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}`; + } props_declaration = `${props_declaration} = $props();`; } @@ -326,6 +336,7 @@ export function migrate(source, { filename } = {}) { * 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; }>; * props_insertion_point: number; * has_props_rune: boolean; + * has_type_or_fallback: boolean; * end: number; * names: Record; * legacy_imports: Set; @@ -517,7 +528,7 @@ const instance_script = { : '', optional: !!declarator.init, bindable: binding.updated, - ...extract_type_and_comment(declarator, state.str, path) + ...extract_type_and_comment(declarator, state, path) }); } @@ -1253,10 +1264,11 @@ function migrate_slot_usage(node, path, state) { /** * @param {VariableDeclarator} declarator - * @param {MagicString} str + * @param {State} state * @param {SvelteNode[]} path */ -function extract_type_and_comment(declarator, str, path) { +function extract_type_and_comment(declarator, state, path) { + const str = state.str; const parent = path.at(-1); // Try to find jsdoc above the declaration @@ -1271,6 +1283,7 @@ function extract_type_and_comment(declarator, str, path) { } if (declarator.id.typeAnnotation) { + state.has_type_or_fallback = true; let start = declarator.id.typeAnnotation.start + 1; // skip the colon while (str.original[start] === ' ') { start++; @@ -1300,6 +1313,7 @@ function extract_type_and_comment(declarator, str, path) { // try to find a comment with a type annotation, hinting at jsdoc if (parent?.type === 'ExportNamedDeclaration' && comment_node) { + state.has_type_or_fallback = true; const match = /@type {(.+)}/.exec(comment_node.value); if (match) { return { type: match[1], comment }; @@ -1308,6 +1322,7 @@ function extract_type_and_comment(declarator, str, path) { // try to infer it from the init if (declarator.init?.type === 'Literal') { + 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 }; @@ -1533,6 +1548,8 @@ function handle_identifier(node, state, path) { parent.type === 'TSInterfaceDeclaration' ? parent.body.body : parent.typeAnnotation?.members; if (Array.isArray(members)) { if (node.name === '$$Props') { + state.has_type_or_fallback = true; + for (const member of members) { const prop = state.props.find((prop) => prop.exported === member.key.name); diff --git a/packages/svelte/tests/migrate/samples/$$slots-used-as-variable/output.svelte b/packages/svelte/tests/migrate/samples/$$slots-used-as-variable/output.svelte index 996d449406..ed4557a80d 100644 --- a/packages/svelte/tests/migrate/samples/$$slots-used-as-variable/output.svelte +++ b/packages/svelte/tests/migrate/samples/$$slots-used-as-variable/output.svelte @@ -1,13 +1,4 @@ diff --git a/packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/input.svelte b/packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/input.svelte new file mode 100644 index 0000000000..9674508dd4 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/input.svelte @@ -0,0 +1,6 @@ + + + diff --git a/packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/output.svelte b/packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/output.svelte new file mode 100644 index 0000000000..e268209374 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/props-rest-props-jsdoc/output.svelte @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/props-rest-props/output.svelte b/packages/svelte/tests/migrate/samples/props-rest-props/output.svelte index d2e4f81619..3a872b1585 100644 --- a/packages/svelte/tests/migrate/samples/props-rest-props/output.svelte +++ b/packages/svelte/tests/migrate/samples/props-rest-props/output.svelte @@ -1,10 +1,4 @@ diff --git a/packages/svelte/tests/migrate/samples/reactive-statements-reorder-not-deleting-additions/output.svelte b/packages/svelte/tests/migrate/samples/reactive-statements-reorder-not-deleting-additions/output.svelte index 35cf7318e4..9809ac79e0 100644 --- a/packages/svelte/tests/migrate/samples/reactive-statements-reorder-not-deleting-additions/output.svelte +++ b/packages/svelte/tests/migrate/samples/reactive-statements-reorder-not-deleting-additions/output.svelte @@ -3,12 +3,6 @@ import { blah } from './blah.js' - /** - * @typedef {Object} Props - * @property {any} data - */ - - /** @type {Props} */ let { data } = $props(); let bar = $state()