Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork20
Generate TypeScript definitions for your Svelte components
License
carbon-design-system/sveld
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
sveld is a TypeScript definition generator for Svelte components. It analyzes props, events, slots, and other component features through static analysis. Types and signatures can be defined usingJSDoc notation. The tool can also generate component documentation in Markdown and JSON formats.
The purpose of this project is to make third party Svelte component libraries compatible with the Svelte Language Server and TypeScript with minimal effort required by the author. For example, TypeScript definitions may be used during development via intelligent code completion in Integrated Development Environments (IDE) like VSCode.
Carbon Components Svelte uses this library to auto-generate component types and API metadata:
- TypeScript definitions: Component TypeScript definitions
- Component Index: Markdown file documenting component props, slots, and events
- Component API: Component API metadata in JSON format
Please note that the generated TypeScript definitions require Svelte version 3.55 or greater.
Given a Svelte component,sveld can infer basic prop types to generate TypeScript definitions compatible with theSvelte Language Server:
Button.svelte
<script>exportlet type="button";exportlet primary=false;</script><button {...$$restProps} {type}class:primaryon:click> <slot>Click me</slot></button>
The generated definition extends the officialSvelteComponentTyped interface exported from Svelte.
Button.svelte.d.ts
importtype{SvelteComponentTyped}from"svelte";importtype{SvelteHTMLElements}from"svelte/elements";type$RestProps=SvelteHTMLElements["button"];type$Props={/** *@default "button" */type?:string;/** *@default false */primary?:boolean;[key: `data-${string}`]:any;};exporttypeButtonProps=Omit<$RestProps,keyof$Props>&$Props;exportdefaultclassButtonextendsSvelteComponentTyped<ButtonProps,{click:WindowEventMap["click"]},{default:{}}>{}
Sometimes, inferring prop types is insufficient.
Prop/event/slot types and signatures can be augmented usingJSDoc notations.
/**@type {"button" | "submit" | "reset"} */exportlettype="button";/** * Set to `true` to use the primary variant */exportletprimary=false;
The accompanying JSDoc annotations would generate the following:
importtype{SvelteHTMLElements}from"svelte/elements";type$RestProps=SvelteHTMLElements["button"];type$Props={/** *@default "button" */type?:"button"|"submit"|"reset";/** * Set to `true` to use the primary variant *@default false */primary?:boolean;};exporttypeButtonProps=Omit<$RestProps,keyof$Props>&$Props;exportdefaultclassButtonextendsSvelteComponentTyped<ButtonProps,{click:WindowEventMap["click"]},{default:{}}>{}
sveld uses the Svelte compiler to statically analyze Svelte components exported from a library to generate documentation useful to the end user.
Extracted metadata include:
- props
- slots
- forwarded events
- dispatched events
$$restProps
This library adopts a progressively enhanced approach. Any property type that cannot be inferred (e.g., "hello" is a string) falls back to "any" to minimize incorrectly typed properties or signatures. To mitigate this, the library author can add JSDoc annotations to specify types that cannot be reliably inferred. This represents a progressively enhanced approach because JSDocs are comments that can be ignored by the compiler.
Installsveld as a development dependency.
# npmnpm i -D sveld# pnpmpnpm i -D sveld# Bunbun i -D sveld# Yarnyarn add -D sveld
Import and addsveld as a plugin to yourrollup.config.js.
// rollup.config.jsimportsveltefrom"rollup-plugin-svelte";importresolvefrom"@rollup/plugin-node-resolve";importsveldfrom"sveld";exportdefault{input:"src/index.js",output:{format:"es",file:"lib/index.mjs",},plugins:[svelte(),resolve(),sveld()],};
When building the library, TypeScript definitions are emitted to thetypes folder by default.
Customize the output folder using thetypesOptions.outDir option.
The following example emits the output to thedist folder:
sveld({+ typesOptions: {+ outDir: 'dist'+ }})Thetests/e2e folder contains example set-ups:
- single-export: library that exports one component
- single-export-default-only: library that exports one component using the concise
export { default } ...syntax - multi-export: multi-component library without JSDoc annotations (types are inferred)
- multi-export-typed: multi-component library with JSDoc annotations
- multi-export-typed-ts-only: multi-component library that only generates TS definitions
- glob: library that uses the glob strategy to collect/analyze *.svelte files
- carbon: full
carbon-components-svelteexample
The CLI uses the"svelte" field from yourpackage.json as the entry point:
npx sveld
Generate documentation in JSON and/or Markdown formats using the following flags:
npx sveld --json --markdown
You can also usesveld programmatically in Node.js.
If noinput is specified,sveld will infer the entry point based on thepackage.json#svelte field.
const{ sveld}=require("sveld");constpkg=require("./package.json");sveld({input:"./src/index.js",glob:true,markdown:true,markdownOptions:{onAppend:(type,document,components)=>{if(type==="h1")document.append("quote",`${components.size} components exported from${pkg.name}@${pkg.version}.`);},},json:true,jsonOptions:{outFile:"docs/src/COMPONENT_API.json",},});
Ifjson istrue, aCOMPONENT_API.json file will be generated at the root of your project. This file contains documentation for all components.
Use thejsonOptions.outDir option to specify the folder for individual JSON files to be emitted.
sveld({json:true,jsonOptions:{// an individual JSON file will be generated for each component API// e.g. "docs/Button.api.json"outDir:"docs",},});
TypeScript definitions are outputted to thetypes folder by default. Don't forget to include the folder in yourpackage.json when publishing the package to NPM.
{ "svelte": "./src/index.js", "main": "./lib/index.mjs",+ "types": "./types/index.d.ts", "files": [ "src", "lib",+ "types", ]}By default, only TypeScript definitions are generated.
To generate documentation in Markdown and JSON formats, setmarkdown andjson totrue.
sveld({+ markdown: true,+ json: true,})Without a@type annotation,sveld will infer the primitive type for a prop:
exportletkind="primary";// inferred type: "string"
Use the@type tag to explicitly document the type. In the following example, thekind property has an enumerated (enum) type.
Signature:
/** * Optional description *@type {Type} */
Example:
/** * Specify the kind of button *@type {"primary" | "secondary" | "tertiary"} */exportletkind="primary";/** * Specify the Carbon icon to render *@type {typeof import("carbon-icons-svelte").CarbonIcon} */exportletrenderIcon=Close20;
The@typedef tag can be used to define a common type that is used multiple times within a component. All typedefs defined in a component will be exported from the generated TypeScript definition file.
Signature:
/** *@typedef {Type} TypeName */
Example:
/** *@typedef {string} AuthorName *@typedef {{ name?: AuthorName; dob?: string; }} Author *//**@type {Author} */exportletauthor={};/**@type {Author[]} */exportletauthors=[];
Use the@slot tag for typing component slots. Note that@slot is a non-standard JSDoc tag.
Descriptions are optional for named slots. Currently, the default slot cannot have a description.
Signature:
/** *@slot {Type} slot-name [slot description] */Omitthe`slot-name`totypethedefaultslot./** *@slot {Type} */
Example:
<script>/** * @slot {{ prop: number; doubled: number; }} * @slot {{}} title * @slot {{ prop: number }} body - Customize the paragraph text.*/exportlet prop=0;</script><h1> <slot {prop}doubled={prop*2} /> <slotname="title" /></h1><p> <slotname="body" {prop} /></p>
Use the@event tag to type dispatched events. An event name is required and a description optional.
Usenull as the value if no event detail is provided.
Signature:
/** *@event {EventDetail} eventname [event description] */
Example:
/** *@event {{ key: string }} button:key *@event {null} key – Fired when `key` changes. */exportletkey="";import{createEventDispatcher}from"svelte";constdispatch=createEventDispatcher();$:dispatch("button:key",{ key});$:if(key)dispatch("key");
Output:
exportdefaultclassComponentextendsSvelteComponentTyped<ComponentProps,{"button:key":CustomEvent<{key:string}>;/** Fired when `key` changes. */key:CustomEvent<null>;},{}>{}
sveld can pick up inline HTML elements that$$restProps is forwarded to. However, it cannot infer the underlying element for instantiated components.
You can use the@restProps tag to specify the element tags that$$restProps is forwarded to.
Signature:
/** * Single element *@restProps {tagname} * * Multiple elements *@restProps {tagname-1 | tagname-2 | tagname-3} */
Example:
<script>/** @restProps {h1 | button}*/exportlet edit=false;importButtonfrom"../";</script>{#ifedit} <Button {...$$restProps} />{:else} <h1 {...$$restProps}><slot /></h1>{/if}
In some cases, a component may be based on another component. The@extends tag can be used to extend generated component props.
Signature:
/** *@extends {<relative path to component>} ComponentProps */
Example:
/**@extends {"./Button.svelte"} ButtonProps */exportconstsecondary=true;importButtonfrom"./Button.svelte";
Currently, to define generics for a Svelte component, you must usegenerics attribute on the script tag. Note that this feature isexperimental and may change in the future.
However, thegenerics attribute only works if usinglang="ts"; the language server will produce an error ifgenerics is used without specifyinglang="ts".
<!-- This causes an error because `lang="ts"` must be used. --><scriptgenerics="Row extends DataTableRow = any"></script>
Becausesveld is designed to support JavaScript-only usage as a baseline, the API design to specify generics uses a custom JSDoc tag@generics.
Signature:
/** *@generics {GenericParameter} GenericName */
Example
/** *@generics {Row extends DataTableRow = any} Row */
The generated TypeScript definition will resemble the following:
exportdefaultclassComponent<RowextendsDataTableRow=any>extendsSvelteComponentTyped<ComponentProps<Row>,Record<string,any>,Record<string,any>>{}
For a parameter list, the name should be comma-separated but not include spaces.
/** *@generics {Param1, Param2} Name1,Name2 */
exportdefaultclassComponent<Param1,Param2>extendsSvelteComponentTyped<ComponentProps<Name1,Name2>,Record<string,any>,Record<string,any>>{}
The Svelte Language Server supports component-level comments through the following syntax:<!-- @component [comment] -->.
sveld will copy these over to the exported default component in the TypeScript definition.
Example:
<!-- @component@example<Button> Text</Button>--><button> <slot /></button>
Output:
/** *@example * <Button> * Text * </Button> */exportdefaultclassButtonextendsSvelteComponentTyped<ButtonProps,{},{default:{}}>{}
Refer to thecontributing guidelines.
About
Generate TypeScript definitions for your Svelte components
Topics
Resources
License
Code of conduct
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Contributors12
Uh oh!
There was an error while loading.Please reload this page.