xvfb-ts
Version:
Easily start and stop an X Virtual Frame Buffer from your node app
106 lines • 3.19 kB
JavaScript
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