diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 7823c8e33e..e5b8ca7aa1 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -316,15 +316,52 @@ export interface EventDispatcher> { /** * Defines the options accepted by the `mount()` function. */ -export type MountOptions = Record> = { - /** - * Target element where the component will be mounted. - */ - target: Document | Element | ShadowRoot; - /** - * Optional node inside `target`. When specified, it is used to render the component immediately before it. - */ - anchor?: Node; +type MountRenderer = { + createFragment(): object; + createElement(name: string): object; + createTextNode(data: string): object; + createComment(data: string): object; +}; + +type MountRendererTarget = Renderer extends { + createFragment(): infer TFragment; + createElement(name: string): infer TElement; +} + ? TFragment | TElement + : never; + +type MountRendererAnchor = Renderer extends { + createElement(name: string): infer TElement; + createTextNode(data: string): infer TTextNode; + createComment(data: string): infer TComment; +} + ? TElement | TTextNode | TComment + : never; + +export type MountOptions< + Props extends Record = Record, + Renderer = undefined +> = (Renderer extends MountRenderer + ? { + /** Custom renderer to use instead of the DOM. */ + renderer: Renderer; + /** Target node where the component will be mounted. */ + target: MountRendererTarget; + /** Optional node inside `target`. When specified, it is used to render the component immediately before it. */ + anchor?: MountRendererAnchor; + } + : { + /** + * Target element where the component will be mounted. + */ + target: Document | Element | ShadowRoot; + /** + * Optional node inside `target`. When specified, it is used to render the component immediately before it. + */ + anchor?: Node; + /** Custom renderer to use instead of the DOM. */ + renderer?: undefined; + }) & { /** * Allows the specification of events. * @deprecated Use callback props instead. @@ -345,18 +382,18 @@ export type MountOptions = Record */ transformError?: (error: unknown) => unknown | Promise; } & ({} extends Props - ? { - /** - * Component properties. - */ - props?: Props; - } - : { - /** - * Component properties. - */ - props: Props; - }); + ? { + /** + * Component properties. + */ + props?: Props; + } + : { + /** + * Component properties. + */ + props: Props; + }); /** * Represents work that is happening off-screen, such as data being preloaded diff --git a/packages/svelte/src/internal/client/custom-renderer/index.js b/packages/svelte/src/internal/client/custom-renderer/index.js index 9d6fe9e840..c7ff363a67 100644 --- a/packages/svelte/src/internal/client/custom-renderer/index.js +++ b/packages/svelte/src/internal/client/custom-renderer/index.js @@ -1,11 +1,4 @@ -/** @import { ComponentContext } from '#client' */ /** @import { Renderer, RendererNodes, DefaultNodes } from "./types.js" */ -/** @import { Component, ComponentType, SvelteComponent } from '../../../index.js' */ -import { boundary } from '../dom/blocks/boundary.js'; -import { branch, effect_root } from '../reactivity/effects.js'; -import { push, pop, component_context } from '../context.js'; -import { push_renderer } from './state.js'; -import { get_parent_node, remove_child } from '../dom/operations.js'; /** * @template {RendererNodes} [T=DefaultNodes] @@ -13,49 +6,10 @@ import { get_parent_node, remove_child } from '../dom/operations.js'; * @template {object} [TElement=T extends DefaultNodes ? object : T['element']] * @template {object} [TTextNode=T extends DefaultNodes ? object : T['text']] * @template {object} [TComment=T extends DefaultNodes ? object : T['comment']] - * @param {Renderer} renderer - * @returns {Renderer & { render: , Exports extends Record>(component: ComponentType> | Component, options: {} extends Props ? { target: TFragment | TElement | TTextNode | TComment, props?: Props, context?: Map } : { target: TFragment | TElement | TTextNode | TComment, props: Props, context?: Map }) => { component: Exports, unmount: () => void } }} + * @template {Renderer} [R=Renderer] + * @param {R} renderer + * @returns {R} */ export function createRenderer(renderer) { - const compound_renderer = { - ...renderer, - /** - * @template {Record} Props - * @template {Record} Exports - * @param {ComponentType> | Component} Component - * @param {{} extends Props ? { target: TFragment | TElement | TTextNode | TComment, props?: Props, context?: Map } : { target: TFragment | TElement | TTextNode | TComment, props: Props, context?: Map }} options - */ - render(Component, { target, props, context }) { - var pop_renderer = push_renderer(compound_renderer); - - try { - /** @type {Exports} */ - // @ts-expect-error will be defined because the render effect runs synchronously - var component = undefined; - - const unmount = effect_root(() => { - var anchor = compound_renderer.createComment(''); - compound_renderer.insert(/** @type {*} */ (target), anchor, null); - boundary(/** @type {*} */ (anchor), { pending: () => {} }, (anchor) => { - push({}); - var ctx = /** @type {ComponentContext} */ (component_context); - if (context) ctx.c = context; - branch(() => { - component = /** @type {Function} */ (Component)(anchor, props ?? {}) || {}; - }); - pop(); - }); - - return () => { - var parent = get_parent_node(/** @type {*} */ (anchor)); - if (parent) remove_child(parent, /** @type {*} */ (anchor)); - }; - }); - return { component, unmount }; - } finally { - pop_renderer(); - } - } - }; - return compound_renderer; + return renderer; } diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 0278639951..b96a906fdd 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -1,5 +1,6 @@ /** @import { ComponentContext, Effect, EffectNodes, TemplateNode } from '#client' */ /** @import { Component, ComponentType, SvelteComponent, MountOptions } from '../../index.js' */ +/** @import { Renderer } from './custom-renderer/types.js' */ import { DEV } from 'esm-env'; import { clear_text_content, @@ -32,6 +33,7 @@ import { assign_nodes } from './dom/template.js'; import { is_passive_event } from '../../utils.js'; import { COMMENT_NODE, STATE_SYMBOL, TEXT_CACHE } from './constants.js'; import { boundary } from './dom/blocks/boundary.js'; +import { push_renderer } from './custom-renderer/state.js'; /** * This is normally true — block effects should run their intro transitions — @@ -66,8 +68,9 @@ export function set_text(text, value) { * * @template {Record} Props * @template {Record} Exports + * @template [Renderer=undefined] * @param {ComponentType> | Component} component - * @param {MountOptions} options + * @param {MountOptions} options * @returns {Exports} */ export function mount(component, options) { @@ -160,135 +163,150 @@ const listeners = new Map(); /** * @template {Record} Exports * @param {ComponentType> | Component} Component - * @param {MountOptions} options + * @param {MountOptions & { renderer?: Renderer }} options * @returns {Exports} */ function _mount( Component, - { target, anchor, props = {}, events, context, intro = true, transformError } + { target, anchor, props = {}, events, context, intro = true, transformError, renderer } ) { + var pop_renderer = renderer ? push_renderer(renderer) : null; + /** @type {Exports} */ // @ts-expect-error will be defined because the render effect runs synchronously var component = undefined; - var unmount = component_root(() => { - var anchor_node = anchor ?? /** @type {Text} */ (append_child(target, create_text())); - - boundary( - /** @type {TemplateNode} */ (anchor_node), - { - pending: () => {} - }, - (anchor_node) => { - push({}); - var ctx = /** @type {ComponentContext} */ (component_context); - if (context) ctx.c = context; - - if (events) { - // We can't spread the object or else we'd lose the state proxy stuff, if it is one - /** @type {any} */ (props).$$events = events; - } - - if (hydrating) { - assign_nodes(/** @type {TemplateNode} */ (anchor_node), null); - } - - should_intro = intro; - // @ts-expect-error the public typings are not what the actual function looks like - component = Component(anchor_node, props) || {}; - should_intro = true; - - if (hydrating) { - /** @type {Effect & { nodes: EffectNodes }} */ (active_effect).nodes.end = hydrate_node; - - if ( - hydrate_node === null || - node_type(hydrate_node) !== COMMENT_NODE || - get_node_value(hydrate_node) !== HYDRATION_END - ) { - w.hydration_mismatch(); - throw HYDRATION_ERROR; + try { + var unmount = component_root(() => { + var anchor_node = + anchor ?? /** @type {Text} */ (append_child(/** @type {Node} */ (target), create_text())); + + boundary( + /** @type {TemplateNode} */ (anchor_node), + { + pending: () => {} + }, + (anchor_node) => { + push({}); + var ctx = /** @type {ComponentContext} */ (component_context); + if (context) ctx.c = context; + + if (events) { + // We can't spread the object or else we'd lose the state proxy stuff, if it is one + /** @type {any} */ (props).$$events = events; } - } - pop(); - }, - transformError - ); - - // Setup event delegation _after_ component is mounted - if an error would happen during mount, it would otherwise not be cleaned up - /** @type {Set} */ - var registered_events = new Set(); - - /** @param {Array} events */ - var event_handle = (events) => { - for (var i = 0; i < events.length; i++) { - var event_name = events[i]; - - if (registered_events.has(event_name)) continue; - registered_events.add(event_name); - - var passive = is_passive_event(event_name); - - // Add the event listener to both the container and the document. - // The container listener ensures we catch events from within in case - // the outer content stops propagation of the event. - // - // The document listener ensures we catch events that originate from elements that were - // manually moved outside of the container (e.g. via manual portals). - for (const node of [target, document]) { - var counts = listeners.get(node); - - if (counts === undefined) { - counts = new Map(); - listeners.set(node, counts); + if (hydrating) { + assign_nodes(/** @type {TemplateNode} */ (anchor_node), null); } - var count = counts.get(event_name); - - if (count === undefined) { - add_event_listener(node, event_name, handle_event_propagation, { passive }); - counts.set(event_name, 1); - } else { - counts.set(event_name, count + 1); + should_intro = intro; + // @ts-expect-error the public typings are not what the actual function looks like + component = Component(anchor_node, props) || {}; + should_intro = true; + + if (hydrating) { + /** @type {Effect & { nodes: EffectNodes }} */ (active_effect).nodes.end = hydrate_node; + + if ( + hydrate_node === null || + node_type(hydrate_node) !== COMMENT_NODE || + get_node_value(hydrate_node) !== HYDRATION_END + ) { + w.hydration_mismatch(); + throw HYDRATION_ERROR; + } } - } - } - }; - event_handle(array_from(all_registered_events)); - root_event_handles.add(event_handle); - - return () => { - for (var event_name of registered_events) { - for (const node of [target, document]) { - var counts = /** @type {Map} */ (listeners.get(node)); - var count = /** @type {number} */ (counts.get(event_name)); + pop(); + }, + transformError + ); + + // Setup event delegation _after_ component is mounted - if an error would happen during mount, it would otherwise not be cleaned up + /** @type {Set} */ + var registered_events = new Set(); + /** @type {null | ((events: Array) => void)} */ + var event_handle = null; + + if (!renderer) { + var dom_target = /** @type {EventTarget} */ (target); + + /** @param {Array} events */ + event_handle = (events) => { + for (var i = 0; i < events.length; i++) { + var event_name = events[i]; + + if (registered_events.has(event_name)) continue; + registered_events.add(event_name); + + var passive = is_passive_event(event_name); + + // Add the event listener to both the container and the document. + // The container listener ensures we catch events from within in case + // the outer content stops propagation of the event. + // + // The document listener ensures we catch events that originate from elements that were + // manually moved outside of the container (e.g. via manual portals). + for (const node of [dom_target, document]) { + var counts = listeners.get(node); + + if (counts === undefined) { + counts = new Map(); + listeners.set(node, counts); + } + + var count = counts.get(event_name); + + if (count === undefined) { + add_event_listener(node, event_name, handle_event_propagation, { passive }); + counts.set(event_name, 1); + } else { + counts.set(event_name, count + 1); + } + } + } + }; - if (--count == 0) { - remove_event_listener(node, event_name, handle_event_propagation); - counts.delete(event_name); + event_handle(array_from(all_registered_events)); + root_event_handles.add(event_handle); + } - if (counts.size === 0) { - listeners.delete(node); + return () => { + if (event_handle !== null) { + for (var event_name of registered_events) { + for (const node of [/** @type {EventTarget} */ (target), document]) { + var counts = /** @type {Map} */ (listeners.get(node)); + var count = /** @type {number} */ (counts.get(event_name)); + + if (--count == 0) { + remove_event_listener(node, event_name, handle_event_propagation); + counts.delete(event_name); + + if (counts.size === 0) { + listeners.delete(node); + } + } else { + counts.set(event_name, count); + } } - } else { - counts.set(event_name, count); } - } - } - root_event_handles.delete(event_handle); + root_event_handles.delete(event_handle); + } - if (anchor_node !== anchor) { - var parent = get_parent_node(anchor_node); - if (parent) remove_child(parent, /** @type {ChildNode} */ (anchor_node)); - } - }; - }); + if (anchor_node !== anchor) { + var parent = get_parent_node(/** @type {Node} */ (anchor_node)); + if (parent) remove_child(parent, /** @type {ChildNode} */ (anchor_node)); + } + }; + }); - mounted_components.set(component, unmount); - return component; + mounted_components.set(component, unmount); + return component; + } finally { + pop_renderer?.(); + } } /** diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 801c126b2c..f4b3f0db3d 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -113,7 +113,7 @@ class Svelte4Component { } ); - this.#instance = (options.hydrate ? hydrate : mount)(options.component, { + const render_options = { target: options.target, anchor: options.anchor, props, @@ -121,7 +121,11 @@ class Svelte4Component { intro: options.intro ?? false, recover: options.recover, transformError: options.transformError - }); + }; + + this.#instance = options.hydrate + ? hydrate(options.component, render_options) + : mount(options.component, render_options); // We don't flushSync for custom element wrappers or if the user doesn't want it, // or if we're in async mode since `flushSync()` will fail diff --git a/packages/svelte/tests/custom-renderers/shared.ts b/packages/svelte/tests/custom-renderers/shared.ts index 1ad0f49ecf..d43682cc7c 100644 --- a/packages/svelte/tests/custom-renderers/shared.ts +++ b/packages/svelte/tests/custom-renderers/shared.ts @@ -6,6 +6,7 @@ import { compile_directory } from '../helpers.js'; import { suite_with_variants, type BaseTest } from '../suite.js'; import type { CompileOptions } from '#compiler'; import renderer, { create_root, serialize, dispatch_event, dom_elements } from './renderer.js'; +import { mount, unmount as unmount_component } from '../../src/index-client.js'; import { writeFile } from 'node:fs/promises'; import { globSync } from 'tinyglobby'; @@ -150,17 +151,15 @@ async function run_test(cwd: string, config: CustomRendererTest, compile_options const mod = await import(`${cwd}/_output/client/main.svelte.js`); const target = create_root(); - let unmount: (() => void) | undefined; let component: Record | undefined; try { dom_elements.length = 0; - const result = renderer.render(mod.default, { + component = mount(mod.default, { + renderer, target, props: config.props ?? {}, context: config.context }); - unmount = result.unmount; - component = result.component; } catch (err) { if (config.error) { assert.include((err as Error).message, config.error); @@ -212,7 +211,9 @@ async function run_test(cwd: string, config: CustomRendererTest, compile_options assert.fail('Expected a runtime error'); } } finally { - unmount?.(); + if (component) { + await unmount_component(component); + } if (config.warnings) { assert.deepEqual(warnings, config.warnings); diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index 6f30fb5d98..290f61957f 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -470,14 +470,16 @@ async function run_test_variant( }; } else { run_hydratables_init(); - const render = variant === 'hydrate' ? hydrate : mount; - instance = render(mod.default, { + const options = { target, props, intro: config.intro, recover: config.recover ?? false, transformError: config.transformError - }); + }; + + instance = + variant === 'hydrate' ? hydrate(mod.default, options) : mount(mod.default, options); } } else { run_hydratables_init(); diff --git a/packages/svelte/tests/types/component.ts b/packages/svelte/tests/types/component.ts index 06749e9937..edfb167243 100644 --- a/packages/svelte/tests/types/component.ts +++ b/packages/svelte/tests/types/component.ts @@ -9,6 +9,7 @@ import { type Component, type ComponentInternals } from 'svelte'; +import { createRenderer } from 'svelte/renderer'; import { render } from 'svelte/server'; SvelteComponent.element === HTMLElement; @@ -291,6 +292,52 @@ mount( // if component receives no args, props can be omitted mount(null as any as Component<{}>, { target: null as any }); +type CustomFragment = { kind: 'fragment' }; +type CustomElement = { kind: 'element' }; +type CustomText = { kind: 'text' }; +type CustomComment = { kind: 'comment' }; +const custom_renderer = createRenderer<{ + fragment: CustomFragment; + element: CustomElement; + text: CustomText; + comment: CustomComment; +}>(null as any); + +mount(functionComponent, { + renderer: custom_renderer, + target: null as any as CustomFragment, + anchor: null as any as CustomComment, + props: { + binding: true, + readonly: 'foo' + } +}); + +// @ts-expect-error target must match the renderer +mount(functionComponent, { + renderer: custom_renderer, + target: null as any as HTMLElement, + props: { + binding: true, + readonly: 'foo' + } +}); + +// @ts-expect-error anchor must match the renderer +mount(functionComponent, { + renderer: custom_renderer, + target: null as any as CustomElement, + anchor: null as any as CustomFragment, + props: { + binding: true, + readonly: 'foo' + } +}); + +// @ts-expect-error createRenderer returns the renderer directly +custom_renderer.render; +createRenderer({ ...custom_renderer, extra: true }).extra === true; + hydrate(functionComponent, { target: null as any as Document | Element | ShadowRoot, props: { diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index dd264d3239..e2bffab6a7 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -315,15 +315,52 @@ declare module 'svelte' { /** * Defines the options accepted by the `mount()` function. */ - export type MountOptions = Record> = { - /** - * Target element where the component will be mounted. - */ - target: Document | Element | ShadowRoot; - /** - * Optional node inside `target`. When specified, it is used to render the component immediately before it. - */ - anchor?: Node; + type MountRenderer = { + createFragment(): object; + createElement(name: string): object; + createTextNode(data: string): object; + createComment(data: string): object; + }; + + type MountRendererTarget = Renderer extends { + createFragment(): infer TFragment; + createElement(name: string): infer TElement; + } + ? TFragment | TElement + : never; + + type MountRendererAnchor = Renderer extends { + createElement(name: string): infer TElement; + createTextNode(data: string): infer TTextNode; + createComment(data: string): infer TComment; + } + ? TElement | TTextNode | TComment + : never; + + export type MountOptions< + Props extends Record = Record, + Renderer = undefined + > = (Renderer extends MountRenderer + ? { + /** Custom renderer to use instead of the DOM. */ + renderer: Renderer; + /** Target node where the component will be mounted. */ + target: MountRendererTarget; + /** Optional node inside `target`. When specified, it is used to render the component immediately before it. */ + anchor?: MountRendererAnchor; + } + : { + /** + * Target element where the component will be mounted. + */ + target: Document | Element | ShadowRoot; + /** + * Optional node inside `target`. When specified, it is used to render the component immediately before it. + */ + anchor?: Node; + /** Custom renderer to use instead of the DOM. */ + renderer?: undefined; + }) & { /** * Allows the specification of events. * @deprecated Use callback props instead. @@ -344,18 +381,18 @@ declare module 'svelte' { */ transformError?: (error: unknown) => unknown | Promise; } & ({} extends Props - ? { - /** - * Component properties. - */ - props?: Props; - } - : { - /** - * Component properties. - */ - props: Props; - }); + ? { + /** + * Component properties. + */ + props?: Props; + } + : { + /** + * Component properties. + */ + props: Props; + }); /** * Represents work that is happening off-screen, such as data being preloaded @@ -536,7 +573,7 @@ declare module 'svelte' { * Transitions will play during the initial render unless the `intro` option is set to `false`. * * */ - export function mount, Exports extends Record>(component: ComponentType> | Component, options: MountOptions): Exports; + export function mount, Exports extends Record, Renderer = undefined>(component: ComponentType> | Component, options: MountOptions): Exports; /** * Hydrates a component on the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component * @@ -2568,20 +2605,7 @@ declare module 'svelte/reactivity/window' { } declare module 'svelte/renderer' { - export function createRenderer = DefaultNodes, TFragment extends object = T extends DefaultNodes ? object : T["fragment"], TElement extends object = T extends DefaultNodes ? object : T["element"], TTextNode extends object = T extends DefaultNodes ? object : T["text"], TComment extends object = T extends DefaultNodes ? object : T["comment"]>(renderer: Renderer): Renderer & { - render: , Exports extends Record>(component: ComponentType> | Component, options: {} extends Props ? { - target: TFragment | TElement | TTextNode | TComment; - props?: Props; - context?: Map; - } : { - target: TFragment | TElement | TTextNode | TComment; - props: Props; - context?: Map; - }) => { - component: Exports; - unmount: () => void; - }; - }; + export function createRenderer = DefaultNodes, TFragment extends object = T extends DefaultNodes ? object : T["fragment"], TElement extends object = T extends DefaultNodes ? object : T["element"], TTextNode extends object = T extends DefaultNodes ? object : T["text"], TComment extends object = T extends DefaultNodes ? object : T["comment"], R extends Renderer = Renderer>(renderer: R): R; type Renderer< TFragment extends object = object, TElement extends object = object, @@ -2690,206 +2714,6 @@ declare module 'svelte/renderer' { // for the other arguments (TFragment, TElement, TTextNode, TComment) type UnsetObject = object & { readonly __unset: unique symbol }; type DefaultNodes = RendererNodes; - /** - * @deprecated In Svelte 4, components are classes. In Svelte 5, they are functions. - * Use `mount` instead to instantiate components. - * See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) - * for more info. - */ - interface ComponentConstructorOptions< - Props extends Record = Record - > { - target: Element | Document | ShadowRoot; - anchor?: Element; - props?: Props; - context?: Map; - hydrate?: boolean; - intro?: boolean; - recover?: boolean; - sync?: boolean; - idPrefix?: string; - $$inline?: boolean; - transformError?: (error: unknown) => unknown; - } - - /** - * Utility type for ensuring backwards compatibility on a type level that if there's a default slot, add 'children' to the props - */ - type Properties = Props & - (Slots extends { default: any } - ? // This is unfortunate because it means "accepts no props" turns into "accepts any prop" - // but the alternative is non-fixable type errors because of the way TypeScript index - // signatures work (they will always take precedence and make an impossible-to-satisfy children type). - Props extends Record - ? any - : { children?: any } - : {}); - - /** - * This was the base class for Svelte components in Svelte 4. Svelte 5+ components - * are completely different under the hood. For typing, use `Component` instead. - * To instantiate components, use `mount` instead. - * See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more info. - */ - class SvelteComponent< - Props extends Record = Record, - Events extends Record = any, - Slots extends Record = any - > { - /** The custom element version of the component. Only present if compiled with the `customElement` compiler option */ - static element?: typeof HTMLElement; - - [prop: string]: any; - /** - * @deprecated This constructor only exists when using the `asClassComponent` compatibility helper, which - * is a stop-gap solution. Migrate towards using `mount` instead. See - * [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#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: Props; // Without Properties: unnecessary, causes type bugs - /** - * 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; - /** - * For type checking capabilities only. - * Does not exist at runtime. - * ### DO NOT USE! - */ - $$bindings?: string; - - /** - * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#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 [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#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 [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) - * for more info. - */ - $set(props: Partial): void; - } - - const brand: unique symbol; - type Brand = { [brand]: B }; - type Branded = T & Brand; - - /** - * Internal implementation details that vary between environments - */ - type ComponentInternals = Branded<{}, 'ComponentInternals'>; - - /** - * 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 type { Component } from 'svelte'; - * export declare const MyComponent: Component<{ 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 - * - * - * ``` - */ - interface Component< - Props extends Record = {}, - Exports extends Record = {}, - Bindings extends keyof Props | '' = string - > { - /** - * @param internal An internal object used by Svelte. Do not use or modify. - * @param props The props passed to the component. - */ - ( - this: void, - internals: ComponentInternals, - props: Props - ): { - /** - * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) - * for more info. - */ - $on?(type: string, callback: (e: any) => void): () => void; - /** - * @deprecated This method only exists when using one of the legacy compatibility helpers, which - * is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) - * for more info. - */ - $set?(props: Partial): void; - } & Exports; - /** The custom element version of the component. Only present if compiled with the `customElement` compiler option */ - element?: typeof HTMLElement; - /** Does not exist at runtime, for typing capabilities only. DO NOT USE */ - z_$$bindings?: Bindings; - } - - /** - * @deprecated This type is obsolete when working with the new `Component` type. - * - * @description - * Convenience type to get the type of a Svelte component. Useful for example in combination with - * dynamic components using ``. - * - * Example: - * ```html - * - * - * - * - * ``` - */ - 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; - }; export {}; }