handle jsdoc

pull/11334/head
Simon Holthausen 2 years ago
parent d996519105
commit 294a944aa1

@ -87,13 +87,12 @@ export function migrate(source) {
// some render tags or forwarded event attributes to add
str.appendRight(state.props_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 = '';
if (
parsed.instance?.attributes.some(
(attr) => attr.name === 'lang' && /** @type {any} */ (attr).value[0].data === 'ts'
)
) {
if (uses_ts) {
if (analysis.uses_props || analysis.uses_rest_props) {
type = `interface ${type_name} { [key: string]: any }`;
} else {
@ -103,13 +102,26 @@ export function migrate(source) {
})
.join(`,${props_separator}`)}${has_many_props ? `\n${indent}` : ' '}}`;
}
} else {
if (analysis.uses_props || analysis.uses_rest_props) {
type = `{Record<string, any>}`;
} else {
type = `{${state.props
.map((prop) => {
return `${prop.exported}${prop.optional ? '?' : ''}: ${prop.type}`;
})
.join(`, `)}}`;
}
}
let props_declaration = `let {${props_separator}${props}${has_many_props ? `\n${indent}` : ' '}}`;
if (type) {
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 = `${props_declaration} = $props();`;
}
props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`;
if (parsed.instance) {
props_declaration = `\n${indent}${props_declaration}`;
@ -269,7 +281,7 @@ const instance_script = {
)
: '',
optional: !!declarator.init,
type: extract_type(declarator, state.str),
type: extract_type(declarator, state.str, path),
bindable: binding.mutated || binding.reassigned
});
state.props_insertion_point = /** @type {number} */ (declarator.end);
@ -446,7 +458,7 @@ const template = {
init: '',
bindable: false,
optional: true,
type: slot_props ? 'Snippet' : 'Snippet<[any]>'
type: `import('svelte').${slot_props ? 'Snippet<[any]>' : 'Snippet'}`
});
if (node.fragment.nodes.length > 0) {
@ -466,8 +478,9 @@ const template = {
/**
* @param {import('estree').VariableDeclarator} declarator
* @param {MagicString} str
* @param {import('#compiler').SvelteNode[]} path
*/
function extract_type(declarator, str) {
function extract_type(declarator, str, path) {
if (declarator.id.typeAnnotation) {
let start = declarator.id.typeAnnotation.start + 1; // skip the colon
while (str.original[start] === ' ') {
@ -476,6 +489,19 @@ function extract_type(declarator, str) {
return str.original.substring(start, declarator.id.typeAnnotation.end);
}
// try to find a comment with a type annotation, hinting at jsdoc
const parent = path.at(-1);
if (parent?.type === 'ExportNamedDeclaration' && parent.leadingComments) {
const last = parent.leadingComments[parent.leadingComments.length - 1];
if (last.type === 'Block') {
const match = /@type {([^}]+)}/.exec(last.value);
if (match) {
str.update(/** @type {any} */ (last).start, /** @type {any} */ (last).end, '');
return match[1];
}
}
}
// try to infer it from the init
if (declarator.init?.type === 'Literal') {
const type = typeof declarator.init.value;
@ -506,7 +532,7 @@ function handle_events(node, state) {
// turn on:click into a prop
let exported = `on${name}`;
if (!regex_is_valid_identifier.test(name)) {
exported = `'${name}'`;
exported = `'${exported}'`;
}
// Check if prop already set, could happen when on:click on different elements
let local = state.props.find((prop) => prop.exported === exported)?.local;

@ -1,5 +1,6 @@
<script>
let { onclick, ontoggle, 'custom-event-bubble': oncustom_event_bubble } = $props();
/** @type {{onclick?: (payload: any) => void, ontoggle?: (payload: any) => void, 'oncustom-event-bubble'?: (payload: any) => void}} */
let { onclick, ontoggle, 'oncustom-event-bubble': oncustom_event_bubble } = $props();
</script>
<button onclick={(payload) => {

@ -1,4 +1,5 @@
<script>
/** @type {{Record<string, any>}} */
let { foo, ...rest } = $props();
</script>

@ -1,4 +1,5 @@
<script>
/** @type {{readonly: any, optional?: string, binding: any, bindingOptional?: string}} */
let {
readonly,
optional = 'foo',

@ -1,4 +1,5 @@
<script>
/** @type {{children?: import('svelte').Snippet, foo_1?: import('svelte').Snippet<[any]>, dashed_name?: import('svelte').Snippet}} */
let { children, foo_1, dashed_name } = $props();
</script>

Loading…
Cancel
Save