UNPKG

@decaf-ts/decorator-validation

Version:
231 lines 9.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Decoration = void 0; const constants_1 = require("./constants.cjs"); // eslint-disable-next-line @typescript-eslint/no-unused-vars function defaultFlavourResolver(target) { return constants_1.DefaultFlavour; } /** * @description A decorator management class that handles flavoured decorators * @summary The Decoration class provides a builder pattern for creating and managing decorators with different flavours. * It supports registering, extending, and applying decorators with context-aware flavour resolution. * The class implements a fluent interface for defining, extending, and applying decorators with different flavours, * allowing for framework-specific decorator implementations while maintaining a consistent API. * @template T Type of the decorator (ClassDecorator | PropertyDecorator | MethodDecorator) * @param {string} [flavour] Optional flavour parameter for the decorator context * @class * @category Model * @example * ```typescript * // Create a new decoration for 'component' with default flavour * const componentDecorator = new Decoration() * .for('component') * .define(customComponentDecorator); * * // Create a flavoured decoration * const vueComponent = new Decoration('vue') * .for('component') * .define(vueComponentDecorator); * * // Apply the decoration * @componentDecorator * class MyComponent {} * ``` * @mermaid * sequenceDiagram * participant C as Client * participant D as Decoration * participant R as FlavourResolver * participant F as DecoratorFactory * * C->>D: new Decoration(flavour) * C->>D: for(key) * C->>D: define(decorators) * D->>D: register(key, flavour, decorators) * D->>F: decoratorFactory(key, flavour) * F->>R: resolve(target) * R-->>F: resolved flavour * F->>F: apply decorators * F-->>C: decorated target */ class Decoration { /** * @description Static map of registered decorators * @summary Stores all registered decorators organized by key and flavour */ static { this.decorators = {}; } /** * @description Function to resolve flavour from a target * @summary Resolver function that determines the appropriate flavour for a given target */ static { this.flavourResolver = defaultFlavourResolver; } constructor(flavour = constants_1.DefaultFlavour) { this.flavour = flavour; } /** * @description Sets the key for the decoration builder * @summary Initializes a new decoration chain with the specified key * @param {string} key The identifier for the decorator * @return {DecorationBuilderMid} Builder instance for method chaining */ for(key) { this.key = key; return this; } /** * @description Adds decorators to the current context * @summary Internal method to add decorators with addon support * @param {boolean} [addon=false] Whether the decorators are addons * @param decorators Array of decorators * @return {this} Current instance for chaining */ decorate(addon = false, ...decorators) { if (!this.key) throw new Error("key must be provided before decorators can be added"); if ((!decorators || !decorators.length) && !addon && this.flavour !== constants_1.DefaultFlavour) throw new Error("Must provide overrides or addons to override or extend decaf's decorators"); if (this.flavour === constants_1.DefaultFlavour && addon) throw new Error("Default flavour cannot be extended"); this[addon ? "extras" : "decorators"] = new Set([ ...(this[addon ? "extras" : "decorators"] || new Set()).values(), ...decorators, ]); return this; } /** * @description Defines the base decorators * @summary Sets the primary decorators for the current context * @param decorators Decorators to define * @return Builder instance for finishing the chain */ define(...decorators) { return this.decorate(false, ...decorators); } /** * @description Extends existing decorators * @summary Adds additional decorators to the current context * @param decorators Additional decorators * @return {DecorationBuilderBuild} Builder instance for building the decorator */ extend(...decorators) { return this.decorate(true, ...decorators); } decoratorFactory(key, f = constants_1.DefaultFlavour) { const contextDecorator = function contextDecorator(target, propertyKey, descriptor) { const flavour = Decoration.flavourResolver(target); const cache = Decoration.decorators[key]; let decorators; const extras = cache[flavour] ? cache[flavour].extras : cache[constants_1.DefaultFlavour].extras; const extraArgs = [ ...(cache[constants_1.DefaultFlavour].extras ? cache[constants_1.DefaultFlavour].extras.values() : []), ].reduce((accum, e, i) => { if (e.args) accum[i] = e.args; return accum; }, {}); if (cache && cache[flavour] && cache[flavour].decorators && cache[flavour].decorators.size) { decorators = cache[flavour].decorators; } else { decorators = cache[constants_1.DefaultFlavour].decorators; } const decoratorArgs = [ ...cache[constants_1.DefaultFlavour].decorators.values(), ].reduce((accum, e, i) => { if (e.args) accum[i] = e.args; return accum; }, {}); const toApply = [ ...(decorators ? decorators.values() : []), ...(extras ? extras.values() : []), ]; return toApply.reduce((_, d, i) => { switch (typeof d) { case "object": { const { decorator, args, transform } = d; const argz = args || i < (decorators ? decorators.size : 0) ? decoratorArgs[i] : extraArgs[i - (decorators ? decorators.size : 0)] || (decorators ? decoratorArgs[i - decorators.size] : []); const transformed = transform ? transform(argz || []) : argz || []; return decorator(...transformed)(target, propertyKey, descriptor); } case "function": return d(target, propertyKey, descriptor); default: throw new Error(`Unexpected decorator type: ${typeof d}`); } }, { target, propertyKey, descriptor }); }; Object.defineProperty(contextDecorator, "name", { value: [f, key].join("_decorator_for_"), writable: false, }); return contextDecorator; } /** * @description Creates the final decorator function * @summary Builds and returns the decorator factory function * @return {function(any, any?, TypedPropertyDescriptor?): any} The generated decorator function */ apply() { if (!this.key) throw new Error("No key provided for the decoration builder"); Decoration.register(this.key, this.flavour, this.decorators || new Set(), this.extras); return this.decoratorFactory(this.key, this.flavour); } /** * @description Registers decorators for a specific key and flavour * @summary Internal method to store decorators in the static registry * @param {string} key Decorator key * @param {string} flavour Decorator flavour * @param [decorators] Primary decorators * @param [extras] Additional decorators */ static register(key, flavour, decorators, extras) { if (!key) { throw new Error("No key provided for the decoration builder"); } if (!decorators) throw new Error("No decorators provided for the decoration builder"); if (!flavour) throw new Error("No flavour provided for the decoration builder"); if (!Decoration.decorators[key]) Decoration.decorators[key] = {}; if (!Decoration.decorators[key][flavour]) Decoration.decorators[key][flavour] = {}; if (decorators) Decoration.decorators[key][flavour].decorators = decorators; if (extras) Decoration.decorators[key][flavour].extras = extras; } /** * @description Sets the global flavour resolver * @summary Configures the function used to determine decorator flavours * @param {FlavourResolver} resolver Function to resolve flavours */ static setFlavourResolver(resolver) { Decoration.flavourResolver = resolver; } static for(key) { return new Decoration().for(key); } static flavouredAs(flavour) { return new Decoration(flavour); } } exports.Decoration = Decoration; //# sourceMappingURL=Decoration.js.map