UNPKG

3.04 kBPlain TextView Raw
1import * as express from 'express';
2import * as cors from 'cors';
3import * as cookieParser from 'cookie-parser';
4import * as bodyParser from 'body-parser';
5import { R, log, constants, fsPath } from '../libs';
6
7const listEndpoints = require('express-list-endpoints');
8
9export const Router = express.Router;
10export { staticRoutes } from './middleware/staticRoutes';
11
12export type Request = express.Request;
13export type Response = express.Response;
14
15const IS_PRODUCTION = process.env.NODE_ENV === 'production';
16
17interface IErrorStack {
18 message: string;
19 stack: string[];
20}
21
22/**
23 * Turns an Error stack into a friendlier JSON value.
24 */
25function formatErrorStack(stack: string = ''): IErrorStack {
26 const lines = stack.split('\n');
27 const message = lines[0];
28 lines.shift();
29 return {
30 message,
31 stack: lines.map(line => line.trim()),
32 };
33}
34
35/**
36 * Sends an HTTP error to the client, with full stack details
37 * if running locally in development.
38 */
39export function sendError(code: number, res: express.Response, err: Error) {
40 const error = IS_PRODUCTION ? err.message : formatErrorStack(err.stack);
41 res.status(code).send({
42 status: code,
43 error,
44 });
45}
46
47/**
48 * Create an express app.
49 */
50export function app(
51 options: {
52 cors?: cors.CorsOptions;
53 json?: bodyParser.OptionsJson;
54 static?: string;
55 } = {},
56) {
57 const app = express()
58 .use(cors(options.cors))
59 .use(bodyParser.json(options.json) as any) // HACK: avoid type incompatibility.
60 .use(cookieParser());
61
62 if (options.static) {
63 app.use(express.static(options.static));
64 }
65
66 return app;
67}
68
69/**
70 * Creates an express router.
71 * NB:
72 * This is a helper that makes it easier to create
73 * and export a router without the annoying typescript
74 * errors complaining about the `express-serve-static-core`
75 * reference requirement.
76 */
77export function router() {
78 const routes = express.Router();
79 return routes as express.Router;
80}
81
82/**
83 * Retrieves a list of all routes that have been registered.
84 */
85export function routes(router: express.Router): RouteInfo[] {
86 const items = listEndpoints(router) as RouteInfo[];
87 return items.reduce(
88 (acc, next) => {
89 const index = acc.findIndex(r => r.path === next.path);
90 if (index > -1) {
91 // Route already exist, merge in the methods.
92 const route = items[index];
93 route.methods = R.uniq([...route.methods, ...next.methods]);
94 return acc;
95 }
96 return [...acc, next];
97 },
98 [] as RouteInfo[],
99 );
100}
101export type RouteInfo = {
102 path: string;
103 methods: Array<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'>;
104};
105
106/**
107 * Standard logger for when the express application starts.
108 */
109export function logStarted(port: number, args: {} = {}) {
110 const PACKAGE = require(fsPath.resolve('./package.json'));
111 log.info(`\n> Ready on ${log.cyan('localhost')}:${log.magenta(port)}`);
112 log.info();
113 log.info.gray(` name: ${log.white(PACKAGE.name)}@${PACKAGE.version}`);
114 log.info.gray(` dev: ${constants.IS_DEV}`);
115 log.info();
116}