UNPKG

@makerx/ts-dossier

Version:

A support library to facilitate the easy creation of builders for use with an Object-Mother test pattern in TypeScript

1 lines 4.45 kB
{"version":3,"file":"data-builder.mjs","sources":["../../src/data-builder.ts"],"sourcesContent":["import { deepClone } from './deep-clone'\n\n/**\n * Data builder is an abstract class builders can inherit to make working with the {@linkplain dossierProxy} easier.\n *\n * ```typescript\n * export class ShapeBuilder extends DataBuilder<Shape> {\n * constructor() {\n * super({\n * name: randomString(10, 20),\n * sides: randomNumberBetween(1, 4),\n * colour: randomElement(['Blue', 'Red', 'Yellow', 'Green']),\n * })\n * }\n *\n * public withName(name: string) {\n * return this.with('name', name + ' Intercepted')\n * }\n * }\n *\n * export const shapeBuilder = dossierProxy<ShapeBuilder, Shape>(ShapeBuilder)\n * ```\n */\nexport abstract class DataBuilder<T> {\n protected constructor(protected thing: T) {\n this.thing = thing\n }\n\n public with<K extends keyof T>(key: K, value: T[K]): this {\n this.thing[key] = value\n return this\n }\n\n public build(): T {\n return deepClone(this.thing)\n }\n\n public clone(): this {\n return new Proxy(deepClone(this), proxyHandler)\n }\n}\n\ntype WithMethods<T extends object, TBuilder> = CoerceIntellisense<\n {\n [K in keyof T as K extends string ? `with${Capitalize<K>}` : never]-?: (d: T[K]) => WithMethods<T, TBuilder> & TBuilder\n } & TBuilder\n>\n\nexport type CoerceIntellisense<T> = T extends infer O ? { [K in keyof O]: O[K] } : never\n\nconst proxyHandler = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n get: function (target: any, prop: string, receiver: any) {\n if (!target[prop] && prop.startsWith('with')) {\n const propertyName = prop[4].toLocaleLowerCase() + prop.substring(5)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (value: any) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n target.with(propertyName as any, value)\n return receiver\n }\n }\n return Reflect.get(target, prop, receiver)\n },\n}\n\ntype DynamicDataBuilder<TDataBuilder, TData extends object> = TDataBuilder & WithMethods<TData, TDataBuilder>\n\n/**\n * The proxy builder allows one to easily create a builder and have a [proxy]{@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/proxy/proxy}\n * instance handle any with* methods not specifically declared by the builder itself.\n *\n * ```typescript\n * class ShapeBuilder extends DataBuilder<Shape> {\n * constructor() {\n * super({\n * name: randomString(10, 20),\n * sides: randomNumberBetween(1, 4),\n * colour: randomElement(['Blue', 'Red', 'Yellow', 'Green']),\n * })\n * }\n *\n * public withName(name: string) {\n * return this.with('name', name + ' Intercepted')\n * }\n * }\n *\n * const shapeBuilder = dossierProxy<ShapeBuilder, Shape>(ShapeBuilder)\n *\n * const shape = shapeBuilder().withName('Square').withSides(4).withColour('Red').build()\n *\n * console.log(shape) // Outputs { name: 'Square Intercepted', sides: 4, colour: 'Red' }\n * ```\n * @param builder The constructor to call to create a new instance of the builder.\n */\nexport function dossierProxy<TDataBuilder, TData extends object>(\n builder: new () => TDataBuilder,\n): () => DynamicDataBuilder<TDataBuilder, TData> {\n return () => new Proxy(new builder(), proxyHandler)\n}\n"],"names":[],"mappings":";;AAEA;;;;;;;;;;;;;;;;;;;;AAoBG;MACmB,WAAW,CAAA;AACC,IAAA,KAAA;AAAhC,IAAA,WAAA,CAAgC,KAAQ,EAAA;QAAR,IAAK,CAAA,KAAA,GAAL,KAAK;AACnC,QAAA,IAAI,CAAC,KAAK,GAAG,KAAK;;IAGb,IAAI,CAAoB,GAAM,EAAE,KAAW,EAAA;AAChD,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK;AACvB,QAAA,OAAO,IAAI;;IAGN,KAAK,GAAA;AACV,QAAA,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;;IAGvB,KAAK,GAAA;QACV,OAAO,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;;AAElD;AAUD,MAAM,YAAY,GAAG;;AAEnB,IAAA,GAAG,EAAE,UAAU,MAAW,EAAE,IAAY,EAAE,QAAa,EAAA;AACrD,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE;AAC5C,YAAA,MAAM,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;;YAEpE,OAAO,CAAC,KAAU,KAAI;;AAEpB,gBAAA,MAAM,CAAC,IAAI,CAAC,YAAmB,EAAE,KAAK,CAAC;AACvC,gBAAA,OAAO,QAAQ;AACjB,aAAC;;QAEH,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;KAC3C;CACF;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AACG,SAAU,YAAY,CAC1B,OAA+B,EAAA;AAE/B,IAAA,OAAO,MAAM,IAAI,KAAK,CAAC,IAAI,OAAO,EAAE,EAAE,YAAY,CAAC;AACrD;;;;"}