function extract_type_and_comment(declarator, state, path) {
	const str = state.str;
	const parent = path.at(-1);

	// Try to find jsdoc above the declaration
	let comment_node = /** @type {Node} */ (parent)?.leadingComments?.at(-1);

	const comment_start = /** @type {any} */ (comment_node)?.start;
	const comment_end = /** @type {any} */ (comment_node)?.end;
	let comment = comment_node && str.original.substring(comment_start, comment_end);
	if (comment_node) {
		str.update(comment_start, comment_end, '');
	}

	// Find trailing comments
	const trailing_comment_node = /** @type {Node} */ (parent)?.trailingComments?.at(0);
	const trailing_comment_start = /** @type {any} */ (trailing_comment_node)?.start;
	const trailing_comment_end = /** @type {any} */ (trailing_comment_node)?.end;
	let trailing_comment =
		trailing_comment_node && str.original.substring(trailing_comment_start, trailing_comment_end);

	if (trailing_comment_node) {
		str.update(trailing_comment_start, trailing_comment_end, '');
	}

	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++;
		}
		return {
			type: str.original.substring(start, declarator.id.typeAnnotation.end),
			comment,
			trailing_comment
		};
	}

	let cleaned_comment_arr = 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_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');

	let cleaned_comment_arr_trailing = trailing_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_trailing = cleaned_comment_arr_trailing?.findIndex((line) =>
		line.startsWith('@')
	);
	let cleaned_comment_trailing = cleaned_comment_arr_trailing
		?.slice(
			0,
			first_at_comment_trailing !== -1
				? first_at_comment_trailing
				: cleaned_comment_arr_trailing.length
		)
		.join('\n');

	// 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) {
			// try to find JSDoc comments after a hyphen `-`
			const jsdoc_comment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value);
			if (jsdoc_comment) {
				cleaned_comment += jsdoc_comment[1]?.trim();
			}
			return {
				type: match[1],
				comment: cleaned_comment,
				trailing_comment: cleaned_comment_trailing
			};
		}
	}

	// 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: state.uses_ts ? comment : cleaned_comment,
				trailing_comment: state.uses_ts ? trailing_comment : cleaned_comment_trailing
			};
		}
	}
	return {
		type: 'any',
		comment: state.uses_ts ? comment : cleaned_comment,
		trailing_comment: state.uses_ts ? trailing_comment : cleaned_comment_trailing
	};
}
@param {VariableDeclarator} declarator
@param {State} state
@param {AST.SvelteNode[]} path