Resolve the props anchor of a legacy component from its svelte2tsx virtual.
svelte2tsx ends $$render with return {props: …, slots: …, …}, and the
props value is the anchor. The shapes recognized:
- Typed —
<object> as <Type>. The type is a literal svelte2tsx writes
from the exports, carrying each prop's JSDoc and optionality
({a?: typeof a, b: number}: 'generated'), or the author's reserved
$$Props, bare or intersected with a literal of the accessors
('authored'). - Untyped — a bare object literal (
{a: a, b: b}: 'untyped'), what a
JS component gets, and a TS one whose exports are all untyped and
uninitialized, with each binding's JSDoc copied onto its property, so a
@type there types the prop.
Anything else is undefined: an unknown virtual yields no props rather
than guessed ones, and the caller reports it.
A props value svelte2tsx built from no exports is unrecognized here too:
an empty object under a cast ({} as Record<string, never>, or in JS a
JSDoc cast on ({})). The caller asks only when the scan found exports,
so an empty value means svelte2tsx didn't read the script the scan read,
and the exports are reported, not dropped in silence.
Whether the component is legacy at all isn't decided here. The caller asks
only for a component svelte2tsx read as legacy (classifyRenderMode in
svelte.ts); one it read as runes has an empty props type that would be
unrecognized here, and its exported bindings are instance exports.
The props are what the type declares, so an export let name an authored
$$Props leaves out is no prop: a typed consumer can't pass it.
Where svelte2tsx's props value departs from Svelte, the output follows it:
- A binding exported both directly and through a renaming clause
(
export let a; export {a as b}) keeps only the renamed prop, without the
binding's JSDoc. In TS it reads as optional. - An export clause written above its binding loses the binding's JSDoc too,
and in TS reads as optional.