diff --git a/.changeset/sweet-melons-itch.md b/.changeset/sweet-melons-itch.md new file mode 100644 index 0000000000..6d2f9d3e63 --- /dev/null +++ b/.changeset/sweet-melons-itch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use typedef for JSDoc props and maintain comments diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 43d4720a75..b0867766e3 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -213,23 +213,18 @@ export function migrate(source, { filename } = {}) { } type += `\n${indent}}`; } else { - type = `{${state.props + type = `/**\n${indent} * @typedef {Object} ${type_name}${state.props .map((prop) => { - return `${prop.exported}${prop.optional ? '?' : ''}: ${prop.type}`; + return `\n${indent} * @property {${prop.type}} ${prop.optional ? `[${prop.exported}]` : prop.exported}${prop.comment ? ` - ${prop.comment}` : ''}`; }) - .join(`, `)}`; - if (analysis.uses_props || analysis.uses_rest_props) { - type += `${state.props.length > 0 ? ', ' : ''}[key: string]: any`; - } - type += '}'; + .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}`; props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`; } else { - props_declaration = `/** @type {${type}} */\n${indent}${props_declaration}`; + 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}`; props_declaration = `${props_declaration} = $props();`; } @@ -1265,11 +1260,10 @@ function extract_type_and_comment(declarator, str, path) { // Try to find jsdoc above the declaration let comment_node = /** @type {Node} */ (parent)?.leadingComments?.at(-1); - if (comment_node?.type !== 'Block') comment_node = undefined; const comment_start = /** @type {any} */ (comment_node)?.start; const comment_end = /** @type {any} */ (comment_node)?.end; - const comment = comment_node && str.original.substring(comment_start, comment_end); + let comment = comment_node && str.original.substring(comment_start, comment_end); if (comment_node) { str.update(comment_start, comment_end, ''); @@ -1283,11 +1277,31 @@ function extract_type_and_comment(declarator, str, path) { return { type: str.original.substring(start, declarator.id.typeAnnotation.end), comment }; } + let cleaned_comment = comment + ?.split('\n') + .map((line) => + line + .trim() + // replace `// ` for one liners + .replace(/^\/\/\s*/g, '') + // replace `\**` for the initial JSDoc + .replace(/^\/\*\*?\s*/g, '') + // migrate `*/` for the end of JSDoc + .replace(/\s*\*\/$/g, '') + // remove any initial `* ` to clean the comment + .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) + .join('\n'); + // try to find a comment with a type annotation, hinting at jsdoc if (parent?.type === 'ExportNamedDeclaration' && comment_node) { const match = /@type {(.+)}/.exec(comment_node.value); if (match) { - return { type: match[1] }; + return { type: match[1], comment }; } } diff --git a/packages/svelte/tests/migrate/samples/$$slots-used-as-variable-$$props/output.svelte b/packages/svelte/tests/migrate/samples/$$slots-used-as-variable-$$props/output.svelte index 72960b7e34..c1a3e8d636 100644 --- a/packages/svelte/tests/migrate/samples/$$slots-used-as-variable-$$props/output.svelte +++ b/packages/svelte/tests/migrate/samples/$$slots-used-as-variable-$$props/output.svelte @@ -1,5 +1,11 @@ diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte new file mode 100644 index 0000000000..333980513a --- /dev/null +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte new file mode 100644 index 0000000000..271dd23ffb --- /dev/null +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/props-and-labeled/output.svelte b/packages/svelte/tests/migrate/samples/props-and-labeled/output.svelte index 57893d11ac..8b0d6ea35b 100644 --- a/packages/svelte/tests/migrate/samples/props-and-labeled/output.svelte +++ b/packages/svelte/tests/migrate/samples/props-and-labeled/output.svelte @@ -1,5 +1,11 @@ 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 7afdc7aac7..d2e4f81619 100644 --- a/packages/svelte/tests/migrate/samples/props-rest-props/output.svelte +++ b/packages/svelte/tests/migrate/samples/props-rest-props/output.svelte @@ -1,5 +1,10 @@ diff --git a/packages/svelte/tests/migrate/samples/props/output.svelte b/packages/svelte/tests/migrate/samples/props/output.svelte index 346489e456..80b63c008e 100644 --- a/packages/svelte/tests/migrate/samples/props/output.svelte +++ b/packages/svelte/tests/migrate/samples/props/output.svelte @@ -1,6 +1,14 @@ diff --git a/packages/svelte/tests/migrate/samples/slots-multiple/output.svelte b/packages/svelte/tests/migrate/samples/slots-multiple/output.svelte index c663f2bcdd..521a4b47d8 100644 --- a/packages/svelte/tests/migrate/samples/slots-multiple/output.svelte +++ b/packages/svelte/tests/migrate/samples/slots-multiple/output.svelte @@ -1,5 +1,10 @@ diff --git a/packages/svelte/tests/migrate/samples/slots-with-$$props/output.svelte b/packages/svelte/tests/migrate/samples/slots-with-$$props/output.svelte index c76c7d8283..2d6d5b8a36 100644 --- a/packages/svelte/tests/migrate/samples/slots-with-$$props/output.svelte +++ b/packages/svelte/tests/migrate/samples/slots-with-$$props/output.svelte @@ -1,5 +1,12 @@ diff --git a/packages/svelte/tests/migrate/samples/slots/output.svelte b/packages/svelte/tests/migrate/samples/slots/output.svelte index 30fdf8dfbe..550661e31e 100644 --- a/packages/svelte/tests/migrate/samples/slots/output.svelte +++ b/packages/svelte/tests/migrate/samples/slots/output.svelte @@ -1,5 +1,12 @@ diff --git a/packages/svelte/tests/migrate/samples/svelte-component/output.svelte b/packages/svelte/tests/migrate/samples/svelte-component/output.svelte index 1e61d23ba5..e8e29edd19 100644 --- a/packages/svelte/tests/migrate/samples/svelte-component/output.svelte +++ b/packages/svelte/tests/migrate/samples/svelte-component/output.svelte @@ -1,5 +1,5 @@ diff --git a/packages/svelte/tests/migrate/samples/svelte-self/output.svelte b/packages/svelte/tests/migrate/samples/svelte-self/output.svelte index 23b9af5c00..e23b185b90 100644 --- a/packages/svelte/tests/migrate/samples/svelte-self/output.svelte +++ b/packages/svelte/tests/migrate/samples/svelte-self/output.svelte @@ -1,6 +1,6 @@