import EventEmitter from 'events'; import { type NormalizedScriptLocator } from './NativeScriptManager.js'; import { Script } from './Script.js'; import type { ScriptLocator, ScriptLocatorResolver, StorageApi } from './types.js'; type Cache = Record>; type ScriptsPromises = Record & { isPrefetch?: true; }) | undefined>; type MaybePromise = T | Promise; export interface ResolverOptions { /** * Priority of the resolver. Defaults to `2`. * Resolvers are called based on the highest priority, * so higher the number, the higher priority the resolver gets. */ priority?: number; /** * Unique key to identify the resolver. * If not provided, the resolver will be added unconditionally. * If provided, the new resolver will be replace the existing one configured with the same `uniqueKey`. */ key?: string; } interface ResolveScriptOptions { scriptId: string; caller?: string; webpackContext: RepackRuntimeGlobals.WebpackRequire; referenceUrl?: string; } interface LoadScriptOptions extends ResolveScriptOptions { } interface ResolveHookOptions { options: ResolveScriptOptions; resolvers: Array<[string, string | number, ScriptLocatorResolver]>; } interface BeforeResolveHookOptions { options: ResolveScriptOptions; } interface AfterResolveHookOptions { options: ResolveScriptOptions; locator: ScriptLocator; } interface ErrorResolveHookOptions { options: ResolveScriptOptions; error: Error; } interface BeforeLoadHookOptions { options: LoadScriptOptions; script: Script; } interface LoadHookOptions { options: LoadScriptOptions; script: Script; loadScript: (scriptId?: string, locator?: NormalizedScriptLocator & { retryDelay?: number; retry?: number; }) => Promise; } interface AfterLoadHookOptions { options: LoadScriptOptions; script: Script; } interface ErrorLoadHookOptions { options: LoadScriptOptions; error: Error; } /** * A manager to ease resolution, downloading and executing additional code from: * - arbitrary JavaScript scripts * - Webpack chunks * - Webpack bundles * - Webpack MF containers * * ScriptManager is globally available under `ScriptManager.shared` in main bundle, chunks and containers. * * Use `ScriptManager.shared` instead of creating new instance of `ScriptManager`. * * This API is mainly useful, if you are working with any form of Code Splitting. * * `ScriptManager` is also an `EventEmitter` and emits the following events: * - `resolving` with `{ scriptId, caller }` * - `resolved` with `scriptId: string, caller?: string, locator: NormalizedScriptLocator, cache: boolean` * - `prefetching` with `scriptId: string, caller?: string, locator: NormalizedScriptLocator, cache: boolean` * - `loading` with `scriptId: string, caller?: string, locator: NormalizedScriptLocator, cache: boolean` * - `loaded` with `scriptId: string, caller?: string, locator: NormalizedScriptLocator, cache: boolean` * - `error` with `error: Error` * * Example of using this API with async Webpack chunk: * ```js * import * as React from 'react'; * import { ScriptManager, Script } from '@callstack/repack/client'; * * ScriptManager.shared.addResolver(async (scriptId) => { * if (__DEV__) { * return { * url: Script.getDevServerURL(scriptId); * cache: false, * }; * } * * return { * url: Script.getRemoteURL(`http://domain.exaple/apps/${scriptId}`), * }; * }); * * // ScriptManager.shared.loadScript is called internally when running `import()` * const TeacherModule = React.lazy(() => import('./Teacher.js')); * const StudentModule = React.lazy(() => import('./Student.js')); * * export function App({ role }) { * if (role === 'teacher') { * return ; * } * * return * } * ``` */ export declare class ScriptManager extends EventEmitter { private nativeScriptManager; static init(): void; static get shared(): ScriptManager; protected cache: Cache; protected scriptsPromises: ScriptsPromises; protected cacheInitialized: boolean; protected resolvers: Array<[string, string | number, ScriptLocatorResolver]>; protected storage?: StorageApi; /** * Constructs instance of `ScriptManager`. * * __Should not be called directly__ - use `ScriptManager.shared`. * * @internal */ protected constructor(nativeScriptManager?: import("./NativeScriptManager.js").Spec); private hookMap; hooks: { beforeResolve: (fn: (args: BeforeResolveHookOptions) => MaybePromise) => void; resolve: (fn: (args: ResolveHookOptions) => MaybePromise) => void; afterResolve: (fn: (args: AfterResolveHookOptions) => MaybePromise) => void; errorResolve: (fn: (args: ErrorResolveHookOptions) => MaybePromise) => void; beforeLoad: (fn: (args: BeforeLoadHookOptions) => MaybePromise) => void; load: (fn: (args: LoadHookOptions) => MaybePromise) => void; afterLoad: (fn: (args: AfterLoadHookOptions) => MaybePromise) => void; errorLoad: (fn: (args: ErrorLoadHookOptions) => MaybePromise) => void; }; /** * Sets a storage backend to cache resolved scripts locator data. * * The stored data is used to detect if scripts locator data of previously downloaded * script hasn't changed to avoid over-fetching the script. * * @param storage Implementation of storage functions. */ setStorage(storage?: StorageApi): void; /** * Adds new script locator resolver. * * Resolver is an async function to resolve script locator data - in other words, it's a function to * tell the {@link ScriptManager} how to fetch the script. * * There's no limitation on what logic you can run inside this function - it can include: * - fetching/loading remote config * - fetching/loading feature flags * - fetching/loading A/B testing data * - calling native modules * - running arbitrary logic * * @param resolver Resolver function to add. * @param options Resolver options. */ addResolver(resolver: ScriptLocatorResolver, options?: ResolverOptions): void; /** * Removes previously added resolver. * * @param resolver Resolver function or resolver's `uniqueKey` to remove. * @returns `true` if resolver was found and removed, `false` otherwise. */ removeResolver(resolver: ScriptLocatorResolver | string): boolean; /** * Removes all previously added resolvers. */ removeAllResolvers(): void; protected initCache(): Promise; protected saveCache(): Promise; protected handleError(error: any, message: string, ...args: any[]): never; /** * Resolves a {@link Script} instance with normalized locator data. * * Resolution will use previously added (via `ScriptManager.shared.addResolver(...)`) resolvers * in series, util one returns a locator data or will throw if no resolver handled the request. * * Use `ScriptManager.shared.on('resolving', ({ scriptId, caller }) => { })` to listen for when * the script resolution begins. * * Use `ScriptManager.shared.on('resolved', (script) => { })` to listen for when * the script's locator data is resolved. * * @param scriptId Id of the script to resolve. * @param caller Name of the calling script - it can be for example: name of the bundle, chunk or container. */ resolveScript(scriptId: string, caller?: string, webpackContext?: RepackRuntimeGlobals.WebpackRequire, referenceUrl?: string): Promise