import * as Express from 'express' ; import 'reflect-metadata' ; export class Route { method : string ; path : string ; key : string | symbol ; handlers : ( Express . Handler | Express . RequestParamHandler ) [ ] ; } export type Middleware = Express . Handler | string | symbol ; export const routesKey = Symbol ( 'routesKey' ) ; export const basePathKey = Symbol ( 'basePathKey' ) ; export function getRouteMetadata ( target ) { let routes = Reflect . getMetadata ( routesKey , target ) ; if ( ! routes ) { routes = [ ] ; Reflect . defineMetadata ( routesKey , routes , target ) ; } return routes ; } export function route ( method , path , middleware = [ ] ) { return < T extends Express . Handler > ( target , key , descriptor ) => { const routes = getRouteMetadata ( target ) ; const handlers = < Express . Handler [ ] > middleware . map ( m => getMiddleware ( target , m ) ) ; routes . push ( { method , path , key , handlers : [ ... handlers , descriptor . value ] } ) ; return descriptor ; } ; } export function basePath ( path ) { return Reflect . metadata ( basePathKey , path ) ; } export function get ( path = '*' , middleware = [ ] ) { return route ( 'get' , path , middleware ) ; } export function post ( path = '*' , middleware = [ ] ) { return route ( 'post' , path , middleware ) ; } export function put ( path = '*' , middleware = [ ] ) { return route ( 'put' , path , middleware ) ; } export function patch ( path = '*' , middleware = [ ] ) { return route ( 'patch' , path , middleware ) ; } export function del ( path = '*' , middleware = [ ] ) { return route ( 'del' , path , middleware ) ; } export function options ( path = '*' , middleware = [ ] ) { return route ( 'options' , path , middleware ) ; } export function head ( path = '*' , middleware = [ ] ) { return route ( 'head' , path , middleware ) ; } export function use ( path = '*' ) { return route ( 'use' , path ) ; } export function all ( path = '*' , middleware = [ ] ) { return route ( 'all' , path , middleware ) ; } export function param ( param ) { return < T extends Express . RequestParamHandler > ( target , key , descriptor ) => { const routes = getRouteMetadata ( target ) ; const handler = descriptor . value ; routes . push ( { method : 'param' , path : param , key , handlers : [ function ( request , response , next , value , name ) { return Promise . resolve ( handler . call ( this , request , response , next , value , name ) ) . then ( null , next ) ; } ] } ) ; return descriptor ; } ; } export function middleware ( fn ) { return < T extends Express . Handler > ( target , key , descriptor ) => { const routes = getRouteMetadata ( target ) ; const middleware = getMiddleware ( target , fn ) ; routes . push ( { method : null , path : null , key , handlers : [ middleware ] } ) ; return descriptor ; } ; } function getMiddleware ( target , fn ) { if ( fn instanceof Function ) { return fn ; } else { const middleware = function ( ) { const func = this [ fn ] ; if ( ! func ) { throw new Error ( `could not find middleware function ${ fn } on ${ target . constructor . name }` ) ; } func . apply ( this , arguments ) ; } ; return middleware ; } } function trimslash ( s ) { return s [ s . length - 1 ] === '/' ? s . slice ( 0 , s . length - 1 ) : s ; } export function getRoutes ( target ) { let routes = Reflect . getMetadata ( routesKey , target ) || [ ] ; const basePath = Reflect . getMetadata ( basePathKey , target . constructor ) ; if ( basePath ) { routes = routes . map ( ( { method , path , key , handlers } ) => ( { method , path : method === 'param' ? path : trimslash ( basePath ) + path , key , handlers } ) ) ; } const groups = routes . reduce ( ( groups , route ) => { if ( ! groups [ route . key ] ) groups [ route . key ] = [ ] ; groups [ route . key ] . push ( route ) ; return groups ; } , { } ) ; routes = [ ] ; for ( const k in groups ) { const group = groups [ k ] ; const middleware = group . filter ( x => x . method === null ) . map ( ( { handlers } ) => handlers [ 0 ] ) ; const notMiddleware = group . filter ( x => x . method !== null ) . map ( ( { method , path , key , handlers } ) => ( { method , path , key , handlers : [ ... middleware , ... handlers ] . map ( h => h . bind ( target ) ) } ) ) ; [ ] . push . apply ( routes , notMiddleware ) ; } return routes ; } export function register ( router , target ) { const routes = getRoutes ( target ) ; for ( const route of routes ) { const handlers = route . method !== 'param' ? route . handlers . map ( ( handler ) => ( request , response , next ) => Promise . resolve ( handler ( request , response , next ) ) . then ( null , next ) ) : route . handlers ; const args = [ route . path , ... handlers ] ; router [ route . method ] . apply ( router , args ) ; } }