UNPKG

pandora

Version:

A powerful and lightweight application manager for Node.js applications powered by TypeScript.

174 lines (158 loc) 4.62 kB
'use strict'; import Messenger, {MessengerClient} from 'pandora-messenger'; import {consoleLogger, getDaemonStdoutLogPath} from '../universal/LoggerBroker'; import * as os from 'os'; import * as fs from 'fs'; import * as path from 'path'; import {spawn} from 'child_process'; import {DAEMON_MESSENGER, DAEMON_READY, SEND_DAEMON_MESSAGE} from '../const'; import {dirname} from 'path'; import mkdirp = require('mkdirp'); const is = require('is-type-of'); const tmpDir = os.tmpdir(); const pidFile = path.join(tmpDir, 'nodejs_pandora.pid'); const pathDaemonBootstrap = require.resolve('./DaemonBootstrap'); let preparedClient: MessengerClient = null; /** * Get messenger client of the daemon * @return {Client} */ function getMessengerClient() { return new Messenger.Client({ name: DAEMON_MESSENGER, }); } /** * Determine the daemon is running * @return {Promise<boolean>} */ export function isDaemonRunning(): Promise<boolean> { return new Promise((resolve, reject) => { const client = getMessengerClient(); client.once('connect', () => { resolve(true); client.close(); }); client.once('error', err => { resolve(false); }); }); }; /** * Ensure the daemon is running * @param options * @return {Promise<any>} */ export async function barrierDaemon(options?) { if (!await isDaemonRunning()) { return new Promise((resolve, reject) => { startDaemonReal(options, resolve, reject); }); } return Promise.resolve(); } export function getDaemonClient(args?: { extensionPath }): Promise<MessengerClient> { if (preparedClient) return Promise.resolve(preparedClient); return new Promise((resolve, reject) => { const client = Messenger.getClient({ name: DAEMON_MESSENGER, }); client.on('connect', () => { preparedClient = client; resolve(client); client.removeListener('error', onError); }); function onError(err) { reject(err); } client.once('error', onError); }); } /** * Send a message to the daemon, it will start the daemon automatically if that is't running. * @param command * @param args * @param callback * @param timeout * @return {Promise<void>} */ export async function send(command, args, callback, timeout?): Promise<void> { timeout = timeout || 300 * 1000; try { await barrierDaemon(); const daemonClient = await getDaemonClient({extensionPath: args.extensionPath}); daemonClient.send(SEND_DAEMON_MESSAGE, {command, args}, (err, data) => { if (err) { return callback(err, data); } if (data && data.error) { return callback(data.error, data.data); } return callback(null, data.data); }, timeout); } catch (err) { callback(err); } } /** * Exit the daemon automatically if there not remind any application. * @param code */ export function clearCliExit(code) { code = code != null ? code : 0; send('list', {}, (err, data) => { if (data && data.length === 0) { consoleLogger.info('No application remind, exit the pandora daemon automatically'); preparedClient.once('error', () => { process.exit(code); }); send('exit', {}, (err, data) => { if (err) { consoleLogger.error(data); process.exit(code); return; } consoleLogger.info(data); process.exit(code); }); } else { process.exit(code); } }); } function startDaemonReal(options, resolve, reject) { options = options || {}; if (is.function(options)) { resolve = options; options = {}; } const args = []; if (/\.ts$/.test(module.filename)) { args.push('-r', 'ts-node/register', '-r', 'nyc-ts-patch'); } args.push(pathDaemonBootstrap); const daemonStdoutPath = getDaemonStdoutLogPath(); const daemonStdoutDir = dirname(daemonStdoutPath); mkdirp.sync(daemonStdoutDir); const stdout = fs.openSync(daemonStdoutPath, 'a'); const daemon = spawn(process.execPath, args, <any> { stdio: ['ipc', stdout, stdout], env: Object.assign(process.env, { extensionPath: options.extensionPath }), detached: true, }); daemon.on('exit', function (code, signal) { const err = new Error(`daemon[${daemon.pid}] died unexpectedly with exit code ${code} , signal ${signal}`); reject(err); }); daemon.on('message', function (message) { consoleLogger.info(`pandora[${daemon.pid}] daemon started successfully.`); if (message === DAEMON_READY) { fs.writeFileSync(pidFile, daemon.pid); resolve(); } }); return daemon; }