diff --git a/types/alpinejs/alpinejs-tests.ts b/types/alpinejs/alpinejs-tests.ts index 47ccc0e4aed0b6..64adb25dbb3a02 100644 --- a/types/alpinejs/alpinejs-tests.ts +++ b/types/alpinejs/alpinejs-tests.ts @@ -55,6 +55,24 @@ import Alpine, { Alpine.raw(data); } +{ + // Alpine.transaction + // introduced in 3.16.0 + + const data = Alpine.reactive({ count: 1 }); + + // $ExpectType Promise + Alpine.transaction(() => { + data.count = 2; + data.count = 3; + }); + + // $ExpectType Promise + Alpine.transaction(async () => { + await Promise.resolve(); + }); +} + { // Alpine.version // $ExpectType string @@ -86,6 +104,21 @@ import Alpine, { }); } +{ + // Alpine.setErrorHandler + // introduced in 3.16.0 + + // $ExpectType void + Alpine.setErrorHandler((error, el, expression) => { + // $ExpectType Error + error; + // $ExpectType ElementWithXAttributes + el; + // $ExpectType string | undefined + expression; + }); +} + { // Alpine.closestDataStack const someNode = document.body; @@ -163,6 +196,21 @@ import Alpine, { Alpine.deferMutations(); } +{ + // Alpine.deferInit + // introduced in 3.17.0 + + const el = document.body; + const prerequisite = Promise.resolve(); + + // $ExpectType void + Alpine.deferInit(el, prerequisite); + + // any value is accepted, it is passed through `Promise.resolve` + // $ExpectType void + Alpine.deferInit(el, "not a promise"); +} + { // Alpine.mapAttributes // inspired by @@ -214,6 +262,62 @@ import Alpine, { Alpine.setEvaluator(justExpressionEvaluator); } +{ + // Alpine.setRawEvaluator + // introduced in 3.16.0 + + // $ExpectType void + Alpine.setRawEvaluator((el, expression) => Alpine.evaluate(el, expression)); +} + +{ + // Alpine.evaluateRaw + // introduced in 3.16.0 + + const el = document.body; + + // $ExpectType number + Alpine.evaluateRaw(el, "1 + 1"); + + // $ExpectType unknown + Alpine.evaluateRaw(el, "1 + 1", { scope: { two: 2 } }); +} + +{ + // Alpine.initInterceptors + // exposed on the Alpine object since 3.16.0 + + const data: Record = {}; + + // $ExpectType void + Alpine.initInterceptors(data); + + // $ExpectType void + Alpine.initInterceptors(data, (callback) => callback); +} + +{ + // Alpine.injectMagics + // exposed on the Alpine object since 3.16.0 + + const el = document.body; + const scope: Record = {}; + + // $ExpectType Record & Magics> + Alpine.injectMagics(scope, el); + + const data = { count: 0 }; + + // $ExpectType { count: number; } & Magics<{ count: number; }> + Alpine.injectMagics(data, el); + + // $ExpectType string + Alpine.injectMagics(data, el).$el.tagName; + + // $ExpectType number + Alpine.injectMagics(data, el).count; +} + { // Alpine.mergeProxies const el = document.body; @@ -292,6 +396,18 @@ import Alpine, { }; }, ); + + // the interceptor callback receives a cleanup registrar since 3.16.0 + Alpine.interceptor((initialValue, getter, setter, path, key, cleanup) => { + // $ExpectType string + path; + // $ExpectType string + key; + // $ExpectType (callback: () => void) => void + cleanup; + cleanup(() => storage.removeItem(`_x_${path}`)); + return initialValue; + }); } { @@ -428,6 +544,28 @@ import Alpine, { const data = Alpine.evaluate(el, expression, { scope: dataProviderContext }); } +{ + // Alpine.watch + // available on the Alpine object since 3.13 + + const data = Alpine.reactive({ count: 1 }); + + // $ExpectType () => void + Alpine.watch( + () => data.count, + ( + // $ExpectType number + newValue, + // $ExpectType number + oldValue, + ) => {}, + ); + + const stopWatching = Alpine.watch(() => data.count, () => {}); + // $ExpectType () => void + stopWatching; +} + { // Alpine.initTree // inspired by diff --git a/types/alpinejs/index.d.ts b/types/alpinejs/index.d.ts index 1db25a01e292f1..b4584a9362cdfa 100644 --- a/types/alpinejs/index.d.ts +++ b/types/alpinejs/index.d.ts @@ -32,6 +32,15 @@ declare namespace Alpine { * @returns raw object */ readonly raw: (obj: T) => T; + /** + * Batches all reactive updates triggered inside the callback + * Effects are queued but not flushed until the callback (and any + * promise it returns) has settled + * + * @param callback to run inside the transaction + * @returns a promise that resolves once the transaction is committed + */ + readonly transaction: (callback: () => unknown) => Promise; version: string; /** * Handles all deferred mutation entries @@ -68,9 +77,16 @@ declare namespace Alpine { setReactivityEngine: (engine: { reactive: (obj: T) => T; release: (effect: E) => void; - effect: (fn: () => any) => E; + effect: (fn: () => any, options?: { scheduler?: (task: () => void) => void }) => E; raw: (obj: T) => T; }) => void; + /** + * Registers a handler that is called whenever an Alpine expression throws + * Replaces the default handler, which warns and rethrows out of band + * + * @param handler to handle the error, el and expression may be undefined + */ + setErrorHandler(handler: (error: Error, el: Alpine.ElementWithXAttributes, expression?: string) => void): void; /** * Registers a listener for when a specific attribute is removed from an element * @param el @@ -140,6 +156,16 @@ declare namespace Alpine { * call `flushAndStopDeferringMutations` to resume handling */ deferMutations(): void; + /** + * Suspends initialization of the tree rooted at the element + * until the given promise settles + * Directives inside the tree do not evaluate, added nodes wait and + * attribute changes are replayed once every registered promise has settled + * + * @param el root of the tree to suspend + * @param promise to await before initializing the tree + */ + deferInit(el: Alpine.ElementWithXAttributes, promise: unknown): void; /** * Registers a callback to preprocess attributes/directives before they are evaluated * Allows transforming custom syntaxes into known directives @@ -164,6 +190,7 @@ declare namespace Alpine { extras?: { scope?: object; params?: unknown[]; + context?: unknown; }, ) => void; /** @@ -172,6 +199,26 @@ declare namespace Alpine { * @param callback */ interceptInit(callback: Alpine.WalkerCallback): void; + /** + * Walks a data object and initializes every interceptor it contains + * Interceptors are replaced in place by their initialized value + * + * @param data object to initialize interceptors on + * @param cleanup registers a callback to run when the owning element is destroyed + */ + initInterceptors(data: Record, cleanup?: (callback: () => void) => void): void; + /** + * Defines every registered magic (`$name`) onto the provided object, + * bound to the given element + * + * @param obj to define the magics on + * @param el the magics will be bound to + * @returns the same object, augmented with the magics + */ + injectMagics>( + obj: T, + el: Alpine.ElementWithXAttributes, + ): T & Alpine.Magics; /** * Registers an evaluator to be used * Used internally by Alpine CSP to use a CSP safe evaluator @@ -186,9 +233,27 @@ declare namespace Alpine { extras?: { scope?: object; params?: unknown[]; + context?: unknown; }, ) => void, ) => void; + /** + * Registers the evaluator used by {@link Alpine.evaluateRaw} + * The raw evaluator is synchronous and returns the value directly + * + * @param newEvaluator + */ + setRawEvaluator: ( + newEvaluator: ( + el: Alpine.ElementWithXAttributes, + expression: string, + extras?: { + scope?: object; + params?: unknown[]; + context?: unknown; + }, + ) => unknown, + ) => void; /** * "Flattens" an array of objects into a single Proxy object * @param {Array} objects @@ -348,7 +413,43 @@ declare namespace Alpine { * @param extras additional values to expose to the expression * @returns whatever the expression returns */ - evaluate(el: Node, expression: string | (() => T_9), extras?: {}): T_9; + evaluate( + el: Node, + expression: string | (() => T_9), + extras?: { + scope?: object; + params?: unknown[]; + context?: unknown; + }, + ): T_9; + /** + * Evaluates a string expression synchronously, without going through + * the standard (async) evaluator, and returns the value directly + * + * @param el element in Alpine Context + * @param expression string expression + * @param extras additional values to expose to the expression + * @returns whatever the expression returns + */ + // eslint-disable-next-line @definitelytyped/no-unnecessary-generics + evaluateRaw( + el: Alpine.ElementWithXAttributes, + expression: string, + extras?: { + scope?: object; + params?: unknown[]; + context?: unknown; + }, + ): T; + /** + * Watches a reactive getter and runs the callback whenever its value changes + * Objects and arrays are watched deeply + * + * @param getter returning the value to watch + * @param callback to run with the new and the previous value + * @returns a function that stops watching + */ + watch(getter: () => T, callback: (newValue: T, oldValue: T) => void): () => void; /** * Initializes the Alpine tree rooted at a particular element * Used internally in {@link Alpine.start} and to initialize cloned templates @@ -500,13 +601,45 @@ declare namespace Alpine { _x_refs_proxy: Record; _x_refs: unknown; _x_keyExpression: string; - _x_prevKeys: string[]; + /** + * Marker dispensed when the element is initialized + * Used to tell whether a node has already been initialized, + * so moved or re-inserted nodes are not initialized twice + * + * @since 3.14 + */ + _x_marker: number; + /** + * Set while the tree rooted at this element is suspended by + * {@link Alpine.deferInit} + * + * @since 3.17.0 + */ + _x_deferInit: { + pending: number; + ownsIgnore: boolean; + queuedAttributes: Map }>; + }; _x_forScope: Record; - _x_lookup: Record; + _x_lookup: Map; + /** + * The element most recently rendered by `x-if` or `x-for` + * Lets morph skip past the rendered output instead of diffing it + * + * @since 3.16.0 + */ + _x_lastRenderedEl: ElementWithXAttributes; _x_currentIfEl: ElementWithXAttributes; _x_undoIf: () => void; _x_removeModelListeners: Record void>; _x_model: GetterSetter; + /** + * Model sync callbacks deferred until the form is submitted + * Used by `x-model` on inputs inside a form with a pending submit + * + * @since 3.16.0 + */ + _x_pendingModelUpdates: Array<() => void>; _x_forceModelUpdate: (value: unknown) => void; _x_forwardEvents: string[]; _x_doHide: () => void; @@ -572,12 +705,24 @@ declare namespace Alpine { original: string; } - type InterceptorCallback = (initial: T, get: () => T, set: (val: T) => void, path: string, key: string) => T; + type InterceptorCallback = ( + initial: T, + get: () => T, + set: (val: T) => void, + path: string, + key: string, + cleanup: (callback: () => void) => void, + ) => T; interface InterceptorObject { initialValue: T; _x_interceptor: true; - initialize: (data: Record, path: string, key: string) => T; + initialize: ( + data: Record, + path: string, + key: string, + cleanup?: (callback: () => void) => void, + ) => T; } /** @@ -708,11 +853,28 @@ declare namespace Alpine { id: number; active: boolean; raw: () => T; + /** + * Marks the effect as structural so it is flushed before other effects, + * ordered by the depth of its element in the tree + * + * @since 3.16 + */ + _x_schedulerPriority?: { + el: ElementWithXAttributes; + order: number; + }; } interface GetterSetter { get(): T; set(value: T): void; + /** + * Setter that also applies the modifiers (`.debounce`, `.throttle`) + * declared on the `x-model` directive + * + * @since 3.16.0 + */ + setWithModifiers?: (value: T) => void; } } diff --git a/types/alpinejs/package.json b/types/alpinejs/package.json index 12332b7496005c..79814f0a3e7bf8 100644 --- a/types/alpinejs/package.json +++ b/types/alpinejs/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@types/alpinejs", - "version": "3.13.9999", + "version": "3.17.9999", "projects": [ "https://github.com/alpinejs/alpine" ],