@nxextensions/firebase
Version:
An Nx plugin for firebase applications that would like to run the emulators in conjunction with their app
176 lines • 6.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.startEmulators = startEmulators;
const devkit_1 = require("@nx/devkit");
const process = require("node:process");
const fileutils_1 = require("nx/src/utils/fileutils");
const request_1 = require("./request");
const child_process_1 = require("child_process");
const es = require("event-stream");
const fs_1 = require("fs");
const path_1 = require("path");
const chalk = require("chalk");
async function startEmulators(command, context, exportPath) {
if (await isServerUp(command, context)) {
return;
}
const killEmulators = runCommand(command, context, exportPath);
await waitForServer(command, context);
return killEmulators;
}
function isServerUp(command, context) {
const parts = command.split('--');
const onlyPart = parts.filter((p) => p.includes('only'))[0];
const firstPort = getFirstEmulatorPort(context, onlyPart);
const url = `http://localhost:${firstPort}`;
return new Promise((res) => {
return (0, request_1.default)(url, () => {
res(true);
}, () => {
res(false);
});
});
}
function getFirstEmulatorPort(context, only) {
const projectRoot = (0, devkit_1.joinPathFragments)(context.cwd, context.projectsConfigurations.projects[context.projectName].root);
let path = 'firebase.json';
if (!(0, fs_1.existsSync)('firebase.json')) {
path = (0, devkit_1.joinPathFragments)(projectRoot, path);
if (!(0, fs_1.existsSync)((0, devkit_1.joinPathFragments)(projectRoot, 'nx-firebase.json'))) {
const message = chalk.bgKeyword('orange')("The process has detected that you are running a version of this plugin that does not have the nx-firebase.json file present. This will not be supported in v2. Please run 'nx migrate @nxextensions/firebase@v1.3.0' to fix this issue");
process.stdout.write(message);
}
}
const firebaseConfig = (0, fileutils_1.readJsonFile)(path);
if (!(firebaseConfig === null || firebaseConfig === void 0 ? void 0 : firebaseConfig.emulators)) {
throw new Error(`firebase.json does not contain the necessary emulators configuration`);
}
let port;
Object.keys(firebaseConfig.emulators).forEach((emulator) => {
if (!port && firebaseConfig.emulators[emulator].port) {
if ((only && only.includes(emulator)) || !only) {
port = firebaseConfig.emulators[emulator].port;
}
}
});
return port;
}
function runCommand(command, context, exportPath) {
var _a, _b;
let cwd = process.cwd();
if (!(0, fs_1.existsSync)((0, path_1.join)(cwd, 'firebase.json'))) {
cwd = (0, devkit_1.joinPathFragments)(cwd, context.projectsConfigurations.projects[context.projectName].root);
}
const cp = (0, child_process_1.exec)(command, { cwd });
(_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
process.stdout.write(data);
});
(_b = cp.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
process.stderr.write(data);
});
return async () => {
if (process.platform === 'win32') {
if (exportPath) {
(0, child_process_1.execSync)(`firebase emulators:export ${exportPath}`);
}
await listChildProcessPID(cp.pid, (pids, err) => {
if (err) {
throw err;
}
pids.forEach((pid) => {
(0, child_process_1.exec)(`taskkill /f /pid ${pid.PID}`);
});
});
}
else {
process.kill(-cp.pid, 'SIGINT');
}
};
}
function waitForServer(command, context) {
const port = getFirstEmulatorPort(context, command);
return new Promise((res, rej) => {
let pollTimeout;
const timeout = setTimeout(() => {
clearTimeout(pollTimeout);
rej();
}, 120000);
function pollForServer() {
void (0, request_1.default)(`http://localhost:${port}`, () => {
clearTimeout(timeout);
res();
}, () => {
pollTimeout = setTimeout(pollForServer, 100);
});
}
pollForServer();
});
}
function listChildProcessPID(pid, callback) {
return new Promise((res, rej) => {
let headers = null;
if (typeof callback !== 'function') {
throw new Error('listChildProcessPID: callback must be a function');
}
if (typeof pid === 'number') {
pid = pid.toString();
}
let processLister;
if (process.platform === 'win32') {
processLister = (0, child_process_1.exec)('wmic.exe PROCESS GET Name,ProcessId,ParentProcessId,Status');
}
else {
processLister = (0, child_process_1.exec)('ps -A -o ppid,pid,stat,comm');
}
processLister.on('exit', (code) => {
if (code !== 0) {
rej(new Error('wmic.exe exited with code ' + code));
}
else {
res();
}
});
es.connect(processLister.stdout, es.split(), es.map((line, cb) => {
const columns = line.trim().split(/\s+/);
if (!headers) {
headers = columns;
headers = headers.map(normalizeHeaders);
return cb();
}
const row = {};
const h = headers.slice();
while (h.length) {
row[h.shift()] = h.length ? columns.shift() : columns.join(' ');
}
return cb(null, row);
}), es.writeArray((err, ps) => {
const parents = {}, children = [];
parents[pid] = true;
ps.forEach((p) => {
if (parents[p.PPID]) {
parents[p.PID] = true;
children.push(p);
}
});
callback(children, null);
})).on('error', (err) => {
callback([], err);
});
});
}
function normalizeHeaders(str) {
switch (str) {
case 'Name':
case 'COMM':
return 'COMMAND';
case 'ParentProcessId':
return 'PPID';
case 'ProcessId':
return 'PID';
case 'Status':
return 'STAT';
default:
return str;
}
}
//# sourceMappingURL=firebase.js.map