From 47da31e84f45c08dc8dd630db5404c558f0abf18 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 11 May 2023 10:54:30 +0200 Subject: [PATCH] fixes, public/private.dts, d.ts generation --- generate-types.mjs | 117 +++++++++++++++++ package.json | 2 +- rollup.config.mjs | 6 +- src/compiler/tsconfig.json | 4 +- .../action/{index.d.ts => public.d.ts} | 28 ++-- src/runtime/animate/index.js | 4 +- .../animate/{index.d.ts => public.d.ts} | 0 src/runtime/index.js | 1 - src/runtime/internal/Component.js | 4 +- .../internal/ResizeObserverSingleton.js | 28 ++-- src/runtime/internal/animations.js | 6 +- src/runtime/internal/await_block.js | 4 +- src/runtime/internal/dev.js | 13 +- src/runtime/internal/globals.js | 7 +- src/runtime/internal/lifecycle.js | 13 +- src/runtime/internal/loop.js | 6 +- src/runtime/internal/private.d.ts | 50 ++++++- src/runtime/internal/public.d.ts | 47 +------ src/runtime/internal/transitions.js | 8 +- src/runtime/internal/utils.js | 2 +- src/runtime/motion/private.d.ts | 28 ++++ src/runtime/motion/public.d.ts | 15 +++ src/runtime/motion/spring.js | 52 ++------ src/runtime/motion/tweened.js | 25 +--- src/runtime/store/index.js | 122 ++++++++---------- src/runtime/store/public.d.ts | 61 +++++++++ src/runtime/transition/index.js | 116 ++++------------- src/runtime/transition/public.d.ts | 60 +++++++++ tsconfig.json | 3 + 29 files changed, 497 insertions(+), 335 deletions(-) create mode 100644 generate-types.mjs rename src/runtime/action/{index.d.ts => public.d.ts} (67%) rename src/runtime/animate/{index.d.ts => public.d.ts} (100%) create mode 100644 src/runtime/motion/private.d.ts create mode 100644 src/runtime/motion/public.d.ts create mode 100644 src/runtime/store/public.d.ts create mode 100644 src/runtime/transition/public.d.ts diff --git a/generate-types.mjs b/generate-types.mjs new file mode 100644 index 0000000000..a06de292b9 --- /dev/null +++ b/generate-types.mjs @@ -0,0 +1,117 @@ +// This script generates the TypeScript definitions + +import { execSync } from 'child_process'; +import { readFileSync, writeFileSync, readdirSync, existsSync, copyFileSync } from 'fs'; + +execSync('tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly', { stdio: 'inherit' }); + +function modify(path, modifyFn) { + const content = readFileSync(path, 'utf8'); + writeFileSync(path, modifyFn(content)); +} + +function adjust(input) { + // Remove typedef jsdoc (duplicated in the type definition) + input = input.replace(/\/\*\*\n(\r)? \* @typedef .+?\*\//gs, ''); + input = input.replace(/\/\*\* @typedef .+?\*\//gs, ''); + + // Extract the import paths and types + const import_regex = /import\(("|')(.+?)("|')\)\.(\w+)/g; + let import_match; + const import_map = new Map(); + + while ((import_match = import_regex.exec(input)) !== null) { + const imports = import_map.get(import_match[2]) || new Set(); + imports.add(import_match[4]); + import_map.set(import_match[2], imports); + } + + // Replace inline imports with their type names + const transformed = input.replace(import_regex, "$4"); + + // Remove/adjust @template, @param and @returns lines + // TODO rethink if we really need to do this for @param and @returns, doesn't show up in hover so unnecessary + const lines = transformed.split("\n"); + + let filtered_lines = []; + let removing = null; + let openCount = 1; + let closedCount = 0; + + for (let line of lines) { + let start_removing = false; + if (line.trim().startsWith("* @template")) { + removing = "template"; + start_removing = true; + } + + if (line.trim().startsWith("* @param {")) { + openCount = 1; + closedCount = 0; + removing = "param"; + start_removing = true; + } + + if (line.trim().startsWith("* @returns {")) { + openCount = 1; + closedCount = 0; + removing = "returns"; + start_removing = true; + } + + if (removing === "returns" || removing === "param") { + let i = start_removing ? line.indexOf('{') + 1 : 0; + for (; i < line.length; i++) { + if (line[i] === "{") openCount++; + if (line[i] === "}") closedCount++; + if (openCount === closedCount) break; + } + if (openCount === closedCount) { + line = start_removing ? (line.slice(0, line.indexOf('{')) + line.slice(i + 1)) : (` * @${removing} ` + line.slice(i + 1)); + removing = null; + } + } + + if (removing && !start_removing && (line.trim().startsWith("* @") || line.trim().startsWith("*/"))) { + removing = null; + } + + if (!removing) { + filtered_lines.push(line); + } + } + + // Replace generic type names with their plain versions + const renamed_generics = filtered_lines.map(line => { + return line.replace(/(\W|\s)([A-Z][\w\d$]*)_\d+(\W|\s)/g, "$1$2$3"); + }); + + // Generate the import statement for the types used + const import_statements = Array.from(import_map.entries()) + .map(([path, types]) => `import { ${[...types].join(', ')} } from '${path}';`) + .join("\n"); + + return [import_statements, ...renamed_generics].join("\n"); +} + +for (const dir of readdirSync('types/runtime')) { + if (dir.endsWith('.d.ts')) continue; + + modify( + `types/runtime/${dir}/index.d.ts`, + content => { + // TODO adjust all d.ts files + content = adjust(content); + + if (existsSync(`src/runtime/${dir}/public.d.ts`)) { + copyFileSync(`src/runtime/${dir}/public.d.ts`, `types/runtime/${dir}/public.d.ts`); + content + "\nexport * from './public.js'"; + } + + return content; + } + ); +} + +copyFileSync(`src/runtime/ambient.d.ts`, `types/runtime/ambient.d.ts`); +modify(`types/runtime/index.d.ts`, content => content + "\nimport './ambient.js'"); diff --git a/package.json b/package.json index 9c2ada6782..a10a52feab 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "dev": "rollup -cw", "posttest": "agadoo internal/index.mjs", "prepublishOnly": "node check_publish_env.js && npm run lint && npm run build && npm test", - "tsd": "tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly", + "tsd": "node ./generate-types.mjs", "lint": "eslint \"{src,test}/**/*.{ts,js}\" --cache" }, "repository": { diff --git a/rollup.config.mjs b/rollup.config.mjs index 54b988b51b..eacdc7d13e 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -32,7 +32,7 @@ const runtime_entrypoints = Object.fromEntries( fs .readdirSync('src/runtime', { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) - .map((dirent) => [dirent.name, `src/runtime/${dirent.name}/index.ts`]) + .map((dirent) => [dirent.name, `src/runtime/${dirent.name}/index.js`]) ); /** @@ -42,8 +42,8 @@ export default [ { input: { ...runtime_entrypoints, - index: 'src/runtime/index.ts', - ssr: 'src/runtime/ssr.ts' + index: 'src/runtime/index.js', + ssr: 'src/runtime/ssr.js' }, output: ['es', 'cjs'].map( /** @returns {import('rollup').OutputOptions} */ diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index c5939a0fdc..c7395a0c8d 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -3,7 +3,9 @@ "include": ["."], "compilerOptions": { - "lib": ["es2017", "webworker"] + "lib": ["es2017", "webworker"], + "allowJs": true, + "checkJs": true // TODO: remove mocha types from the whole project // "types": ["node", "estree"] diff --git a/src/runtime/action/index.d.ts b/src/runtime/action/public.d.ts similarity index 67% rename from src/runtime/action/index.d.ts rename to src/runtime/action/public.d.ts index b36a8d82ce..0abde77f80 100644 --- a/src/runtime/action/index.d.ts +++ b/src/runtime/action/public.d.ts @@ -1,8 +1,7 @@ /** * 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, which makes it illegal to set the `update` method. + * immediately after Svelte has applied updates to the markup. * - 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. @@ -27,10 +26,10 @@ * Docs: https://svelte.dev/docs#template-syntax-element-directives-use-action */ export interface ActionReturn< - Parameter = never, + Parameter = any, Attributes extends Record = Record > { - update?: [Parameter] extends [never] ? never : (parameter: Parameter) => void; + update?: (parameter: Parameter) => void; destroy?: () => void; /** * ### DO NOT USE THIS @@ -46,12 +45,10 @@ export interface ActionReturn< * 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 }) => { + * 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. * @@ -59,18 +56,11 @@ export interface ActionReturn< */ export interface Action< Element = HTMLElement, - Parameter = never, + Parameter = any, Attributes extends Record = Record > { - ( - ...args: [Parameter] extends [never] - ? [node: Node] - : undefined extends Parameter - ? [node: Node, parameter?: Parameter] - : [node: Node, parameter: Parameter] - ): void | ActionReturn; + (node: Node, parameter?: Parameter): void | ActionReturn< + Parameter, + Attributes + >; } - -// Implementation notes: -// - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode -// - [X] extends [never] is needed, X extends never would reduce the whole resulting type to never and not to one of the condition outcomes diff --git a/src/runtime/animate/index.js b/src/runtime/animate/index.js index 2637626cdd..7e301009cb 100644 --- a/src/runtime/animate/index.js +++ b/src/runtime/animate/index.js @@ -4,8 +4,8 @@ import { is_function } from '../internal/index.js'; /** * @param {Element} node * @param {{ from: DOMRect; to: DOMRect }} fromTo - * @param {import('.').FlipParams} params - * @returns {import('.').AnimationConfig} + * @param {import('./public.js').FlipParams} params + * @returns {import('./public.js').AnimationConfig} */ export function flip(node, { from, to }, params = {}) { const style = getComputedStyle(node); diff --git a/src/runtime/animate/index.d.ts b/src/runtime/animate/public.d.ts similarity index 100% rename from src/runtime/animate/index.d.ts rename to src/runtime/animate/public.d.ts diff --git a/src/runtime/index.js b/src/runtime/index.js index 9900f7018d..9d17708fe4 100644 --- a/src/runtime/index.js +++ b/src/runtime/index.js @@ -1,4 +1,3 @@ -import './ambient.js'; export { onMount, onDestroy, diff --git a/src/runtime/internal/Component.js b/src/runtime/internal/Component.js index 8769286875..ad39801d35 100644 --- a/src/runtime/internal/Component.js +++ b/src/runtime/internal/Component.js @@ -95,7 +95,7 @@ export function init( ) { const parent_component = current_component; set_current_component(component); - /** @type {import('./public.d.ts').T$$} */ + /** @type {import('./private.js').T$$} */ const $$ = (component.$$ = { fragment: null, ctx: [], @@ -352,7 +352,7 @@ function get_custom_element_value(prop, value, props_definition, transform) { * @internal * * Turn a Svelte component into a custom element. - * @param {import('./public.d.ts').ComponentType} Component A Svelte component constructor + * @param {import('./public.js').ComponentType} Component A Svelte component constructor * @param {Record} props_definition The props to observe * @param {string[]} slots The slots to create * @param {string[]} accessors Other accessors besides the ones for props the component has diff --git a/src/runtime/internal/ResizeObserverSingleton.js b/src/runtime/internal/ResizeObserverSingleton.js index 707356a40e..a9e895ea88 100644 --- a/src/runtime/internal/ResizeObserverSingleton.js +++ b/src/runtime/internal/ResizeObserverSingleton.js @@ -6,14 +6,30 @@ import { globals } from './globals.js'; * https://groups.google.com/a/chromium.org/g/blink-dev/c/z6ienONUb5A/m/F5-VcUZtBAAJ */ export class ResizeObserverSingleton { + /** + * @private + * @readonly + * @type {WeakMap} + */ + _listeners = 'WeakMap' in globals ? new WeakMap() : undefined; + + /** + * @private + * @type {ResizeObserver} + */ + _observer = undefined; + + /** @type {ResizeObserverOptions} */ options; + + /** @param {ResizeObserverOptions} options */ constructor(options) { this.options = options; } /** * @param {Element} element - * @param {import('./private.d.ts').Listener} listener + * @param {import('./private.js').Listener} listener * @returns {() => void} */ observe(element, listener) { @@ -27,16 +43,6 @@ export class ResizeObserverSingleton { /** * @private - * @readonly - * @default 'WeakMap' in globals ? new WeakMap() : undefined - */ - _listeners = 'WeakMap' in globals ? new WeakMap() : undefined; - - /** @private */ - _observer = undefined; - - /** @private - * @returns {ResizeObserver} */ _getObserver() { return ( diff --git a/src/runtime/internal/animations.js b/src/runtime/internal/animations.js index 87b234fae1..a589413841 100644 --- a/src/runtime/internal/animations.js +++ b/src/runtime/internal/animations.js @@ -5,8 +5,8 @@ import { create_rule, delete_rule } from './style_manager.js'; /** * @param {Element & ElementCSSInlineStyle} node - * @param {import('./private.d.ts').PositionRect} from - * @param {import('./private.d.ts').AnimationFn} fn + * @param {import('./private.js').PositionRect} from + * @param {import('./private.js').AnimationFn} fn * @returns {any} */ export function create_animation(node, from, fn, params) { @@ -88,7 +88,7 @@ export function fix_position(node) { /** * @param {Element & ElementCSSInlineStyle} node - * @param {import('./private.d.ts').PositionRect} a + * @param {import('./private.js').PositionRect} a * @returns {void} */ export function add_transform(node, a) { diff --git a/src/runtime/internal/await_block.js b/src/runtime/internal/await_block.js index 5a9287b197..fed0f10470 100644 --- a/src/runtime/internal/await_block.js +++ b/src/runtime/internal/await_block.js @@ -6,13 +6,13 @@ import { get_current_component, set_current_component } from './lifecycle.js'; /** * @template T * @param {Promise} promise - * @param {import('./private.d.ts').PromiseInfo} info + * @param {import('./private.js').PromiseInfo} info * @returns {boolean} */ export function handle_promise(promise, info) { const token = (info.token = {}); /** - * @param {import('./public.d.ts').FragmentFactory} type + * @param {import('./private.js').FragmentFactory} type * @param {0 | 1 | 2} index * @param {number} [key] * @param {any} [value] diff --git a/src/runtime/internal/dev.js b/src/runtime/internal/dev.js index 1d9dbc2ef6..135e0988ab 100644 --- a/src/runtime/internal/dev.js +++ b/src/runtime/internal/dev.js @@ -294,9 +294,9 @@ export function construct_svelte_component_dev(component, props) { * * * ``` - * @template {Record} Props - * @template {Record} Events - * @template {Record} Slots + * @template {Record} [Props=any] + * @template {Record} [Events=any] + * @template {Record} [Slots=any] * @extends SvelteComponent */ export class SvelteComponentDev extends SvelteComponent { @@ -325,6 +325,7 @@ export class SvelteComponentDev extends SvelteComponent { /** @type {Slots} */ $$slot_def = undefined; + /** @param {import('./public.js').ComponentConstructorOptions} options */ constructor(options) { if (!options || (!options.target && !options.$$inline)) { throw new Error("'target' is a required option"); @@ -347,9 +348,9 @@ export class SvelteComponentDev extends SvelteComponent { $inject_state() {} } /** - * @template {Record} Props - * @template {Record} Events - * @template {Record} Slots + * @template {Record} [Props=any] + * @template {Record} [Events=any] + * @template {Record} [Slots=any] * @deprecated Use `SvelteComponent` instead. See PR for more information: https://github.com/sveltejs/svelte/pull/8512 * @extends SvelteComponentDev */ diff --git a/src/runtime/internal/globals.js b/src/runtime/internal/globals.js index 15cf9ae259..bd21be49ed 100644 --- a/src/runtime/internal/globals.js +++ b/src/runtime/internal/globals.js @@ -1,3 +1,8 @@ /** @type {typeof globalThis} */ export const globals = - typeof window !== 'undefined' ? window : typeof globalThis !== 'undefined' ? globalThis : global; + typeof window !== 'undefined' + ? window + : typeof globalThis !== 'undefined' + ? globalThis + : // @ts-ignore Node typings have this + global; diff --git a/src/runtime/internal/lifecycle.js b/src/runtime/internal/lifecycle.js index c054a6098c..68c02eced9 100644 --- a/src/runtime/internal/lifecycle.js +++ b/src/runtime/internal/lifecycle.js @@ -92,7 +92,7 @@ export function onDestroy(fn) { * * https://svelte.dev/docs#run-time-svelte-createeventdispatcher * @template {Record} EventMap - * @returns {import('./public.d.ts').EventDispatcher} + * @returns {import('./public.js').EventDispatcher} */ export function createEventDispatcher() { const component = get_current_component(); @@ -120,6 +120,7 @@ export function createEventDispatcher() { * * https://svelte.dev/docs#run-time-svelte-setcontext * @template T + * @param {string} key * @param {T} context * @returns {T} */ @@ -134,6 +135,7 @@ export function setContext(key, context) { * * https://svelte.dev/docs#run-time-svelte-getcontext * @template T + * @param {string} key * @returns {T} */ export function getContext(key) { @@ -146,7 +148,7 @@ export function getContext(key) { * programmatically create a component and want to pass the existing context to it. * * https://svelte.dev/docs#run-time-svelte-getallcontexts - * @template T + * @template {Map} [T=Map] * @returns {T} */ export function getAllContexts() { @@ -158,6 +160,7 @@ export function getAllContexts() { * Must be called during component initialisation. * * https://svelte.dev/docs#run-time-svelte-hascontext + * @param {string} key * @returns {boolean} */ export function hasContext(key) { @@ -167,7 +170,11 @@ export function hasContext(key) { // TODO figure out if we still want to support // shorthand events, or if we want to implement // a real bubbling mechanism -/** @returns {void} */ +/** + * @param component + * @param event + * @returns {void} + */ export function bubble(component, event) { const callbacks = component.$$.callbacks[event.type]; if (callbacks) { diff --git a/src/runtime/internal/loop.js b/src/runtime/internal/loop.js index c5bde437d4..5a14565600 100644 --- a/src/runtime/internal/loop.js +++ b/src/runtime/internal/loop.js @@ -27,11 +27,11 @@ export function clear_loops() { /** * Creates a new task that runs on each raf frame * until it returns a falsy value or is aborted - * @param {import('./private.d.ts').TaskCallback} callback - * @returns {import('./public.d.ts').Task} + * @param {import('./private.js').TaskCallback} callback + * @returns {import('./private.js').Task} */ export function loop(callback) { - /** @type {import('./private.d.ts').TaskEntry} */ + /** @type {import('./private.js').TaskEntry} */ let task; if (tasks.size === 0) raf(run_tasks); return { diff --git a/src/runtime/internal/private.d.ts b/src/runtime/internal/private.d.ts index 2ae88b3cc4..50f4a9046b 100644 --- a/src/runtime/internal/private.d.ts +++ b/src/runtime/internal/private.d.ts @@ -1,5 +1,4 @@ -import type { AnimationConfig } from '../animate'; -import type { Fragment, FragmentFactory } from './public'; +import type { AnimationConfig } from '../animate/public.js'; export type AnimationFn = ( node: Element, @@ -7,7 +6,7 @@ export type AnimationFn = ( params: any ) => AnimationConfig; -type Listener = (entry: ResizeObserverEntry) => any; +export type Listener = (entry: ResizeObserverEntry) => any; //todo: documentation says it is DOMRect, but in IE it would be ClientRect export type PositionRect = DOMRect | ClientRect; @@ -79,3 +78,48 @@ export interface StyleInformation { export type TaskCallback = (now: number) => boolean | void; export type TaskEntry = { c: TaskCallback; f: () => void }; + +/** + * INTERNAL, DO NOT USE. Code may change at any time. + */ +export interface Fragment { + key: string | null; + first: null; + /* create */ c: () => void; + /* claim */ l: (nodes: any) => void; + /* hydrate */ h: () => void; + /* mount */ m: (target: HTMLElement, anchor: any) => void; + /* update */ p: (ctx: T$$['ctx'], dirty: T$$['dirty']) => void; + /* measure */ r: () => void; + /* fix */ f: () => void; + /* animate */ a: () => void; + /* intro */ i: (local: any) => void; + /* outro */ o: (local: any) => void; + /* destroy */ d: (detaching: 0 | 1) => void; +} + +export type FragmentFactory = (ctx: any) => Fragment; + +export interface T$$ { + dirty: number[]; + ctx: any[]; + bound: any; + update: () => void; + callbacks: any; + after_update: any[]; + props: Record; + fragment: null | false | Fragment; + not_equal: any; + before_update: any[]; + context: Map; + on_mount: any[]; + on_destroy: any[]; + skip_bound: boolean; + on_disconnect: any[]; + root: Element | ShadowRoot; +} + +export interface Task { + abort(): void; + promise: Promise; +} diff --git a/src/runtime/internal/public.d.ts b/src/runtime/internal/public.d.ts index de7d87bb54..62a3d8edc8 100644 --- a/src/runtime/internal/public.d.ts +++ b/src/runtime/internal/public.d.ts @@ -1,4 +1,4 @@ -import type { SvelteComponent } from './Component'; +import type { SvelteComponent } from './Component.js'; export interface ComponentConstructorOptions< Props extends Record = Record @@ -91,27 +91,6 @@ export interface EventDispatcher> { ): boolean; } -/** - * INTERNAL, DO NOT USE. Code may change at any time. - */ -export interface Fragment { - key: string | null; - first: null; - /* create */ c: () => void; - /* claim */ l: (nodes: any) => void; - /* hydrate */ h: () => void; - /* mount */ m: (target: HTMLElement, anchor: any) => void; - /* update */ p: (ctx: T$$['ctx'], dirty: T$$['dirty']) => void; - /* measure */ r: () => void; - /* fix */ f: () => void; - /* animate */ a: () => void; - /* intro */ i: (local: any) => void; - /* outro */ o: (local: any) => void; - /* destroy */ d: (detaching: 0 | 1) => void; -} - -export type FragmentFactory = (ctx: any) => Fragment; - export interface SvelteComponentDev< Props extends Record = any, Events extends Record = any, @@ -126,33 +105,9 @@ export interface SvelteComponentDev< [accessor: string]: any; } -export interface T$$ { - dirty: number[]; - ctx: any[]; - bound: any; - update: () => void; - callbacks: any; - after_update: any[]; - props: Record; - fragment: null | false | Fragment; - not_equal: any; - before_update: any[]; - context: Map; - on_mount: any[]; - on_destroy: any[]; - skip_bound: boolean; - on_disconnect: any[]; - root: Element | ShadowRoot; -} - // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SvelteComponentTyped< Props extends Record = any, Events extends Record = any, Slots extends Record = any > extends SvelteComponentDev {} - -export interface Task { - abort(): void; - promise: Promise; -} diff --git a/src/runtime/internal/transitions.js b/src/runtime/internal/transitions.js index fb15ce7fca..9da6270be2 100644 --- a/src/runtime/internal/transitions.js +++ b/src/runtime/internal/transitions.js @@ -60,7 +60,7 @@ export function check_outros() { } /** - * @param {import('./public.js').Fragment} block + * @param {import('./private.js').Fragment} block * @param {0 | 1} [local] * @returns {void} */ @@ -72,7 +72,7 @@ export function transition_in(block, local) { } /** - * @param {import('./public.js').Fragment} block + * @param {import('./private.js').Fragment} block * @param {0 | 1} local * @param {0 | 1} detach * @returns {void} @@ -95,7 +95,7 @@ export function transition_out(block, local, detach, callback) { } /** - * @type {import('types/transition').TransitionConfig} + * @type {import('../transition/public.js').TransitionConfig} */ const null_transition = { duration: 0 }; @@ -401,7 +401,7 @@ export function create_bidirectional_transition(node, fn, params, intro) { /** @typedef {1} INTRO */ /** @typedef {0} OUTRO */ /** @typedef {{ direction: 'in' | 'out' | 'both' }} TransitionOptions */ -/** @typedef {(node: Element, params: any, options: TransitionOptions) => import('types/transition').TransitionConfig} TransitionFn */ +/** @typedef {(node: Element, params: any, options: TransitionOptions) => import('../transition/public.js').TransitionConfig} TransitionFn */ /** * @typedef {Object} Outro diff --git a/src/runtime/internal/utils.js b/src/runtime/internal/utils.js index c5851738d2..524b3218a2 100644 --- a/src/runtime/internal/utils.js +++ b/src/runtime/internal/utils.js @@ -112,7 +112,7 @@ export function subscribe(store, ...callbacks) { /** * @template T - * @param {import('../store').Readable} store + * @param {import('../store/public.js').Readable} store * @returns {T} */ export function get_store_value(store) { diff --git a/src/runtime/motion/private.d.ts b/src/runtime/motion/private.d.ts new file mode 100644 index 0000000000..7a39983d48 --- /dev/null +++ b/src/runtime/motion/private.d.ts @@ -0,0 +1,28 @@ +import { Spring } from './public'; + +export interface TickContext { + inv_mass: number; + dt: number; + opts: Spring; + settled: boolean; +} + +export interface SpringOpts { + stiffness?: number; + damping?: number; + precision?: number; +} + +export interface SpringUpdateOpts { + hard?: any; + soft?: string | number | boolean; +} + +export type Updater = (target_value: T, value: T) => T; + +export interface TweenedOptions { + delay?: number; + duration?: number | ((from: T, to: T) => number); + easing?: (t: number) => number; + interpolate?: (a: T, b: T) => (t: number) => T; +} diff --git a/src/runtime/motion/public.d.ts b/src/runtime/motion/public.d.ts new file mode 100644 index 0000000000..372d31636f --- /dev/null +++ b/src/runtime/motion/public.d.ts @@ -0,0 +1,15 @@ +import { Readable } from '../store/public.js'; +import { SpringUpdateOpts, TweenedOptions, Updater } from './private'; + +export interface Spring extends Readable { + set: (new_value: T, opts?: SpringUpdateOpts) => Promise; + update: (fn: Updater, opts?: SpringUpdateOpts) => Promise; + precision: number; + damping: number; + stiffness: number; +} + +export interface Tweened extends Readable { + set(value: T, opts?: TweenedOptions): Promise; + update(updater: Updater, opts?: TweenedOptions): Promise; +} diff --git a/src/runtime/motion/spring.js b/src/runtime/motion/spring.js index f78078c9e5..3c6fb6b06a 100644 --- a/src/runtime/motion/spring.js +++ b/src/runtime/motion/spring.js @@ -3,7 +3,8 @@ import { loop, now } from '../internal/index.js'; import { is_date } from './utils.js'; /** - * @param {TickContext} ctx + * @template T + * @param {import('./private.js').TickContext} ctx * @param {T} last_value * @param {T} current_value * @param {T} target_value @@ -45,16 +46,17 @@ function tick_spring(ctx, last_value, current_value, target_value) { } /** + * @template T * @param {T} value - * @param {SpringOpts} opts - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/spring.ts-to-jsdoc").Spring} + * @param {import('./private.js').SpringOpts} opts + * @returns {import('./public.js').Spring} */ export function spring(value, opts = {}) { const store = writable(value); const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts; /** @type {number} */ let last_time; - /** @type {Task} */ + /** @type {import('../internal/private.js').Task} */ let task; /** @type {object} */ let current_token; @@ -65,8 +67,9 @@ export function spring(value, opts = {}) { let inv_mass = 1; let inv_mass_recovery_rate = 0; let cancel_task = false; - /** @param {T} new_value - * @param {SpringUpdateOpts} opts + /** + * @param {T} new_value + * @param {import('./private.js').SpringUpdateOpts} opts * @returns {Promise} */ function set(new_value, opts = {}) { @@ -115,7 +118,7 @@ export function spring(value, opts = {}) { }); }); } - /** @type {Spring} */ + /** @type {import('./public.js').Spring} */ const spring = { set, update: (fn, opts) => set(fn(target_value, value), opts), @@ -126,38 +129,3 @@ export function spring(value, opts = {}) { }; return spring; } - -/** - * @typedef {(target_value: T, value: T) => T} Updater - * @template T - */ - -/** - * @typedef {Object} TickContext - * @property {number} inv_mass - * @property {number} dt - * @property {Spring} opts - * @property {boolean} settled - */ - -/** - * @typedef {Object} SpringOpts - * @property {number} [stiffness] - * @property {number} [damping] - * @property {number} [precision] - */ - -/** - * @typedef {Object} SpringUpdateOpts - * @property {any} [hard] - * @property {string|number|boolean} [soft] - */ - -/** - * @typedef {Object} Spring - * @property {(new_value:T,opts?:SpringUpdateOpts)=>Promise} set - * @property {(fn:Updater,opts?:SpringUpdateOpts)=>Promise} update - * @property {number} precision - * @property {number} damping - * @property {number} stiffness - */ diff --git a/src/runtime/motion/tweened.js b/src/runtime/motion/tweened.js index 454d4f40e5..30ecdddb07 100644 --- a/src/runtime/motion/tweened.js +++ b/src/runtime/motion/tweened.js @@ -45,18 +45,19 @@ function get_interpolator(a, b) { } /** + * @template T * @param {T} value - * @param {Options} defaults - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/tweened.ts-to-jsdoc").Tweened} + * @param {import('./private.js').TweenedOptions} defaults + * @returns {import('./public.js').Tweened} */ export function tweened(value, defaults = {}) { const store = writable(value); - /** @type {Task} */ + /** @type {import('../internal/private.js').Task} */ let task; let target_value = value; /** * @param {T} new_value - * @param {Options} opts + * @param {import('./private.js').TweenedOptions} opts * @returns {any} */ function set(new_value, opts) { @@ -95,7 +96,7 @@ export function tweened(value, defaults = {}) { previous_task = null; } const elapsed = now - start; - if (elapsed > duration) { + if (elapsed > /** @type {number} */ (duration)) { store.set((value = new_value)); return false; } @@ -111,17 +112,3 @@ export function tweened(value, defaults = {}) { subscribe: store.subscribe }; } - -/** - * @typedef {(target_value: T, value: T) => T} Updater - * @template T - */ - -/** - * @typedef {Object} Options - * @property {number} [delay] - * @property {number|((from:T,to:T)=>number)} [duration] - * @property {(t:number)=>number} [easing] - * @property {(a:T,b:T)=>(t:number)=>T} [interpolate] - */ -/** @typedef {Object} Tweened */ diff --git a/src/runtime/store/index.js b/src/runtime/store/index.js index ec8ec9eedc..158f0a6f22 100644 --- a/src/runtime/store/index.js +++ b/src/runtime/store/index.js @@ -11,9 +11,10 @@ const subscriber_queue = []; /** * Creates a `Readable` store that allows reading by subscription. - * @param {T} value initial value - * @param {StartStopNotifier} start undefined - * @returns {Readable} + * @template T + * @param {T} value initial value + * @param {import('./public.js').StartStopNotifier} start + * @returns {import('./public.js').Readable} */ export function readable(value, start) { return { @@ -23,14 +24,15 @@ export function readable(value, start) { /** * Create a `Writable` store that allows both updating and reading by subscription. - * @param {T} value initial value - * @param {StartStopNotifier} start undefined - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").Writable} + * @template T + * @param {T} value initial value + * @param {import('./public.js').StartStopNotifier} start + * @returns {import('./public.js').Writable} */ export function writable(value, start = noop) { - /** @type {Unsubscriber} */ + /** @type {import('./public.js').Unsubscriber} */ let stop; - /** @type {Set>} */ + /** @type {Set>} */ const subscribers = new Set(); /** @param {T} new_value * @returns {void} @@ -55,19 +57,19 @@ export function writable(value, start = noop) { } } /** - * @param {Updater} fn + * @param {import('./public.js').Updater} fn * @returns {void} */ function update(fn) { set(fn(value)); } /** - * @param {Subscriber} run - * @param {Invalidator} invalidate - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").Unsubscriber} + * @param {import('./public.js').Subscriber} run + * @param {import('./public.js').Invalidator} invalidate + * @returns {import('./public.js').Unsubscriber} */ function subscribe(run, invalidate = noop) { - /** @type {SubscribeInvalidateTuple} */ + /** @type {import('./public.js').SubscribeInvalidateTuple} */ const subscriber = [run, invalidate]; subscribers.add(subscriber); if (subscribers.size === 1) { @@ -86,14 +88,42 @@ export function writable(value, start = noop) { } /** - * @param {Stores} stores + * Derived value store by synchronizing one or more readable stores and + * applying an aggregation function over its input values. + * + * @template {import('./public.js').Stores} S + * @template T + * @overload + * @param {S} stores - input stores + * @param {(values: import('./public.js').StoresValues, set: import('./public.js').Subscriber, update: (fn: import('./public.js').Updater) => void) => import('./public.js').Unsubscriber | void} fn - function callback that aggregates the values + * @param {T} [initial_value] - initial value + * @returns {import('./public.js').Readable} + */ + +/** + * Derived value store by synchronizing one or more readable stores and + * applying an aggregation function over its input values. + * + * @template {import('./public.js').Stores} S + * @template T + * @overload + * @param {S} stores - input stores + * @param {(values: import('./public.js').StoresValues) => T} fn - function callback that aggregates the values + * @param {T} [initial_value] - initial value + * @returns {import('./public.js').Readable} + */ + +/** + * @template {import('./public.js').Stores} S + * @template T + * @param {S} stores * @param {Function} fn - * @param {T} initial_value - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").Readable} + * @param {T} [initial_value] + * @returns {import('./public.js').Readable} */ export function derived(stores, fn, initial_value) { const single = !Array.isArray(stores); - /** @type {Array>} */ + /** @type {Array>} */ const stores_array = single ? [stores] : stores; if (!stores_array.every(Boolean)) { throw new Error('derived() expects stores as input, got a falsy value'); @@ -147,8 +177,9 @@ export function derived(stores, fn, initial_value) { /** * Takes a store and returns a new one derived from the old one that is readable. * - * @param {Readable} store - store to make readonly - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").Readable} + * @template T + * @param {import('./public.js').Readable} store - store to make readonly + * @returns {import('./public.js').Readable} */ export function readonly(store) { return { @@ -158,55 +189,8 @@ export function readonly(store) { /** * Get the current value from a store by subscribing and immediately unsubscribing. - * @param store readable - */ -export { get_store_value as get }; - -/** - * @typedef {(value: T) => void} Subscriber * @template T + * @param {import('./public.js').Readable} store readable + * @returns {T} */ - -/** @typedef {() => void} Unsubscriber */ - -/** - * @typedef {(value: T) => T} Updater - * @template T - */ - -/** - * @typedef {(value?: T) => void} Invalidator - * @template T - */ - -/** - * @typedef {( - * set: (value: T) => void, - * update: (fn: Updater) => void - * ) => void | (() => void)} StartStopNotifier - * @template T - */ - -/** - * @typedef {[Subscriber, Invalidator]} SubscribeInvalidateTuple - * @template T - */ - -/** @typedef {Readable | [Readable, ...Array>] | Array>} Stores */ - -/** - * @typedef {T extends Readable - * ? U - * : { [K in keyof T]: T[K] extends Readable ? U : never }} StoresValues - * @template T - */ - -/** - * Readable interface for subscribing. - * @typedef {Object} Readable - */ - -/** - * Writable interface for both updating and subscribing. - * @typedef {Object} Writable - */ +export { get_store_value as get }; diff --git a/src/runtime/store/public.d.ts b/src/runtime/store/public.d.ts new file mode 100644 index 0000000000..e35cc3ec83 --- /dev/null +++ b/src/runtime/store/public.d.ts @@ -0,0 +1,61 @@ +/** Callback to inform of a value updates. */ +export type Subscriber = (value: T) => void; + +/** Unsubscribes from value updates. */ +export type Unsubscriber = () => void; + +/** Callback to update a value. */ +export type Updater = (value: T) => T; + +/** Cleanup logic callback. */ +type Invalidator = (value?: T) => void; + +/** + * Start and stop notification callbacks. + * This function is called when the first subscriber subscribes. + * + * @param {(value: T) => void} set Function that sets the value of the store. + * @param {(value: Updater) => void} set Function that sets the value of the store after passing the current value to the update function. + * @returns {void | (() => void)} Optionally, a cleanup function that is called when the last remaining + * subscriber unsubscribes. + */ +export type StartStopNotifier = ( + set: (value: T) => void, + update: (fn: Updater) => void +) => void | (() => void); + +/** Readable interface for subscribing. */ +export interface Readable { + /** + * Subscribe on value changes. + * @param run subscription callback + * @param invalidate cleanup callback + */ + subscribe(this: void, run: Subscriber, invalidate?: Invalidator): Unsubscriber; +} + +/** Writable interface for both updating and subscribing. */ +export interface Writable extends Readable { + /** + * Set value and inform subscribers. + * @param value to set + */ + set(this: void, value: T): void; + + /** + * Update value using callback and inform subscribers. + * @param updater callback + */ + update(this: void, updater: Updater): void; +} + +/** Pair of subscriber and invalidator. */ +type SubscribeInvalidateTuple = [Subscriber, Invalidator]; + +/** One or more `Readable`s. */ +type Stores = Readable | [Readable, ...Array>] | Array>; + +/** One or more values from `Readable` stores. */ +type StoresValues = T extends Readable + ? U + : { [K in keyof T]: T[K] extends Readable ? U : never }; diff --git a/src/runtime/transition/index.js b/src/runtime/transition/index.js index 04dfc4d848..3b7bc9c0fc 100644 --- a/src/runtime/transition/index.js +++ b/src/runtime/transition/index.js @@ -3,8 +3,8 @@ import { assign, split_css_unit, is_function } from '../internal'; /** * @param {Element} node - * @param {BlurParams} - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').BlurParams} [params] + * @returns {import('./public').TransitionConfig} */ export function blur( node, @@ -25,8 +25,8 @@ export function blur( /** * @param {Element} node - * @param {FadeParams} - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').FadeParams} [params] + * @returns {import('./public').TransitionConfig} */ export function fade(node, { delay = 0, duration = 400, easing = linear } = {}) { const o = +getComputedStyle(node).opacity; @@ -40,8 +40,8 @@ export function fade(node, { delay = 0, duration = 400, easing = linear } = {}) /** * @param {Element} node - * @param {FlyParams} - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').FlyParams} [params] + * @returns {import('./public').TransitionConfig} */ export function fly( node, @@ -65,8 +65,8 @@ export function fly( /** * @param {Element} node - * @param {SlideParams} - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').SlideParams} [params] + * @returns {import('./public').TransitionConfig} */ export function slide(node, { delay = 0, duration = 400, easing = cubicOut, axis = 'y' } = {}) { const style = getComputedStyle(node); @@ -106,8 +106,8 @@ export function slide(node, { delay = 0, duration = 400, easing = cubicOut, axis /** * @param {Element} node - * @param {ScaleParams} - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').ScaleParams} [params] + * @returns {import('./public').TransitionConfig} */ export function scale( node, @@ -131,8 +131,8 @@ export function scale( /** * @param {SVGElement & { getTotalLength(): number }} node - * @param {DrawParams} - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').DrawParams} [params] + * @returns {import('./public').TransitionConfig} */ export function draw(node, { delay = 0, speed, duration, easing = cubicInOut } = {}) { let len = node.getTotalLength(); @@ -161,21 +161,21 @@ export function draw(node, { delay = 0, speed, duration, easing = cubicInOut } = } /** - * @param {CrossfadeParams & { - * fallback?: (node: Element, params: CrossfadeParams, intro: boolean) => TransitionConfig; - * }} - * @returns {[(node: any, params: import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").CrossfadeParams & { key: any; }) => () => import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig, (node: any, params: import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").CrossfadeParams & { key: any; }) => () => import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig]} + * @param {import('./public').CrossfadeParams & { + * fallback?: (node: Element, params: import('./public').CrossfadeParams, intro: boolean) => import('./public').TransitionConfig; + * }} params + * @returns {[(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig, (node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig]} */ export function crossfade({ fallback, ...defaults }) { - /** @type {ClientRectMap} */ + /** @type {Map} */ const to_receive = new Map(); - /** @type {ClientRectMap} */ + /** @type {Map} */ const to_send = new Map(); /** * @param {Element} from_node * @param {Element} node - * @param {CrossfadeParams} params - * @returns {import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @param {import('./public').CrossfadeParams} params + * @returns {import('./public').TransitionConfig} */ function crossfade(from_node, node, params) { const { @@ -208,10 +208,10 @@ export function crossfade({ fallback, ...defaults }) { } /** - * @param {ClientRectMap} items - * @param {ClientRectMap} counterparts + * @param {Map} items + * @param {Map} counterparts * @param {boolean} intro - * @returns {(node: any, params: import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").CrossfadeParams & { key: any; }) => () => import("/Users/elliottjohnson/dev/sveltejs/svelte/index.ts-to-jsdoc").TransitionConfig} + * @returns {(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig} */ function transition(items, counterparts, intro) { return (node, params) => { @@ -232,73 +232,3 @@ export function crossfade({ fallback, ...defaults }) { } return [transition(to_send, to_receive, false), transition(to_receive, to_send, true)]; } - -/** @typedef {(t: number) => number} EasingFunction */ -/** @typedef {Map} ClientRectMap */ - -/** - * @typedef {Object} TransitionConfig - * @property {number} [delay] - * @property {number} [duration] - * @property {EasingFunction} [easing] - * @property {(t:number,u:number)=>string} [css] - * @property {(t:number,u:number)=>void} [tick] - */ - -/** - * @typedef {Object} BlurParams - * @property {number} [delay] - * @property {number} [duration] - * @property {EasingFunction} [easing] - * @property {number|string} [amount] - * @property {number} [opacity] - */ - -/** - * @typedef {Object} FadeParams - * @property {number} [delay] - * @property {number} [duration] - * @property {EasingFunction} [easing] - */ - -/** - * @typedef {Object} FlyParams - * @property {number} [delay] - * @property {number} [duration] - * @property {EasingFunction} [easing] - * @property {number|string} [x] - * @property {number|string} [y] - * @property {number} [opacity] - */ - -/** - * @typedef {Object} SlideParams - * @property {number} [delay] - * @property {number} [duration] - * @property {EasingFunction} [easing] - * @property {'x'|'y'} [axis] - */ - -/** - * @typedef {Object} ScaleParams - * @property {number} [delay] - * @property {number} [duration] - * @property {EasingFunction} [easing] - * @property {number} [start] - * @property {number} [opacity] - */ - -/** - * @typedef {Object} DrawParams - * @property {number} [delay] - * @property {number} [speed] - * @property {number|((len:number)=>number)} [duration] - * @property {EasingFunction} [easing] - */ - -/** - * @typedef {Object} CrossfadeParams - * @property {number} [delay] - * @property {number|((len:number)=>number)} [duration] - * @property {EasingFunction} [easing] - */ diff --git a/src/runtime/transition/public.d.ts b/src/runtime/transition/public.d.ts new file mode 100644 index 0000000000..0b158f9c2e --- /dev/null +++ b/src/runtime/transition/public.d.ts @@ -0,0 +1,60 @@ +export type EasingFunction = (t: number) => number; + +export interface TransitionConfig { + delay?: number; + duration?: number; + easing?: EasingFunction; + css?: (t: number, u: number) => string; + tick?: (t: number, u: number) => void; +} + +export interface BlurParams { + delay?: number; + duration?: number; + easing?: EasingFunction; + amount?: number | string; + opacity?: number; +} + +export interface FadeParams { + delay?: number; + duration?: number; + easing?: EasingFunction; +} + +export interface FlyParams { + delay?: number; + duration?: number; + easing?: EasingFunction; + x?: number | string; + y?: number | string; + opacity?: number; +} + +export interface SlideParams { + delay?: number; + duration?: number; + easing?: EasingFunction; + axis?: 'x' | 'y'; +} + +export interface ScaleParams { + delay?: number; + duration?: number; + easing?: EasingFunction; + start?: number; + opacity?: number; +} + +export interface DrawParams { + delay?: number; + speed?: number; + duration?: number | ((len: number) => number); + easing?: EasingFunction; +} + +export interface CrossfadeParams { + delay?: number; + duration?: number | ((len: number) => number); + easing?: EasingFunction; +} diff --git a/tsconfig.json b/tsconfig.json index 16d4a70709..07813fb1fd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,9 @@ "noEmitOnError": true, "noErrorTruncation": true, + "allowJs": true, + "checkJs": true, + // rollup takes care of these "module": "esnext", "moduleResolution": "node",