UNPKG

@plugjs/plug

Version:
195 lines (194 loc) 7.62 kB
import type { Files } from './files'; import type { Pipe } from './index'; import type { Logger } from './logging'; import type { AbsolutePath } from './paths'; import type { Result } from './types'; /** A convenience type indicating what can be returned by a {@link Plug}. */ export type PlugResult = Files | undefined | void; /** * The {@link Plug} interface describes _build plugin_. * * A {@link Plug} receives a {@link Files} instance in its input (for example * a list of _source `.ts` files_) and optionally produces a possibly different * list (for example the _compiled `.js` files_). */ export interface Plug<T extends PlugResult> { pipe(files: Files, context: Context): T | Promise<T>; } /** A type identifying a {@link Plug} as a `function` */ export type PlugFunction<T extends PlugResult> = Plug<T>['pipe']; /** * The {@link Context} class defines the context in which a {@link Plug} * is invoked. */ export declare class Context { /** The absolute file name where the task was defined. */ readonly buildFile: AbsolutePath; /** The _name_ of the task associated with this {@link Context}. */ readonly taskName: string; /** The directory of the file where the task was defined (convenience). */ readonly buildDir: AbsolutePath; /** The {@link Logger} associated with this instance. */ readonly log: Logger; constructor( /** The absolute file name where the task was defined. */ buildFile: AbsolutePath, /** The _name_ of the task associated with this {@link Context}. */ taskName: string); /** * Resolve a (set of) path(s) in this {@link Context}. * * If the path (or first component thereof) starts with `@...`, then the * resolved path will be relative to the directory containing the build file * where the current task was defined, otherwise it will be relative to the * current working directory. */ resolve(path: string, ...paths: string[]): AbsolutePath; } /** * An internal class recording _hot_ (failure will fail the task) and _cold_ * (failure will be ignored) {@link Promise}s for a task's {@link Context}. */ export declare class ContextPromises { readonly context: Context; private readonly _cold; private readonly _hot; private constructor(); /** Track a {@link Promise} _hot_ (failure will fail the task) */ hot(promise: Promise<Result>): void; /** Track a {@link Promise} _cold_ (failure will be ignored) */ cold(promise: Promise<Result>): void; /** * Await all tracked {@link Promise}s, triggering a build failure if any of * the _hot_ ones is rejected. */ static wait(context: Context): Promise<void>; /** Get a {@link ContextPromises} instance for the given {@link Context} */ static get(context: Context): ContextPromises; } /** The default implementation of the {@link Pipe} interface. */ export interface PipeImpl extends Pipe { } /** The default implementation of the {@link Pipe} interface. */ export declare class PipeImpl implements Pipe { private readonly _context; private readonly _promise; readonly [Symbol.toStringTag] = "Pipe"; constructor(_context: Context, _promise: Promise<Result>); then<R1 = Files, R2 = never>(onfulfilled?: ((value: Files) => R1 | PromiseLike<R1>) | null | undefined, onrejected?: ((reason: any) => R2 | PromiseLike<R2>) | null | undefined): Promise<R1 | R2>; catch<R = never>(onrejected?: ((reason: any) => R | PromiseLike<R>) | null | undefined): Promise<Files | R>; finally(onfinally?: (() => void) | null | undefined): Promise<Files>; plug(plug: Plug<Files>): Pipe; plug(plug: PlugFunction<Files>): Pipe; plug(plug: Plug<void | undefined>): Promise<undefined>; plug(plug: PlugFunction<void | undefined>): Promise<undefined>; } /** The names which can be installed as direct plugs. */ export type PlugName = string & Exclude<keyof Pipe, 'plug' | keyof Promise<any>>; /** The parameters of the plug extension with the given name */ export type PipeParameters<Name extends PlugName> = PipeOverloads<Name>['args']; /** Extract arguments and return types from function overloads. */ type PipeOverloads<Name extends PlugName> = Pipe[Name] extends { (...args: infer A0): infer R0; (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; (...args: infer A4): infer R4; } ? (R0 extends (Pipe | Promise<undefined>) ? { args: A0; ret: R0; } : never) | (R1 extends (Pipe | Promise<undefined>) ? { args: A1; ret: R1; } : never) | (R2 extends (Pipe | Promise<undefined>) ? { args: A2; ret: R2; } : never) | (R3 extends (Pipe | Promise<undefined>) ? { args: A3; ret: R3; } : never) | (R4 extends (Pipe | Promise<undefined>) ? { args: A4; ret: R4; } : never) : Pipe[Name] extends { (...args: infer A0): infer R0; (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; } ? (R0 extends (Pipe | Promise<undefined>) ? { args: A0; ret: R0; } : never) | (R1 extends (Pipe | Promise<undefined>) ? { args: A1; ret: R1; } : never) | (R2 extends (Pipe | Promise<undefined>) ? { args: A2; ret: R2; } : never) | (R3 extends (Pipe | Promise<undefined>) ? { args: A3; ret: R3; } : never) : Pipe[Name] extends { (...args: infer A0): infer R0; (...args: infer A1): infer R1; (...args: infer A2): infer R2; } ? (R0 extends (Pipe | Promise<undefined>) ? { args: A0; ret: R0; } : never) | (R1 extends (Pipe | Promise<undefined>) ? { args: A1; ret: R1; } : never) | (R2 extends (Pipe | Promise<undefined>) ? { args: A2; ret: R2; } : never) : Pipe[Name] extends { (...args: infer A0): infer R0; (...args: infer A1): infer R1; } ? (R0 extends (Pipe | Promise<undefined>) ? { args: A0; ret: R0; } : never) | (R1 extends (Pipe | Promise<undefined>) ? { args: A1; ret: R1; } : never) : Pipe[Name] extends { (...args: infer A0): infer R0; } ? (R0 extends (Pipe | Promise<undefined>) ? { args: A0; ret: R0; } : never) : never; /** The parameters of the plug extension with the given name */ type PipeResult<Name extends PlugName> = PipeOverloads<Name>['ret']; /** * A type defining the correct constructor for a {@link Plug}, inferring * argument types and instance type from the definitions in {@link Pipe}. */ type PlugConstructor<Name extends PlugName> = PipeResult<Name> extends Pipe ? new (...args: PipeParameters<Name>) => Plug<Files> : PipeResult<Name> extends Promise<undefined> ? new (...args: PipeParameters<Name>) => Plug<void | undefined> : PipeResult<Name> extends (Pipe | Promise<undefined>) ? new (...args: PipeParameters<Name>) => Plug<Files | void | undefined> : never; /** * Install a {@link Plug} into our {@link Pipe} prototype. * * This allows our shorthand syntax for well-defined plugs such as: * * ``` * find('./src', '*.ts').write('./target') * // Nicer and easier than... * find('./src', '*.ts').plug(new Write('./target')) * ``` * * Use this alongside interface merging like: * * ``` * declare module '@plugjs/plug/pipe' { * export interface Pipe { * write(): Pipe * } * } * * install('write', class Write implements Plug { * constructorg(...args: PipeParams<'write'>) { * // here `args` is automatically inferred by whatever was declared above * } * * // ... the plug implementation lives here * }) * ``` */ export declare function install<Name extends PlugName, Ctor extends PlugConstructor<Name>>(name: Name, ctor: Ctor): void; export {};