UNPKG

xvfb-ts

Version:

Easily start and stop an X Virtual Frame Buffer from your node app

106 lines 3.19 kB
import { spawn } from 'node:child_process'; import fs from 'node:fs'; async function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } class Xvfb { _display; _reuse; _timeout; _silent; _xvfb_args; _process; _oldDisplay; constructor(options = {}) { this._display = options.displayNum || options.displayNum === 0 ? `:${options.displayNum}` : null; this._reuse = options.reuse ?? false; this._timeout = options.timeout ?? 500; this._silent = options.silent ?? false; this._xvfb_args = options.xvfb_args || []; } async start() { if (this._process) { return this._process; } const lockFile = this._lockFile(); this._setDisplayEnvVariable(); this._spawnProcess(fs.existsSync(lockFile)); let totalTime = 0; while (!fs.existsSync(lockFile)) { if (totalTime > this._timeout) { throw new Error('Could not start Xvfb.'); } await sleep(10); totalTime += 10; } return this._process; } async stop() { if (!this._process) { return; } this._killProcess(); this._restoreDisplayEnvVariable(); const lockFile = this._lockFile(); let totalTime = 0; while (fs.existsSync(lockFile)) { if (totalTime > this._timeout) { throw new Error('Could not stop Xvfb.'); } await sleep(10); totalTime += 10; } } display() { if (this._display) { return this._display; } let displayNum = 98; let lockFile; do { displayNum++; lockFile = this._lockFile(displayNum); } while (!this._reuse && fs.existsSync(lockFile)); this._display = `:${displayNum}`; return this._display; } _setDisplayEnvVariable() { this._oldDisplay = process.env.DISPLAY; process.env.DISPLAY = this.display(); } _restoreDisplayEnvVariable() { process.env.DISPLAY = this._oldDisplay; } _spawnProcess(lockFileExists) { const display = this.display(); if (lockFileExists) { if (!this._reuse) { throw new Error(`Display ${display} is already in use and the "reuse" option is false.`); } } else { this._process = spawn('Xvfb', [display].concat(this._xvfb_args)); if (!this._process || !this._process.stderr) { throw new Error('Could not start Xvfb.'); } this._process.stderr.on('data', (data) => { if (!this._silent) { process.stderr.write(data); } }); } } _killProcess() { if (!this._process) { return; } this._process.kill(); this._process = undefined; } _lockFile(displayNum) { return `/tmp/.X${displayNum || this.display().toString().replace(/^:/, '')}-lock`; } } export { Xvfb }; //# sourceMappingURL=index.js.map