From fc87c009806dcc98867f6b58efe29b042aa01dee Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Mon, 18 May 2026 23:10:59 +0800 Subject: node: update --- node_modules/swup/src/modules/Hooks.ts | 591 --------------------------------- 1 file changed, 591 deletions(-) delete mode 100644 node_modules/swup/src/modules/Hooks.ts (limited to 'node_modules/swup/src/modules/Hooks.ts') diff --git a/node_modules/swup/src/modules/Hooks.ts b/node_modules/swup/src/modules/Hooks.ts deleted file mode 100644 index e3fb459c..00000000 --- a/node_modules/swup/src/modules/Hooks.ts +++ /dev/null @@ -1,591 +0,0 @@ -import type { DelegateEvent } from 'delegate-it'; - -import type Swup from '../Swup.js'; -import { isPromise, runAsPromise } from '../utils.js'; -import { Visit } from './Visit.js'; -import type { FetchOptions, PageData } from './fetchPage.js'; - -export interface HookDefinitions { - 'animation:out:start': undefined; - 'animation:out:await': { skip: boolean }; - 'animation:out:end': undefined; - 'animation:in:start': undefined; - 'animation:in:await': { skip: boolean }; - 'animation:in:end': undefined; - 'animation:skip': undefined; - 'cache:clear': undefined; - 'cache:set': { page: PageData }; - 'content:replace': { page: PageData }; - 'content:scroll': undefined; - 'enable': undefined; - 'disable': undefined; - 'fetch:request': { url: string; options: FetchOptions }; - 'fetch:error': { url: string; status: number; response: Response }; - 'fetch:timeout': { url: string }; - 'history:popstate': { event: PopStateEvent }; - 'link:click': { el: HTMLAnchorElement; event: DelegateEvent }; - 'link:self': undefined; - 'link:anchor': { hash: string }; - 'link:newtab': { href: string }; - 'page:load': { page?: PageData; cache?: boolean; options: FetchOptions }; - 'page:view': { url: string; title: string }; - 'scroll:top': { options: ScrollIntoViewOptions }; - 'scroll:anchor': { hash: string; options: ScrollIntoViewOptions }; - 'visit:start': undefined; - 'visit:transition': undefined; - 'visit:abort': undefined; - 'visit:end': undefined; -} - -export interface HookReturnValues { - 'content:scroll': Promise | boolean; - 'fetch:request': Promise; - 'page:load': Promise; - 'scroll:top': boolean; - 'scroll:anchor': boolean; -} - -export type HookArguments = HookDefinitions[T]; - -export type HookName = keyof HookDefinitions; - -export type HookNameWithModifier = `${HookName}.${HookModifier}`; - -type HookModifier = 'once' | 'before' | 'replace'; - -/** A generic hook handler. */ -export type HookHandler = ( - /** Context about the current visit. */ - visit: Visit, - /** Local arguments passed into the handler. */ - args: HookArguments -) => Promise | unknown; - -/** A default hook handler with an expected return type. */ -export type HookDefaultHandler = ( - /** Context about the current visit. */ - visit: Visit, - /** Local arguments passed into the handler. */ - args: HookArguments, - /** Default handler to be executed. Available if replacing an internal hook handler. */ - defaultHandler?: HookDefaultHandler -) => T extends keyof HookReturnValues ? HookReturnValues[T] : Promise | unknown; - -export type Handlers = { - [K in HookName]: HookHandler[]; -}; - -export type HookInitOptions = { - [K in HookName as K | `${K}.${HookModifier}`]: HookHandler; -} & { - [K in HookName as K | `${K}.${HookModifier}.${HookModifier}`]: HookHandler; -}; - -/** Unregister a previously registered hook handler. */ -export type HookUnregister = () => void; - -/** Define when and how a hook handler is executed. */ -export type HookOptions = { - /** Execute the hook once, then remove the handler */ - once?: boolean; - /** Execute the hook before the internal default handler */ - before?: boolean; - /** Set a priority for when to execute this hook. Lower numbers execute first. Default: `0` */ - priority?: number; - /** Replace the internal default handler with this hook handler */ - replace?: boolean; -}; - -export type HookRegistration< - T extends HookName, - H extends HookHandler | HookDefaultHandler = HookHandler -> = { - id: number; - hook: T; - handler: H; - defaultHandler?: HookDefaultHandler; -} & HookOptions; - -type HookEventDetail = { - hook: HookName; - args: unknown; - visit: Visit; -}; - -export type HookEvent = CustomEvent; - -type HookLedger = Map, HookRegistration>; - -interface HookRegistry extends Map> { - get(key: K): HookLedger | undefined; - set(key: K, value: HookLedger): this; -} - -/** - * Hook registry. - * - * Create, trigger and handle hooks. - * - */ -export class Hooks { - /** Swup instance this registry belongs to */ - protected swup: Swup; - - /** Map of all registered hook handlers. */ - protected registry: HookRegistry = new Map(); - - // Can we deduplicate this somehow? Or make it error when not in sync with HookDefinitions? - // https://stackoverflow.com/questions/53387838/how-to-ensure-an-arrays-values-the-keys-of-a-typescript-interface/53395649 - protected readonly hooks: HookName[] = [ - 'animation:out:start', - 'animation:out:await', - 'animation:out:end', - 'animation:in:start', - 'animation:in:await', - 'animation:in:end', - 'animation:skip', - 'cache:clear', - 'cache:set', - 'content:replace', - 'content:scroll', - 'enable', - 'disable', - 'fetch:request', - 'fetch:error', - 'fetch:timeout', - 'history:popstate', - 'link:click', - 'link:self', - 'link:anchor', - 'link:newtab', - 'page:load', - 'page:view', - 'scroll:top', - 'scroll:anchor', - 'visit:start', - 'visit:transition', - 'visit:abort', - 'visit:end' - ]; - - constructor(swup: Swup) { - this.swup = swup; - this.init(); - } - - /** - * Create ledgers for all core hooks. - */ - protected init() { - this.hooks.forEach((hook) => this.create(hook)); - } - - /** - * Create a new hook type. - */ - create(hook: string) { - if (!this.registry.has(hook as HookName)) { - this.registry.set(hook as HookName, new Map()); - } - } - - /** - * Check if a hook type exists. - */ - exists(hook: HookName): boolean { - return this.registry.has(hook); - } - - /** - * Get the ledger with all registrations for a hook. - */ - protected get(hook: T): HookLedger | undefined { - const ledger = this.registry.get(hook); - if (ledger) { - return ledger; - } - console.error(`Unknown hook '${hook}'`); - } - - /** - * Remove all handlers of all hooks. - */ - clear() { - this.registry.forEach((ledger) => ledger.clear()); - } - - /** - * Register a new hook handler. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute - * @param options Object to specify how and when the handler is executed - * Available options: - * - `once`: Only execute the handler once - * - `before`: Execute the handler before the default handler - * - `priority`: Specify the order in which the handlers are executed - * - `replace`: Replace the default handler with this handler - * @returns A function to unregister the handler - */ - - // Overload: replacing default handler - on(hook: T, handler: HookDefaultHandler, options: O & { replace: true }): HookUnregister; // prettier-ignore - // Overload: passed in handler options - on(hook: T, handler: HookHandler, options: O): HookUnregister; // prettier-ignore - // Overload: no handler options - on(hook: T, handler: HookHandler): HookUnregister; // prettier-ignore - // Implementation - on( - hook: T, - handler: O['replace'] extends true ? HookDefaultHandler : HookHandler, - options: Partial = {} - ): HookUnregister { - const ledger = this.get(hook); - if (!ledger) { - console.warn(`Hook '${hook}' not found.`); - return () => {}; - } - - const id = ledger.size + 1; - const registration: HookRegistration = { ...options, id, hook, handler }; - ledger.set(handler, registration); - - return () => this.off(hook, handler); - } - - /** - * Register a new hook handler to run before the default handler. - * Shortcut for `hooks.on(hook, handler, { before: true })`. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute - * @param options Any other event options (see `hooks.on()` for details) - * @returns A function to unregister the handler - * @see on - */ - // Overload: passed in handler options - before(hook: T, handler: HookHandler, options: HookOptions): HookUnregister; // prettier-ignore - // Overload: no handler options - before(hook: T, handler: HookHandler): HookUnregister; - // Implementation - before( - hook: T, - handler: HookHandler, - options: HookOptions = {} - ): HookUnregister { - return this.on(hook, handler, { ...options, before: true }); - } - - /** - * Register a new hook handler to replace the default handler. - * Shortcut for `hooks.on(hook, handler, { replace: true })`. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute instead of the default handler - * @param options Any other event options (see `hooks.on()` for details) - * @returns A function to unregister the handler - * @see on - */ - // Overload: passed in handler options - replace(hook: T, handler: HookDefaultHandler, options: HookOptions): HookUnregister; // prettier-ignore - // Overload: no handler options - replace(hook: T, handler: HookDefaultHandler): HookUnregister; // prettier-ignore - // Implementation - replace( - hook: T, - handler: HookDefaultHandler, - options: HookOptions = {} - ): HookUnregister { - return this.on(hook, handler, { ...options, replace: true }); - } - - /** - * Register a new hook handler to run once. - * Shortcut for `hooks.on(hook, handler, { once: true })`. - * @param hook Name of the hook to listen for - * @param handler The handler function to execute - * @param options Any other event options (see `hooks.on()` for details) - * @see on - */ - // Overload: passed in handler options - once(hook: T, handler: HookHandler, options: HookOptions): HookUnregister; // prettier-ignore - // Overload: no handler options - once(hook: T, handler: HookHandler): HookUnregister; - // Implementation - once( - hook: T, - handler: HookHandler, - options: HookOptions = {} - ): HookUnregister { - return this.on(hook, handler, { ...options, once: true }); - } - - /** - * Unregister a hook handler. - * @param hook Name of the hook the handler is registered for - * @param handler The handler function that was registered. - * If omitted, all handlers for the hook will be removed. - */ - // Overload: unregister a specific handler - off(hook: T, handler: HookHandler | HookDefaultHandler): void; - // Overload: unregister all handlers - off(hook: T): void; - // Implementation - off(hook: T, handler?: HookHandler | HookDefaultHandler): void { - const ledger = this.get(hook); - if (ledger && handler) { - const deleted = ledger.delete(handler); - if (!deleted) { - console.warn(`Handler for hook '${hook}' not found.`); - } - } else if (ledger) { - ledger.clear(); - } - } - - /** - * Trigger a hook asynchronously, executing its default handler and all registered handlers. - * Will execute all handlers in order and `await` any `Promise`s they return. - * @param hook Name of the hook to trigger - * @param visit The visit object this hook belongs to - * @param args Arguments to pass to the handler - * @param defaultHandler A default implementation of this hook to execute - * @returns The resolved return value of the executed default handler - */ - // Overload: default order of arguments - async call(hook: T, visit: Visit | undefined, args: HookArguments, defaultHandler?: HookDefaultHandler): Promise>>>; // prettier-ignore - // Overload: legacy order of arguments, with visit missing - async call(hook: T, args: HookArguments, defaultHandler?: HookDefaultHandler): Promise>>>; // prettier-ignore - // Implementation - async call( - hook: T, - arg1: Visit | HookArguments, - arg2: HookArguments | HookDefaultHandler, - arg3?: HookDefaultHandler - ): Promise>>> { - const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3); - - const { before, handler, after } = this.getHandlers(hook, defaultHandler); - await this.run(before, visit, args); - const [result] = await this.run(handler, visit, args, true); - await this.run(after, visit, args); - this.dispatchDomEvent(hook, visit, args); - return result; - } - - /** - * Trigger a hook synchronously, executing its default handler and all registered handlers. - * Will execute all handlers in order, but will **not** `await` any `Promise`s they return. - * @param hook Name of the hook to trigger - * @param visit The visit object this hook belongs to - * @param args Arguments to pass to the handler - * @param defaultHandler A default implementation of this hook to execute - * @returns The (possibly unresolved) return value of the executed default handler - */ - // Overload: default order of arguments - callSync(hook: T, visit: Visit | undefined, args: HookArguments, defaultHandler?: HookDefaultHandler): ReturnType>; // prettier-ignore - // Overload: legacy order of arguments, with visit missing - callSync(hook: T, args: HookArguments, defaultHandler?: HookDefaultHandler): ReturnType>; // prettier-ignore - // Implementation - callSync( - hook: T, - arg1: Visit | HookArguments, - arg2: HookArguments | HookDefaultHandler, - arg3?: HookDefaultHandler - ): ReturnType> { - const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3); - const { before, handler, after } = this.getHandlers(hook, defaultHandler); - this.runSync(before, visit, args); - const [result] = this.runSync(handler, visit, args, true); - this.runSync(after, visit, args); - this.dispatchDomEvent(hook, visit, args); - return result; - } - - /** - * Parse the call arguments for call() and callSync() to allow legacy argument order. - */ - protected parseCallArgs( - hook: T, - arg1: Visit | HookArguments | undefined, - arg2: HookArguments | HookDefaultHandler, - arg3?: HookDefaultHandler - ): [Visit | undefined, HookArguments, HookDefaultHandler | undefined] { - const isLegacyOrder = - !(arg1 instanceof Visit) && (typeof arg1 === 'object' || typeof arg2 === 'function'); - if (isLegacyOrder) { - // Legacy positioning: arguments in second or handler passed in third place - return [undefined, arg1 as HookArguments, arg2 as HookDefaultHandler]; - } else { - // Default positioning: visit passed in as first argument - return [arg1, arg2 as HookArguments, arg3]; - } - } - - /** - * Execute the handlers for a hook, in order, as `Promise`s that will be `await`ed. - * @param registrations The registrations (handler + options) to execute - * @param args Arguments to pass to the handler - */ - - // Overload: running HookDefaultHandler: expect HookDefaultHandler return type - protected async run(registrations: HookRegistration>[], visit: Visit | undefined, args: HookArguments, rethrow: true): Promise>>[]>; // prettier-ignore - // Overload: running user handler: expect no specific type - protected async run(registrations: HookRegistration[], visit: Visit | undefined, args: HookArguments): Promise; // prettier-ignore - // Implementation - protected async run[]>( - registrations: R, - visit: Visit | undefined = this.swup.visit, - args: HookArguments, - rethrow: boolean = false - ): Promise>> | unknown[]> { - const results = []; - for (const { hook, handler, defaultHandler, once } of registrations) { - if (visit?.done) continue; - if (once) this.off(hook, handler); - try { - const result = await runAsPromise(handler, [visit, args, defaultHandler]); - results.push(result); - } catch (error) { - if (rethrow) { - throw error; - } else { - console.error(`Error in hook '${hook}':`, error); - } - } - } - return results; - } - - /** - * Execute the handlers for a hook, in order, without `await`ing any returned `Promise`s. - * @param registrations The registrations (handler + options) to execute - * @param args Arguments to pass to the handler - */ - - // Overload: running HookDefaultHandler: expect HookDefaultHandler return type - protected runSync(registrations: HookRegistration>[], visit: Visit | undefined, args: HookArguments, rethrow: true): ReturnType>[]; // prettier-ignore - // Overload: running user handler: expect no specific type - protected runSync(registrations: HookRegistration[], visit: Visit | undefined, args: HookArguments): unknown[]; // prettier-ignore - // Implementation - protected runSync[]>( - registrations: R, - visit: Visit | undefined = this.swup.visit, - args: HookArguments, - rethrow: boolean = false - ): (ReturnType> | unknown)[] { - const results = []; - for (const { hook, handler, defaultHandler, once } of registrations) { - if (visit?.done) continue; - if (once) this.off(hook, handler); - try { - const result = (handler as HookDefaultHandler)(visit, args, defaultHandler); - results.push(result); - if (isPromise(result)) { - console.warn( - `Swup will not await Promises in handler for synchronous hook '${hook}'.` - ); - } - } catch (error) { - if (rethrow) { - throw error; - } else { - console.error(`Error in hook '${hook}':`, error); - } - } - } - return results; - } - - /** - * Get all registered handlers for a hook, sorted by priority and registration order. - * @param hook Name of the hook - * @param defaultHandler The optional default handler of this hook - * @returns An object with the handlers sorted into `before` and `after` arrays, - * as well as a flag indicating if the original handler was replaced - */ - protected getHandlers(hook: T, defaultHandler?: HookDefaultHandler) { - const ledger = this.get(hook); - if (!ledger) { - return { found: false, before: [], handler: [], after: [], replaced: false }; - } - - const registrations = Array.from(ledger.values()); - - // Let TypeScript know that replaced handlers are default handlers by filtering to true - const def = (T: HookRegistration): T is HookRegistration> => true; // prettier-ignore - const sort = this.sortRegistrations; - - // Filter into before, after, and replace handlers - const before = registrations.filter(({ before, replace }) => before && !replace).sort(sort); - const replace = registrations.filter(({ replace }) => replace).filter(def).sort(sort); // prettier-ignore - const after = registrations.filter(({ before, replace }) => !before && !replace).sort(sort); - const replaced = replace.length > 0; - - // Define main handler registration - // Created as HookRegistration[] array to allow passing it into hooks.run() directly - let handler: HookRegistration>[] = []; - if (defaultHandler) { - handler = [{ id: 0, hook, handler: defaultHandler }]; - if (replaced) { - const index = replace.length - 1; - const { handler: replacingHandler, once } = replace[index]; - const createDefaultHandler = (index: number): HookDefaultHandler | undefined => { - const next = replace[index - 1]; - if (next) { - return (visit, args) => - next.handler(visit, args, createDefaultHandler(index - 1)); - } else { - return defaultHandler; - } - }; - const nestedDefaultHandler = createDefaultHandler(index); - handler = [{ id: 0, hook, once, handler: replacingHandler, defaultHandler: nestedDefaultHandler }]; // prettier-ignore - } - } - - return { found: true, before, handler, after, replaced }; - } - - /** - * Sort two hook registrations by priority and registration order. - * @param a The registration object to compare - * @param b The other registration object to compare with - * @returns The sort direction - */ - protected sortRegistrations( - a: HookRegistration, - b: HookRegistration - ): number { - const priority = (a.priority ?? 0) - (b.priority ?? 0); - const id = a.id - b.id; - return priority || id || 0; - } - - /** - * Dispatch a custom event on the `document` for a hook. Prefixed with `swup:` - * @param hook Name of the hook. - */ - protected dispatchDomEvent( - hook: T, - visit: Visit | undefined, - args?: HookArguments - ): void { - if (visit?.done) return; - - const detail: HookEventDetail = { hook, args, visit: visit || this.swup.visit }; - document.dispatchEvent( - new CustomEvent(`swup:any`, { detail, bubbles: true }) - ); - document.dispatchEvent( - new CustomEvent(`swup:${hook}`, { detail, bubbles: true }) - ); - } - - /** - * Parse a hook name into the name and any modifiers. - * @param hook Name of the hook. - */ - parseName(hook: HookName | HookNameWithModifier): [HookName, Partial] { - const [name, ...modifiers] = hook.split('.'); - const options = modifiers.reduce((acc, mod) => ({ ...acc, [mod]: true }), {}); - return [name as HookName, options]; - } -} -- cgit v1.2.3