@reflet/express
Version:
Well-defined and well-typed express decorators
225 lines (224 loc) • 7.05 kB
JavaScript
;
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);
}