import { createLogger } from '@gongt/ts-stl-library/debug/create-logger'; import { LOG_LEVEL } from '@gongt/ts-stl-library/debug/levels'; import { Application } from 'express-serve-static-core'; import { createServer, Server } from 'http'; import { createWaiter, IWaitter } from '../boot/wait'; import { getWebAppListenPort } from '../communication/listen'; import { globalWorkingCallback } from '../boot/gracefull'; const info = createLogger(LOG_LEVEL.INFO, 'express.load'); const sill = createLogger(LOG_LEVEL.SILLY, 'express.load'); export type ExpressApplicationCreator = () => Application; let wait: IWaitter; let waitPromise: Promise; function initWaiter() { if (!wait) { sill('create express boot waiter.'); waitPromise = new Promise((resolve, reject) => { wait = createWaiter((err) => { sill('express boot wait complete.'); wait = null; waitPromise = null; if (err) { reject(err); } else { resolve(); } }); }); } } export function expressWait(what: string, promise: Promise) { sill('expressWait(%s)', what); initWaiter(); wait(what, promise); } export function bootExpressApp(appOrCreate: Application|ExpressApplicationCreator): Promise { let app: Application; if (appOrCreate.constructor === Function) { sill('bootExpressApp(creator: %s)', appOrCreate.name); app = (appOrCreate)(); } else { sill('bootExpressApp(application)'); app = (appOrCreate); } process.emit('express:build' as any, app as any); if (wait) { return waitPromise.then(() => { wait = null; return bootExpressAppInternal(app); }); } else { return bootExpressAppInternal(app); } } function bootExpressAppInternal(app: Application): Promise { const appPort = getWebAppListenPort(); sill('express will listen to port: %s', appPort); return new Promise((resolve, reject) => { app.set('port', appPort); process.emit('express:start' as any, app as any); const server = createServer(app); server.listen(appPort); globalWorkingCallback('express-server@' + appPort, server); server.on('error', onError); server.on('listening', onListening); /** * Event listener for HTTP server "error" event. */ function onError(error) { if (error.syscall !== 'listen') { return reject(error); } const bind = typeof appPort === 'string' ? 'Pipe ' + appPort : 'Port ' + appPort; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); break; } reject(error); } /** * Event listener for HTTP server "listening" event. */ function onListening() { const addr = server.address(); const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; info('express is up, listen on port \x1B[38;5;10m%s\x1B[0m', bind); process.emit('express:listen' as any, server as any); resolve(server); } }); }