UNPKG

@aurbi/tiny-composite-builder

Version:

Small framework for taking base objects and adding extensions using a builder pattern, but with full and strict compile-time type support.

72 lines (71 loc) 2.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Builder = void 0; /** * A builder that should produce a base * @typeparam T extends ExtensibleBase: what kind of extensible object are we creating? */ class Builder { /** * Create a new ExtensibleBase object with optional extensions, added via a builder function. * @param builder Builder function that describes the desired extensions for the object * @returns New instance of ExtensibleBase object with the requested extensions present */ static Create(base, builder, ...args) { let instance = new Builder(base); instance = builder(instance); const built = instance.make(...args); return built; } constructor(_base) { this._base = _base; this._extensions = []; this._methodNameFilter = (m) => m.startsWith("$"); } /** * Add a compatible extension to be grafted onto the base. * @param extClass Extension class (non-instantiated: use the class name as a value!) * @param args Arguments, if any, to pass along to the extension constructor. Do not include the * Base instance parameter, this is automatically provided to the extension constructor later. * @returns The builder, now aware of this extension */ with(extClass, ...args) { const newSelf = this; newSelf._extensions.push([extClass, args]); return newSelf; } /** * Change the default method name filter for creating extension forwarding functions in the base. * * By default, this is `(m) => m.startsWith('$')`, meaning only public functions that start with * a dollar sign ($) have method forwarders constructed on the base instance. * * You can disable method forwarding entirely by just setting this to `() => false`. * @param f method name filter * @returns The builder, with modified filter. */ withMethodNameFilter(f) { this._methodNameFilter = f; return this; } make(...args) { const base = new this._base(...args); const extensions = this._extensions .map(([extClass, extraArgs]) => new extClass(base, ...extraArgs)); base.Extensions = extensions; // provide the forwarding functions in the base extensions.forEach((e, i) => { const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(e)) .filter(m => typeof e[m] === "function") .filter(m => m !== "constructor") .filter(this._methodNameFilter); methods.forEach((m) => { base[m.substring(1)] = (...args) => { return extensions[i][m](...args); }; }); }); return base; } } exports.Builder = Builder;