import type { Bud, Modules } from '@roots/bud-framework'; import type { Compiler } from '@roots/bud-framework/config'; import type { ApplyPluginConstructor } from '@roots/bud-framework/extension/decorators/plugin'; import { type Logger } from '@roots/bud-support/logger'; import DynamicOption from '@roots/bud-support/value'; export type OptionsInterface> = { [K in keyof T as `${K & string}`]?: T[K]; }; export type InternalOptions = { [K in keyof T as `${K & string}`]: DynamicOption<(app: Bud) => T[K]> | T[K]; }; export type OptionCallback = ((value: T[K]) => T[K]) | T[K]; export type OptionCallbackValue = ((value: T[K]) => T[K]) | InternalOptions[K]; export type Setter = (value: OptionCallbackValue) => Extension; export type Getter = () => Options[Property]; export type Accessor = Options[Property]; export type Option = { get: Getter; set: Setter; value: Accessor; }; export interface Meta { boot: boolean; buildAfter: boolean; buildBefore: boolean; compilerDone: boolean; configAfter: boolean; register: boolean; } export interface Constructor { new (...args: any[]): BudExtension; } export type WithOptions = { [K in keyof Options as `${K & string}`]: Options[K]; } & { [Prop in keyof Options as `get${Capitalize}`]: () => Options[Prop]; } & { [Prop in keyof Options as `set${Capitalize}`]: (value: OptionCallbackValue) => Context; }; export type ExtensionApi = BudExtension & WithOptions; /** * Webpack plugin. */ export interface ApplyPlugin { /** * @see {@link https://webpack.js.org/contribute/writing-a-plugin/#basic-plugin-architecture} */ apply(...args: any[]): unknown; } export interface BudExtension { _app: () => Bud; _options: Partial>; /** * Extension.app */ app: Bud; /** * {@link ApplyPlugin.apply} callback */ apply?(compiler: Compiler): unknown | void; /** * `boot` callback */ boot?(app: Bud): Promise; /** * `buildAfter` callback */ buildAfter?(app: Bud): Promise; /** * `buildBefore` callback */ buildBefore?(app: Bud): Promise; /** * Error handler */ catch(error: Error | string): never; /** * `configAfter` callback */ configAfter?(app: Bud): Promise; /** * Depends on */ dependsOn?: Set; /** * Depends on (optional) */ dependsOnOptional?: Set<`${keyof Modules & string}`>; /** * Disable extension * @deprecated pass `false` to {@link Extension.enable} */ disable(): void; /** * Return to bud instance from extension */ done(): Bud; /** * Enable extension */ enable(enabled?: boolean | Bud): this; /** * Extension.enabled * * @remarks * If `true`, the extension is enabled. */ enabled: boolean; execute(key: `${keyof Meta & string}` | `make`): Promise; /** * Get an option value */ get: BudExtension['getOption']; /** * Get option */ getOption(key: K): Options[K]; /** * Get options */ getOptions(): Options; /** * Extension.import */ import(signifier: string, context: string, options?: { bustCache?: boolean; raw?: boolean; }): Promise; /** * Is extension enabled? */ isEnabled(): boolean; /** * The module name */ label?: `${keyof Modules & string}`; /** * Logger instance */ logger: Logger; /** * `make` callback */ make?(app: Bud, options?: Options): Promise; /** * Extension.meta * * @remarks * Tracks which extension methods have been executed to prevent * duplicate execution in race conditions, etc. */ meta: Meta; /** * Extension.options * * @readonly */ options: Options; /** * Plugin constructor */ plugin?: ApplyPluginConstructor; /** * {@link Extension.register} */ register?(app: Bud): Promise; /** * Resolve module using `import.meta.resolve` api */ resolve(signifier: string, context: string): Promise; /** * Set option */ set: BudExtension['setOption']; /** * Set option */ setOption(key: K, valueOrCallback: OptionCallbackValue): BudExtension; /** * Set options */ setOptions(value: Partial>): BudExtension; /** * Extension.when * * Function returning a boolean indicating if the {@link Extension} should be utilized. * * @remarks * If set this takes precedence over {@link Extension.enabled}. */ when?(bud: Bud, options?: Options): boolean; }