From 766a75c5fdb2e9d1f62a3e675c383a9313ad34e0 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 8 Dec 2023 16:33:16 +0100 Subject: [PATCH 1/5] chore: check in types To ensure that changes to code/types doesn't result in unwanted changes in type generation, or that bumps to dts-buddy don't cause unwanted regressions, we're checking in the generated types. Types should be committed as-is (don't format it with prettier!). CI is enhanced to check that git sees no changed files after generating the types, which would mean types have changed. --- .github/workflows/ci.yml | 2 +- packages/svelte/.gitignore | 3 +- packages/svelte/types/index.d.ts | 2350 ++++++++++++++++++++++++++++++ 3 files changed, 2353 insertions(+), 2 deletions(-) create mode 100644 packages/svelte/types/index.d.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08820f0ed3ef..72a22ec0e976 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,4 +50,4 @@ jobs: with: node-version: 18 cache: pnpm - - run: 'pnpm i && pnpm check && pnpm lint' + - run: pnpm i && pnpm check && pnpm lint && pnpm build && [ "`git status --porcelain=v1`" == "" ] # ensures that commited types are up-to-date diff --git a/packages/svelte/.gitignore b/packages/svelte/.gitignore index faa8892951fb..e4925570e53b 100644 --- a/packages/svelte/.gitignore +++ b/packages/svelte/.gitignore @@ -1,4 +1,5 @@ -/types +/types/*.map +/types/compiler /compiler.cjs /action.d.ts diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts new file mode 100644 index 000000000000..6e12b0a7eded --- /dev/null +++ b/packages/svelte/types/index.d.ts @@ -0,0 +1,2350 @@ +declare module 'svelte' { + // This should contain all the public interfaces (not all of them are actually importable, check current Svelte for which ones are). + + /** + * @deprecated Svelte components were classes in Svelte 4. In Svelte 5, thy are not anymore. + * Use `mount` or `createRoot` instead to instantiate components. + * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) + * for more info. + */ + export interface ComponentConstructorOptions< + Props extends Record = Record + > { + target: Element | Document | ShadowRoot; + anchor?: Element; + props?: Props; + context?: Map; + hydrate?: boolean; + intro?: boolean; + $$inline?: boolean; + } + + // Utility type for ensuring backwards compatibility on a type level: If there's a default slot, add 'children' to the props if it doesn't exist there already + type PropsWithChildren = Props & + (Props extends { children?: any } + ? {} + : Slots extends { default: any } + ? { children?: Snippet } + : {}); + + /** + * Can be used to create strongly typed Svelte components. + * + * #### Example: + * + * You have component library on npm called `component-library`, from which + * you export a component called `MyComponent`. For Svelte+TypeScript users, + * you want to provide typings. Therefore you create a `index.d.ts`: + * ```ts + * import { SvelteComponent } from "svelte"; + * export class MyComponent extends SvelteComponent<{foo: string}> {} + * ``` + * Typing this makes it possible for IDEs like VS Code with the Svelte extension + * to provide intellisense and to use the component like this in a Svelte file + * with TypeScript: + * ```svelte + * + * + * ``` + * + * This was the base class for Svelte components in Svelte 4. Svelte 5+ components + * are completely different under the hood. You should only use this type for typing, + * not actually instantiate components with `new` - use `mount` or `createRoot` instead. + * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) + * for more info. + */ + export class SvelteComponent< + Props extends Record = any, + Events extends Record = any, + Slots extends Record = any + > { + [prop: string]: any; + /** + * @deprecated This constructor only exists when using the `asClassComponent` compatibility helper, which + * is a stop-gap solution. Migrate towards using `mount` or `createRoot` instead. See + * https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info. + */ + constructor(options: ComponentConstructorOptions>); + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + * */ + $$prop_def: PropsWithChildren; + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + * + * */ + $$events_def: Events; + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + * + * */ + $$slot_def: Slots; + + /** + * @deprecated This method only exists when using one of the legacy compatibility helpers, which + * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * for more info. + */ + $destroy(): void; + + /** + * @deprecated This method only exists when using one of the legacy compatibility helpers, which + * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * for more info. + */ + $on>( + type: K, + callback: (e: Events[K]) => void + ): () => void; + + /** + * @deprecated This method only exists when using one of the legacy compatibility helpers, which + * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * for more info. + */ + $set(props: Partial): void; + } + + /** + * @deprecated Use `SvelteComponent` instead. See TODO for more information. + */ + export class SvelteComponentTyped< + Props extends Record = any, + Events extends Record = any, + Slots extends Record = any + > extends SvelteComponent {} + + /** + * Convenience type to get the events the given component expects. Example: + * ```html + * + * + * + * ``` + */ + export type ComponentEvents = Comp extends SvelteComponent< + any, + infer Events + > + ? Events + : never; + + /** + * Convenience type to get the props the given component expects. Example: + * ```html + * + * ``` + */ + export type ComponentProps = Comp extends SvelteComponent + ? Props + : never; + + /** + * Convenience type to get the type of a Svelte component. Useful for example in combination with + * dynamic components using ``. + * + * Example: + * ```html + * + * + * + * + * ``` + */ + export type ComponentType = (new ( + options: ComponentConstructorOptions< + Comp extends SvelteComponent ? Props : Record + > + ) => Comp) & { + /** The custom element version of the component. Only present if compiled with the `customElement` compiler option */ + element?: typeof HTMLElement; + }; + + const SnippetReturn: unique symbol; + + /** + * The type of a `#snippet` block. You can use it to (for example) express that your component expects a snippet of a certain type: + * ```ts + * let { banner } = $props<{ banner: Snippet<{ text: string }> }>(); + * ``` + * You can only call a snippet through the `{@render ...}` tag. + */ + export interface Snippet { + (arg: T): typeof SnippetReturn & { + _: 'functions passed to {@render ...} tags must use the `Snippet` type imported from "svelte"'; + }; + } + + interface DispatchOptions { + cancelable?: boolean; + } + + export interface EventDispatcher> { + // Implementation notes: + // - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode + // - | null | undefined is added for convenience, as they are equivalent for the custom event constructor (both result in a null detail) + ( + ...args: null extends EventMap[Type] + ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] + : undefined extends EventMap[Type] + ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] + : [type: Type, parameter: EventMap[Type], options?: DispatchOptions] + ): boolean; + } + /** + * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. + * It must be called during the component's initialisation (but doesn't need to live *inside* the component; + * it can be called from an external module). + * + * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. + * + * `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api). + * + * https://svelte.dev/docs/svelte#onmount + * */ + export function onMount(fn: () => NotFunction | Promise> | (() => any)): void; + /** + * Retrieves the context that belongs to the closest parent component with the specified `key`. + * Must be called during component initialisation. + * + * https://svelte.dev/docs/svelte#getcontext + * */ + export function getContext(key: any): T; + /** + * Associates an arbitrary `context` object with the current component and the specified `key` + * and returns that object. The context is then available to children of the component + * (including slotted content) with `getContext`. + * + * Like lifecycle functions, this must be called during component initialisation. + * + * https://svelte.dev/docs/svelte#setcontext + * */ + export function setContext(key: any, context: T): T; + /** + * Checks whether a given `key` has been set in the context of a parent component. + * Must be called during component initialisation. + * + * https://svelte.dev/docs/svelte#hascontext + * */ + export function hasContext(key: any): boolean; + /** + * Retrieves the whole context map that belongs to the closest parent component. + * Must be called during component initialisation. Useful, for example, if you + * programmatically create a component and want to pass the existing context to it. + * + * https://svelte.dev/docs/svelte#getallcontexts + * */ + export function getAllContexts = Map>(): T; + /** + * Creates an event dispatcher that can be used to dispatch [component events](/docs#template-syntax-component-directives-on-eventname). + * Event dispatchers are functions that can take two arguments: `name` and `detail`. + * + * Component events created with `createEventDispatcher` create a + * [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent). + * These events do not [bubble](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). + * The `detail` argument corresponds to the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) + * property and can contain any type of data. + * + * The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument: + * ```ts + * const dispatch = createEventDispatcher<{ + * loaded: never; // does not take a detail argument + * change: string; // takes a detail argument of type string, which is required + * optional: number | null; // takes an optional detail argument of type number + * }>(); + * ``` + * + * https://svelte.dev/docs/svelte#createeventdispatcher + * */ + export function createEventDispatcher = any>(): EventDispatcher; + /** + * Schedules a callback to run immediately before the component is updated after any state change. + * + * The first time the callback runs will be before the initial `onMount`. + * + * In runes mode use `$effect.pre` instead. + * + * https://svelte.dev/docs/svelte#beforeupdate + * @deprecated Use `$effect.pre` instead — see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate + * */ + export function beforeUpdate(fn: () => void): void; + /** + * Schedules a callback to run immediately after the component has been updated. + * + * The first time the callback runs will be after the initial `onMount`. + * + * In runes mode use `$effect` instead. + * + * https://svelte.dev/docs/svelte#afterupdate + * @deprecated Use `$effect` instead — see https://svelte-5-preview.vercel.app/docs/deprecations#beforeupdate-and-afterupdate + * */ + export function afterUpdate(fn: () => void): void; + /** + * Anything except a function + */ + type NotFunction = T extends Function ? never : T; + /** + * Mounts the given component to the given target and returns a handle to the component's public accessors + * as well as a `$set` and `$destroy` method to update the props of the component or destroy it. + * + * If you don't need to interact with the component after mounting, use `mount` instead to save some bytes. + * + * */ + export function createRoot, Exports extends Record | undefined, Events extends Record>(component: { + new (options: ComponentConstructorOptions | undefined; + })>): SvelteComponent; + }, options: { + target: Node; + props?: Props | undefined; + events?: Events | undefined; + context?: Map | undefined; + intro?: boolean | undefined; + recover?: false | undefined; + }): Exports & { + $destroy: () => void; + $set: (props: Partial) => void; + }; + /** + * Mounts the given component to the given target and returns the accessors of the component and a function to destroy it. + * + * If you need to interact with the component after mounting, use `createRoot` instead. + * + * */ + export function mount, Exports extends Record | undefined, Events extends Record>(component: { + new (options: ComponentConstructorOptions | undefined; + })>): SvelteComponent; + }, options: { + target: Node; + props?: Props | undefined; + events?: Events | undefined; + context?: Map | undefined; + intro?: boolean | undefined; + recover?: false | undefined; + }): [Exports, () => void]; + /** + * Synchronously flushes any pending state changes and those that result from it. + * */ + export function flushSync(fn?: (() => void) | undefined): void; + /** + * Returns a promise that resolves once any pending state changes have been applied. + * */ + export function tick(): Promise; + /** + * Use `untrack` to prevent something from being treated as an `$effect`/`$derived` dependency. + * + * https://svelte-5-preview.vercel.app/docs/functions#untrack + * */ + export function untrack(fn: () => T): T; + /** + * Schedules a callback to run immediately before the component is unmounted. + * + * Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the + * only one that runs inside a server-side component. + * + * https://svelte.dev/docs/svelte#ondestroy + * */ + export function onDestroy(fn: () => any): void; +} + +declare module 'svelte/action' { + /** + * Actions can return an object containing the two properties defined in this interface. Both are optional. + * - update: An action can have a parameter. This method will be called whenever that parameter changes, + * immediately after Svelte has applied updates to the markup. `ActionReturn` and `ActionReturn` both + * mean that the action accepts no parameters. + * - destroy: Method that is called after the element is unmounted + * + * Additionally, you can specify which additional attributes and events the action enables on the applied element. + * This applies to TypeScript typings only and has no effect at runtime. + * + * Example usage: + * ```ts + * interface Attributes { + * newprop?: string; + * 'on:event': (e: CustomEvent) => void; + * } + * + * export function myAction(node: HTMLElement, parameter: Parameter): ActionReturn { + * // ... + * return { + * update: (updatedParameter) => {...}, + * destroy: () => {...} + * }; + * } + * ``` + * + * Docs: https://svelte.dev/docs/svelte-action + */ + export interface ActionReturn< + Parameter = undefined, + Attributes extends Record = Record + > { + update?: (parameter: Parameter) => void; + destroy?: () => void; + /** + * ### DO NOT USE THIS + * This exists solely for type-checking and has no effect at runtime. + * Set this through the `Attributes` generic instead. + */ + $$_attributes?: Attributes; + } + + /** + * Actions are functions that are called when an element is created. + * You can use this interface to type such actions. + * The following example defines an action that only works on `
` elements + * and optionally accepts a parameter which it has a default value for: + * ```ts + * export const myAction: Action = (node, param = { someProperty: true }) => { + * // ... + * } + * ``` + * `Action` and `Action` both signal that the action accepts no parameters. + * + * You can return an object with methods `update` and `destroy` from the function and type which additional attributes and events it has. + * See interface `ActionReturn` for more details. + * + * Docs: https://svelte.dev/docs/svelte-action + */ + export interface Action< + Element = HTMLElement, + Parameter = undefined, + Attributes extends Record = Record + > { + ( + ...args: undefined extends Parameter + ? [node: Node, parameter?: Parameter] + : [node: Node, parameter: Parameter] + ): void | ActionReturn; + } + + // Implementation notes: + // - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode +} + +declare module 'svelte/animate' { + // todo: same as Transition, should it be shared? + export interface AnimationConfig { + delay?: number; + duration?: number; + easing?: (t: number) => number; + css?: (t: number, u: number) => string; + tick?: (t: number, u: number) => void; + } + + export interface FlipParams { + delay?: number; + duration?: number | ((len: number) => number); + easing?: (t: number) => number; + } + /** + * The flip function calculates the start and end position of an element and animates between them, translating the x and y values. + * `flip` stands for [First, Last, Invert, Play](https://aerotwist.com/blog/flip-your-animations/). + * + * https://svelte.dev/docs/svelte-animate#flip + * */ + export function flip(node: Element, { from, to }: { + from: DOMRect; + to: DOMRect; + }, params?: FlipParams): AnimationConfig; +} + +declare module 'svelte/compiler' { + import type { ArrayExpression, AssignmentExpression, Expression, Identifier, MemberExpression, ObjectExpression, Pattern, ClassDeclaration, FunctionDeclaration, ImportDeclaration, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, FunctionExpression, Node, Program } from 'estree'; + import type { Context } from 'zimmerframe'; + /** + * `compile` converts your `.svelte` source code into a JavaScript module that exports a component + * + * https://svelte.dev/docs/svelte-compiler#svelte-compile + * @param source The component source code + * @param options The compiler options + * */ + export function compile(source: string, options: any): any; + /** + * `compileModule` takes your JavaScript source code containing runes, and turns it into a JavaScript module. + * + * https://svelte.dev/docs/svelte-compiler#svelte-compile + * @param source The component source code + * */ + export function compileModule(source: string, options: any): any; + /** + * The parse function parses a component, returning only its abstract syntax tree. + * + * The `modern` option (`false` by default in Svelte 5) makes the parser return a modern AST instead of the legacy AST. + * `modern` will become `true` by default in Svelte 6, and the option will be removed in Svelte 7. + * + * https://svelte.dev/docs/svelte-compiler#svelte-parse + * */ + export function parse(source: string, options?: { + filename?: string | undefined; + modern?: boolean | undefined; + } | undefined): any | LegacySvelteNode; + /** + * @deprecated Replace this with `import { walk } from 'estree-walker'` + * */ + function walk(): never; + interface BaseNode { + type: string; + start: number; + end: number; + } + + interface BaseElement extends BaseNode { + name: string; + attributes: Array; + children: Array; + } + + interface LegacyAction extends BaseNode { + type: 'Action'; + /** The 'x' in `use:x` */ + name: string; + /** The 'y' in `use:x={y}` */ + expression: null | Expression; + } + + interface LegacyAnimation extends BaseNode { + type: 'Animation'; + /** The 'x' in `animate:x` */ + name: string; + /** The y in `animate:x={y}` */ + expression: null | Expression; + } + + interface LegacyBinding extends BaseNode { + type: 'Binding'; + /** The 'x' in `bind:x` */ + name: string; + /** The y in `bind:x={y}` */ + expression: Identifier | MemberExpression; + } + + interface LegacyBody extends BaseElement { + type: 'Body'; + name: 'svelte:body'; + } + + interface LegacyAttribute extends BaseNode { + type: 'Attribute'; + name: string; + value: true | Array; + } + + interface LegacyAttributeShorthand extends BaseNode { + type: 'AttributeShorthand'; + expression: Expression; + } + + interface LegacyLet extends BaseNode { + type: 'Let'; + /** The 'x' in `let:x` */ + name: string; + /** The 'y' in `let:x={y}` */ + expression: null | Identifier | ArrayExpression | ObjectExpression; + } + + interface LegacyCatchBlock extends BaseNode { + type: 'CatchBlock'; + children: LegacySvelteNode[]; + skip: boolean; + } + + interface LegacyClass extends BaseNode { + type: 'Class'; + /** The 'x' in `class:x` */ + name: 'class'; + /** The 'y' in `class:x={y}`, or the `x` in `class:x` */ + expression: Expression; + } + + interface LegacyDocument extends BaseElement { + type: 'Document'; + } + + interface LegacyElement { + type: 'Element'; + } + + interface LegacyEventHandler extends BaseNode { + type: 'EventHandler'; + /** The 'x' in `on:x` */ + name: string; + /** The 'y' in `on:x={y}` */ + expression: null | Expression; + modifiers: string[]; + } + + interface LegacyHead extends BaseElement { + type: 'Head'; + } + + interface LegacyInlineComponent extends BaseElement { + type: 'InlineComponent'; + /** Set if this is a `` */ + expression?: Expression; + } + + interface LegacyMustacheTag extends BaseNode { + type: 'MustacheTag'; + expression: Expression; + } + + interface LegacyOptions { + type: 'Options'; + name: 'svelte:options'; + attributes: Array; + } + + interface LegacyPendingBlock extends BaseNode { + type: 'PendingBlock'; + children: LegacySvelteNode[]; + skip: boolean; + } + + interface LegacyRawMustacheTag extends BaseNode { + type: 'RawMustacheTag'; + expression: Expression; + } + + interface LegacySpread extends BaseNode { + type: 'Spread'; + expression: Expression; + } + + interface LegacySlot extends BaseElement { + type: 'Slot'; + } + + interface LegacySlotTemplate extends BaseElement { + type: 'SlotTemplate'; + } + + interface LegacyThenBlock extends BaseNode { + type: 'ThenBlock'; + children: LegacySvelteNode[]; + skip: boolean; + } + + interface LegacyTitle extends BaseElement { + type: 'Title'; + name: 'title'; + } + + interface LegacyConstTag extends BaseNode { + type: 'ConstTag'; + expression: AssignmentExpression; + } + + interface LegacyTransition extends BaseNode { + type: 'Transition'; + /** The 'x' in `transition:x` */ + name: string; + /** The 'y' in `transition:x={y}` */ + expression: null | Expression; + modifiers: Array<'local' | 'global'>; + /** True if this is a `transition:` or `in:` directive */ + intro: boolean; + /** True if this is a `transition:` or `out:` directive */ + outro: boolean; + } + + interface LegacyWindow extends BaseElement { + type: 'Window'; + } + + type LegacyDirective = + | LegacyAnimation + | LegacyBinding + | LegacyClass + | LegacyLet + | LegacyEventHandler + | StyleDirective + | LegacyTransition + | LegacyAction; + + type LegacyAttributeLike = LegacyAttribute | LegacySpread | LegacyDirective; + + type LegacyElementLike = + | LegacyBody + | LegacyCatchBlock + | LegacyDocument + | LegacyElement + | LegacyHead + | LegacyInlineComponent + | LegacyMustacheTag + | LegacyOptions + | LegacyPendingBlock + | LegacyRawMustacheTag + | LegacySlot + | LegacySlotTemplate + | LegacyThenBlock + | LegacyTitle + | LegacyWindow; + + type LegacySvelteNode = + | LegacyConstTag + | LegacyElementLike + | LegacyAttributeLike + | LegacyAttributeShorthand + | Text; + /** + * The preprocess function provides convenient hooks for arbitrarily transforming component source code. + * For example, it can be used to convert a