@plugjs/plug
Version:
PlugJS Build System ===================
195 lines (194 loc) • 7.62 kB
TypeScript
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 {};