UNPKG

@reflet/express

Version:

Well-defined and well-typed express decorators

225 lines (224 loc) 7.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractRouterMeta = exports.ScopedMiddlewares = exports.Router = exports.DYNAMIC_PATH = void 0; const metadata_map_1 = require("./metadata-map"); const METAKEY_ROUTER = Symbol('router'); /** * Token to use in place of `Router` path parameter, signaling that the path is defined at registration. * @internal */ exports.DYNAMIC_PATH = Symbol('dynamic-path'); /** * Attaches an express Router to a class. * * The routes will be attached to the router at its root `path`. * * @param path - root path of the router. * @param options - specifies router behavior. * * @see https://expressjs.com/en/4x/api.html#router * @example * ```ts * @Router('/things') * class Foo { * @Get() * list(req: Request, res: Response, next: NextFunction) {} * * @Get('/:id') * getOne(req: Request, res: Response, next: NextFunction) {} * } * ``` * ------ * * @public */ function Router(path, options) { return (target) => { const existingRouterMeta = extractRouterMeta(target); if (existingRouterMeta) { existingRouterMeta.path = path; existingRouterMeta.options = options; } else { const newRouterMeta = { path, options }; defineRouterMeta(newRouterMeta, target); } }; } exports.Router = Router; (function (Router) { /** * Attaches children routers to a parent router, to have nested routers. * * @param register - function that should return an array of routers. * * @example * ```ts * @Router('/foo') * @Router.Children(() => [NestedRouter]) * class ParentRouter {} * * @Router('/bar') * class NestedRouter {} * ``` * ------ * @public */ function Children(register) { return (target) => { const existingRouterMeta = extractRouterMeta(target); if (existingRouterMeta) { existingRouterMeta.children = register; existingRouterMeta.childrenDeps = []; } else { const newRouterMeta = { path: null, children: register, childrenDeps: [], }; defineRouterMeta(newRouterMeta, target); } // No need to intercept constructor if no dependency. if (!target.length || !register.length) { return; } // Intercept the constructor to retrieve dependencies and pass them down to children. // https://stackoverflow.com/questions/34411546/how-to-properly-wrap-constructors-with-decorators-in-typescript return new Proxy(target, { construct(targett, args, newTarget) { const routerMeta = extractRouterMeta(target); routerMeta.childrenDeps = args; return new target(...args); }, }); }; } Router.Children = Children; /** * Attaches an express Router to a class, without defining a root path. * The root path must be defined at registration. * * Useful for dynamic nested routers. * * @example * ```ts * @Router.Dynamic() * class Items { * @Get() * list(req: Request, res: Response, next: NextFunction) {} * } * * @Router('/foo') * class Foo { * constructor() { * register(this, [['/items', Items]]) * } * } * * @Router('/bar') * class Bar { * constructor() { * register(this, [['/elements', Items]]) * } * } * ``` * ------ * @public */ function Dynamic(options) { return (target) => { const routerMeta = { path: exports.DYNAMIC_PATH, options }; defineRouterMeta(routerMeta, target); }; } Router.Dynamic = Dynamic; })(Router = exports.Router || (exports.Router = {})); function ScopedMiddlewares(targetMaybe) { if (targetMaybe) { const existingRouterMeta = extractRouterMeta(targetMaybe); if (existingRouterMeta) { existingRouterMeta.scopedMiddlewares = true; } else { const newRouterMeta = { path: null, scopedMiddlewares: true, }; defineRouterMeta(newRouterMeta, targetMaybe); } } else { return (target) => { const existingRouterMeta = extractRouterMeta(target); if (existingRouterMeta) { existingRouterMeta.scopedMiddlewares = true; } else { const newRouterMeta = { path: null, scopedMiddlewares: true, }; defineRouterMeta(newRouterMeta, target); } }; } } exports.ScopedMiddlewares = ScopedMiddlewares; (function (ScopedMiddlewares) { function Dont(targetMaybe, keyMaybe) { if (targetMaybe) { const existingRouterMeta = extractRouterMeta(targetMaybe); if (existingRouterMeta) { existingRouterMeta.scopedMiddlewares = false; } else { const newRouterMeta = { path: null, scopedMiddlewares: false, }; defineRouterMeta(newRouterMeta, targetMaybe); } } else { return (target) => { const existingRouterMeta = extractRouterMeta(target); if (existingRouterMeta) { existingRouterMeta.scopedMiddlewares = false; } else { const newRouterMeta = { path: null, scopedMiddlewares: false, }; defineRouterMeta(newRouterMeta, target); } }; } } ScopedMiddlewares.Dont = Dont; })(ScopedMiddlewares = exports.ScopedMiddlewares || (exports.ScopedMiddlewares = {})); /** * @internal */ function extractRouterMeta(target, appClass) { const appMeta = appClass ? (0, metadata_map_1.getOwnMetadata)(METAKEY_ROUTER, appClass.prototype) : undefined; const routerMeta = (0, metadata_map_1.getOwnMetadata)(METAKEY_ROUTER, target.prototype); if (!appMeta || !routerMeta) { return routerMeta; } if (routerMeta.scopedMiddlewares == null && appMeta.scopedMiddlewares != null) { routerMeta.scopedMiddlewares = appMeta.scopedMiddlewares; } return routerMeta; } exports.extractRouterMeta = extractRouterMeta; /** * @internal */ function defineRouterMeta(routerMeta, target) { // Attached to the prototype, because the constructor might be replaced with a proxy. (0, metadata_map_1.defineMetadata)(METAKEY_ROUTER, routerMeta, target.prototype); }