Skip to content

feat: implement AST approach for TypeScript parsing#267

Merged
metonym merged 3 commits intomainfrom
metonym/ts-component-support
Apr 4, 2026
Merged

feat: implement AST approach for TypeScript parsing#267
metonym merged 3 commits intomainfrom
metonym/ts-component-support

Conversation

@metonym
Copy link
Copy Markdown
Collaborator

@metonym metonym commented Apr 4, 2026

Closes #12

Summary

This changes sveld to preserve and use TypeScript-authored prop types from .svelte components without requiring JSDoc as the primary source of truth.

The parser now keeps raw lang="ts" source for metadata extraction, indexes local TS declarations/imports, and passes internal writer-only metadata so .d.ts output can preserve canonical $props() types while JSON output remains stable.

What Changed

  • stopped preprocessing away TypeScript before metadata parsing
  • strip only the top-level <style> block for parsing
  • added TS declaration indexing in ComponentParser
  • preserve canonical props type info for typed $props() declarations
  • support:
    • typed legacy export let
    • local interface / type aliases
    • intersections
    • typed whole-object $props()
    • imported type references in emitted .d.ts
  • keep positional snippet renders as props-only instead of inventing slot metadata
  • dedupe Snippet imports in emitted declarations
  • documented type precedence and AST-only limitations in the README

Behavior Notes

Type precedence is now:

  1. explicit TypeScript annotation
  2. explicit JSDoc annotation
  3. initializer inference
  4. any

This remains AST-only support. Imported or opaque whole-object $props() types can be preserved in emitted .d.ts, but they are not always expandable into JSON metadata.

Before (Svelte 5 TypeScript)

Given:

<script lang="ts">
  interface NoticeProps {
    message: string;
    tone?: "info" | "warning";
  }

  type ActionProps = {
    onconfirm?: (detail: {
      message: string;
      tone: NoticeProps["tone"];
    }) => void;
  };

  let {
    message,
    tone = "info",
    onconfirm,
  }: NoticeProps & ActionProps = $props();

  function handleClick() {
    onconfirm?.({ message, tone });
  }
</script>

<button
  type="button"
  class:tone-warning={tone === "warning"}
  onclick={handleClick}
>
  {message}
</button>

Output (before)

import { SvelteComponentTyped } from "svelte";

export type ButtonRunesProps = {
  /**
   * @default undefined
   */
  message: string;

  /**
   * @default "info"
   */
  tone?: "info" | "warning";

  /**
   * @default undefined
   */
  onconfirm?: (detail: { message: string; tone: NoticeProps["tone"] }) => void;
};

export default class ButtonRunes extends SvelteComponentTyped<
  ButtonRunesProps,
  Record<string, any>,
  Record<string, never>
> {}

Output (after)

import { SvelteComponentTyped } from "svelte";

interface NoticeProps {
  message: string;
  tone?: "info" | "warning";
}

type ActionProps = {
  onconfirm?: (detail: { message: string; tone: NoticeProps["tone"] }) => void;
};

type $Props = NoticeProps & ActionProps;

export type TypeScriptPropsRunesProps = $Props;

export default class TypeScriptPropsRunes extends SvelteComponentTyped<
  TypeScriptPropsRunesProps,
  Record<string, any>,
  Record<string, never>
> {}

@metonym metonym force-pushed the metonym/ts-component-support branch 3 times, most recently from a7e2855 to 8a7951c Compare April 4, 2026 18:27
@metonym metonym force-pushed the metonym/ts-component-support branch from 8a7951c to aacceb6 Compare April 4, 2026 18:42
@metonym metonym merged commit 6b6bb6a into main Apr 4, 2026
3 checks passed
@metonym metonym deleted the metonym/ts-component-support branch April 4, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parse typescript components

1 participant