Comparing version 14.0.4 to 14.1.0
#!/usr/bin/env node | ||
import { loadConfig } from '../lib/config.js'; | ||
import { loadConfig, removeConfig } from '../lib/config.js'; | ||
import { createResolver } from '../lib/resolver.js'; | ||
import { forwardToDaemon } from '../lib/forwarder.js'; | ||
import { forwardToDaemon, isAlive } from '../lib/forwarder.js'; | ||
import { launchDaemon, stopDaemon } from '../lib/launcher.js'; | ||
@@ -34,7 +34,13 @@ import { filesHash } from '../lib/hash.js'; | ||
case 'start': | ||
if (config) { | ||
if (await isAlive(config)) { | ||
console.log('eslint_d: Already running'); | ||
} else { | ||
await launchDaemon(resolver, hash); | ||
return; | ||
} | ||
if (config) { | ||
await removeConfig(resolver); | ||
} | ||
await launchDaemon(resolver, hash); | ||
return; | ||
@@ -41,0 +47,0 @@ case 'stop': |
import net from 'node:net'; | ||
import supportsColor from 'supports-color'; | ||
import { removeConfig } from '../lib/config.js'; | ||
import { LINT_COMMAND, PING_COMMAND } from './commands.js'; | ||
import { launchDaemon } from './launcher.js'; | ||
/** | ||
* @import { Config} from '../lib/config.js' | ||
* @import { Resolver} from '../lib/resolver.js' | ||
* @import { Config } from './config.js' | ||
* @import { Resolver } from './resolver.js' | ||
*/ | ||
@@ -14,2 +16,31 @@ | ||
/** | ||
* @param {Config | null} config | ||
* @returns {Promise<boolean>} | ||
*/ | ||
export function isAlive(config) { | ||
return forwardCommandToDaemon(config, PING_COMMAND) | ||
.then(() => true) | ||
.catch(() => false); | ||
} | ||
/** | ||
* @param {Config | null} config | ||
* @param {string} command | ||
* @returns {Promise<void>} | ||
*/ | ||
export async function forwardCommandToDaemon(config, command) { | ||
if (!config) { | ||
return Promise.reject(new Error('config not found')); | ||
} | ||
const socket = await connectToDaemon(config); | ||
socket.write(JSON.stringify([config.token, command])); | ||
socket.end(); | ||
return new Promise((resolve, reject) => { | ||
socket.on('end', () => resolve()).on('error', (err) => reject(err)); | ||
}); | ||
} | ||
/** | ||
* @param {Resolver} resolver | ||
@@ -42,5 +73,15 @@ * @param {Config} config | ||
const socket = net.connect(config.port, '127.0.0.1'); | ||
let socket; | ||
try { | ||
socket = await createSocket(true); | ||
} catch (err) { | ||
console.error(`eslint_d: ${err}`); | ||
// eslint-disable-next-line require-atomic-updates | ||
process.exitCode = 1; | ||
return; | ||
} | ||
const args = [ | ||
config.token, | ||
LINT_COMMAND, | ||
stdout ? stdout.level : 0, | ||
@@ -95,12 +136,35 @@ process.cwd(), | ||
}) | ||
.on('error', async (err) => { | ||
.on('error', (err) => { | ||
console.error(`eslint_d: ${err}`); | ||
process.exitCode = 1; | ||
}); | ||
/** | ||
* @param {boolean} retry | ||
* @returns {Promise<net.Socket>} | ||
*/ | ||
async function createSocket(retry) { | ||
try { | ||
return await connectToDaemon(config); | ||
} catch (/** @type {any} */ err) { | ||
if (err['code'] === 'ECONNREFUSED' && retry) { | ||
await removeConfig(resolver); | ||
// @ts-expect-error we check for nullability in the line below | ||
// eslint-disable-next-line require-atomic-updates | ||
config = await launchDaemon(resolver, config.hash); | ||
if (!config) { | ||
throw new Error('unable to start daemon'); | ||
} | ||
return createSocket(false); | ||
} | ||
if (err['code'] === 'ECONNREFUSED') { | ||
console.error(`eslint_d: ${err} - removing config`); | ||
await removeConfig(resolver); | ||
} else { | ||
console.error(`eslint_d: ${err}`); | ||
throw new Error(`${err.message} - removing config`); | ||
} | ||
process.exitCode = 1; | ||
}); | ||
throw err; | ||
} | ||
} | ||
/** | ||
@@ -119,2 +183,18 @@ * @returns {string} | ||
/** | ||
* @param {Config} config | ||
* @returns {Promise<net.Socket>} | ||
*/ | ||
function connectToDaemon(config) { | ||
return new Promise((resolve, reject) => { | ||
const socket = net.connect(config.port, '127.0.0.1'); | ||
socket | ||
.on('connect', () => resolve(socket)) | ||
.on('connectionAttemptFailed', (err) => { | ||
reject(err); | ||
socket.end(); | ||
}); | ||
}); | ||
} | ||
function readStdin() { | ||
@@ -121,0 +201,0 @@ return new Promise((resolve, reject) => { |
import net from 'node:net'; | ||
import child_process from 'node:child_process'; | ||
import EventEmitter from 'node:events'; | ||
import { PassThrough } from 'node:stream'; | ||
import fs from 'node:fs/promises'; | ||
import fs from 'node:fs'; | ||
import fs_promises from 'node:fs/promises'; | ||
import supportsColor from 'supports-color'; | ||
import { assert, refute, match, sinon } from '@sinonjs/referee-sinon'; | ||
import { forwardToDaemon } from './forwarder.js'; | ||
import { forwardToDaemon, isAlive } from './forwarder.js'; | ||
import { createResolver } from './resolver.js'; | ||
import { LINT_COMMAND } from './commands.js'; | ||
@@ -19,2 +23,5 @@ describe('lib/forwarder', () => { | ||
let child; | ||
let read_file_promise; | ||
function returnThis() { | ||
@@ -24,3 +31,10 @@ // @ts-ignore | ||
} | ||
let watcher; | ||
beforeEach(() => { | ||
watcher = new EventEmitter(); | ||
watcher['close'] = sinon.fake(); | ||
sinon.replace(fs, 'watch', sinon.fake.returns(watcher)); | ||
}); | ||
function fakeStdin(text) { | ||
@@ -43,2 +57,46 @@ const stdin = new PassThrough(); | ||
beforeEach(() => { | ||
read_file_promise = sinon.promise(); | ||
sinon.replace( | ||
fs_promises, | ||
'readFile', | ||
sinon.fake.returns(read_file_promise) | ||
); | ||
child = { unref: sinon.fake() }; | ||
sinon.replace(child_process, 'spawn', sinon.fake.returns(child)); | ||
}); | ||
context('isAlive', () => { | ||
it('connects to 127.0.0.1 on port from config', () => { | ||
isAlive(config); | ||
assert.calledOnceWith(net.connect, config.port, '127.0.0.1'); | ||
}); | ||
it('handles response without errors', async () => { | ||
const chunks = []; | ||
sinon.replace( | ||
socket, | ||
'read', | ||
sinon.fake(() => (chunks.length ? chunks.shift() : null)) | ||
); | ||
const result = isAlive(config); | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
assert.equals(await result, true); | ||
}); | ||
it('handles ECONNREFUSED', async () => { | ||
const result = isAlive(config); | ||
const err = new Error('Connection refused'); | ||
err['code'] = 'ECONNREFUSED'; | ||
socket.on.getCall(1).callback(err); // error | ||
assert.equals(await result, false); | ||
}); | ||
}); | ||
context('forwardToDaemon', () => { | ||
@@ -51,10 +109,12 @@ it('connects to 127.0.0.1 on port from config', () => { | ||
it('writes token, color level, cwd and argv to socket', () => { | ||
it('writes token, color level, cwd and argv to socket', async () => { | ||
sinon.replace(process, 'cwd', sinon.fake.returns('the/cwd')); | ||
forwardToDaemon(resolver, config); | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
assert.calledOnceWith( | ||
socket.write, | ||
`["token",${color_level},"the/cwd",[${JSON.stringify(process.argv0)},"eslint_d"]]` | ||
`["token","${LINT_COMMAND}",${color_level},"the/cwd",[${JSON.stringify(process.argv0)},"eslint_d"]]` | ||
); | ||
@@ -90,2 +150,4 @@ assert.calledOnce(socket.end); | ||
await new Promise(setImmediate); | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
@@ -97,3 +159,3 @@ assert.calledThrice(socket.write); | ||
it('forwards socket response to stdout', () => { | ||
it('forwards socket response to stdout', async () => { | ||
const chunks = ['response ', 'from daemon']; | ||
@@ -108,4 +170,6 @@ sinon.replace( | ||
forwardToDaemon(resolver, config); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -118,3 +182,43 @@ assert.calledThrice(process.stdout.write); | ||
it('handles "EXIT000" from response', () => { | ||
it('launch daemon on first connection refused', async () => { | ||
const chunks = ['response ', 'from daemon']; | ||
sinon.replace( | ||
socket, | ||
'read', | ||
sinon.fake(() => (chunks.length ? chunks.shift() : null)) | ||
); | ||
sinon.replace(process.stdout, 'write', sinon.fake()); | ||
sinon.replace(fs_promises, 'unlink', sinon.fake.resolves()); | ||
forwardToDaemon(resolver, config); | ||
const err = new Error('Connection refused'); | ||
err['code'] = 'ECONNREFUSED'; | ||
socket.on.getCall(1).callback(err); // connectionAttemptFailed | ||
await new Promise(setImmediate); | ||
// removeConfig | ||
assert.calledOnceWith(fs_promises.unlink, `${resolver.base}/.eslint_d`); | ||
await new Promise(setImmediate); | ||
// spawn | ||
assert.calledOnce(child_process.spawn); | ||
// watch config | ||
watcher.emit('change', 'rename', '.eslint_d'); | ||
await new Promise(setImmediate); | ||
// read config | ||
read_file_promise.resolve(JSON.stringify(config)); | ||
await new Promise(setImmediate); | ||
assert.calledTwice(net.connect); | ||
socket.on.getCall(2).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(4).callback(); // readable | ||
socket.on.getCall(5).callback(); // end | ||
assert.calledThrice(process.stdout.write); | ||
assert.calledWith(process.stdout.write, 're'); | ||
assert.calledWith(process.stdout.write, 'sponse from'); | ||
assert.calledWith(process.stdout.write, ' daemon'); | ||
}); | ||
it('handles "EXIT000" from response', async () => { | ||
const chunks = ['response from daemonEXIT000']; | ||
@@ -129,4 +233,6 @@ sinon.replace( | ||
forwardToDaemon(resolver, config); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -138,3 +244,3 @@ assert.calledWith(process.stdout.write, 'response from daemon'); | ||
it('handles "EXIT001" from response', () => { | ||
it('handles "EXIT001" from response', async () => { | ||
const chunks = ['response from daemonEXIT001']; | ||
@@ -149,4 +255,6 @@ sinon.replace( | ||
forwardToDaemon(resolver, config); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -158,3 +266,3 @@ assert.calledWith(process.stdout.write, 'response from daemon'); | ||
it('handles "EXIT123" from response', () => { | ||
it('handles "EXIT123" from response', async () => { | ||
const chunks = ['response from daemonEXIT123']; | ||
@@ -169,4 +277,6 @@ sinon.replace( | ||
forwardToDaemon(resolver, config); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -178,3 +288,3 @@ assert.calledWith(process.stdout.write, 'response from daemon'); | ||
it('handles "EXIT001" inside response', () => { | ||
it('handles "EXIT001" inside response', async () => { | ||
const chunks = ['response EXIT001', ' from daemonEXIT002']; | ||
@@ -189,4 +299,6 @@ sinon.replace( | ||
forwardToDaemon(resolver, config); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -199,3 +311,3 @@ assert.calledWith(process.stdout.write, 'response '); | ||
it('logs error and sets exitCode to 1 if response does not end with EXIT marker', () => { | ||
it('logs error and sets exitCode to 1 if response does not end with EXIT marker', async () => { | ||
const chunks = ['response from daemon']; | ||
@@ -210,4 +322,6 @@ sinon.replace( | ||
forwardToDaemon(resolver, config); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -220,15 +334,17 @@ assert.calledWith(process.stdout.write, 'response from'); | ||
it('logs error from stream', () => { | ||
sinon.replace(fs, 'unlink', sinon.fake.resolves()); | ||
it('logs error from stream', async () => { | ||
sinon.replace(fs_promises, 'unlink', sinon.fake.resolves()); | ||
forwardToDaemon(resolver, config); | ||
socket.on.thirdCall.callback(new Error('Ouch!')); // error | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(4).callback(new Error('Ouch!')); // error | ||
assert.calledOnceWith(console.error, 'eslint_d: Error: Ouch!'); | ||
assert.equals(process.exitCode, 1); | ||
refute.called(fs.unlink); | ||
refute.called(fs_promises.unlink); | ||
}); | ||
it('logs ECONNREFUSED error from stream and removes config', () => { | ||
sinon.replace(fs, 'unlink', sinon.fake.resolves()); | ||
it('logs error on second ECONNREFUSED', async () => { | ||
sinon.replace(fs_promises, 'unlink', sinon.fake.resolves()); | ||
@@ -238,4 +354,21 @@ forwardToDaemon(resolver, config); | ||
err['code'] = 'ECONNREFUSED'; | ||
socket.on.thirdCall.callback(err); // error | ||
socket.on.getCall(1).callback(err); // connectionAttemptFailed | ||
await new Promise(setImmediate); | ||
// removeConfig | ||
assert.calledOnceWith(fs_promises.unlink, `${resolver.base}/.eslint_d`); | ||
await new Promise(setImmediate); | ||
// spawn | ||
assert.calledOnce(child_process.spawn); | ||
// watch config | ||
watcher.emit('change', 'rename', '.eslint_d'); | ||
await new Promise(setImmediate); | ||
// read config | ||
read_file_promise.resolve(JSON.stringify(config)); | ||
await new Promise(setImmediate); | ||
assert.calledTwice(net.connect); | ||
socket.on.getCall(3).callback(err); // connectionAttemptFailed | ||
await new Promise(setImmediate); | ||
assert.calledOnceWith( | ||
@@ -246,3 +379,3 @@ console.error, | ||
assert.equals(process.exitCode, 1); | ||
assert.calledOnceWith(fs.unlink, `${resolver.base}/.eslint_d`); | ||
assert.calledTwice(fs_promises.unlink); | ||
}); | ||
@@ -273,2 +406,4 @@ | ||
await new Promise(setImmediate); | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
@@ -278,3 +413,3 @@ assert.calledThrice(socket.write); | ||
socket.write, | ||
`["token",${color_level},"cwd",[${JSON.stringify(process.argv0)},"eslint_d","--stdin","--fix-dry-run","--format","json","--other","--options"]]` | ||
`["token","${LINT_COMMAND}",${color_level},"cwd",[${JSON.stringify(process.argv0)},"eslint_d","--stdin","--fix-dry-run","--format","json","--other","--options"]]` | ||
); | ||
@@ -298,4 +433,6 @@ assert.calledWith(socket.write, '\n'); | ||
await new Promise(setImmediate); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -319,4 +456,6 @@ assert.calledWith(process.stdout.write, 'response from daemon'); | ||
await new Promise(setImmediate); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -340,4 +479,6 @@ assert.calledWith(process.stdout.write, 'text from stdin'); | ||
await new Promise(setImmediate); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -367,4 +508,6 @@ assert.calledWith(process.stdout.write, 'text from stdin'); | ||
await new Promise(setImmediate); | ||
socket.on.firstCall.callback(); // readable | ||
socket.on.secondCall.callback(); // end | ||
socket.on.getCall(0).callback(); // connect | ||
await new Promise(setImmediate); | ||
socket.on.getCall(2).callback(); // readable | ||
socket.on.getCall(3).callback(); // end | ||
@@ -371,0 +514,0 @@ assert.calledWith(process.stdout.write, 'response from daemon'); |
import fs from 'node:fs'; | ||
import net from 'node:net'; | ||
import os from 'node:os'; | ||
import child_process from 'node:child_process'; | ||
import { loadConfig, removeConfig } from './config.js'; | ||
import { forwardCommandToDaemon } from './forwarder.js'; | ||
import { SHUTDOWN_COMMAND } from './commands.js'; | ||
@@ -51,9 +52,10 @@ /** | ||
try { | ||
await platformAwareStopDaemon(config); | ||
await Promise.all([ | ||
waitForConfig(resolver.base), | ||
platformAwareStopDaemon(config) | ||
]); | ||
} catch (err) { | ||
console.error(`eslint_d: ${err} - removing config`); | ||
await removeConfig(resolver); | ||
return; | ||
} | ||
await waitForConfig(resolver.base); | ||
} | ||
@@ -67,8 +69,3 @@ | ||
if (os.platform() === 'win32') { | ||
return new Promise((resolve, reject) => { | ||
const socket = net.connect(config.port, '127.0.0.1'); | ||
socket.once('error', reject); | ||
socket.write(JSON.stringify([config.token, 'ESLINT_D_STOP'])); | ||
socket.end(() => resolve(undefined)); | ||
}); | ||
return forwardCommandToDaemon(config, SHUTDOWN_COMMAND); | ||
} | ||
@@ -75,0 +72,0 @@ |
@@ -11,2 +11,3 @@ import fs from 'node:fs'; | ||
import { strictEqual } from 'node:assert'; | ||
import { SHUTDOWN_COMMAND } from './commands.js'; | ||
@@ -249,6 +250,7 @@ describe('lib/launcher', () => { | ||
it('does not watch for the config file', () => { | ||
stopDaemon(resolver, config); | ||
it('does not wait for the config file removal', async () => { | ||
const promise = stopDaemon(resolver, config); | ||
refute.called(fs.watch); | ||
assert.calledOnceWith(fs.watch, resolver.base); | ||
await assert.resolves(promise, undefined); | ||
}); | ||
@@ -291,3 +293,3 @@ }); | ||
.on('end', () => { | ||
strictEqual(data, '["token","ESLINT_D_STOP"]'); | ||
strictEqual(data, `["token","${SHUTDOWN_COMMAND}"]`); | ||
done(); | ||
@@ -345,6 +347,7 @@ }); | ||
it('does not watch for the config file', () => { | ||
stopDaemon(resolver, config); | ||
it('does not wait for the config file removal', async () => { | ||
const promise = stopDaemon(resolver, config); | ||
refute.called(fs.watch); | ||
assert.calledOnceWith(fs.watch, resolver.base); | ||
await assert.resolves(promise, undefined); | ||
}); | ||
@@ -351,0 +354,0 @@ }); |
import { createRequire } from 'node:module'; | ||
import { LINT_COMMAND, PING_COMMAND, SHUTDOWN_COMMAND } from './commands.js'; | ||
@@ -43,3 +44,4 @@ /** | ||
} | ||
const [request_token, color_level, cwd, argv] = JSON.parse(content); | ||
const [request_token, command, color_level, cwd, argv] = | ||
JSON.parse(content); | ||
if (request_token !== token) { | ||
@@ -49,5 +51,4 @@ con.end(); | ||
} | ||
if (color_level === 'ESLINT_D_STOP') { | ||
shutdown(); | ||
con.end(); | ||
if (command !== LINT_COMMAND) { | ||
onCommand(con, command); | ||
return; | ||
@@ -79,2 +80,21 @@ } | ||
}; | ||
/** | ||
* @param {Socket} con | ||
* @param {string} command | ||
*/ | ||
function onCommand(con, command) { | ||
switch (command) { | ||
case SHUTDOWN_COMMAND: | ||
shutdown(); | ||
break; | ||
case PING_COMMAND: | ||
break; | ||
default: | ||
con.write(`Unknown command: ${command}`); | ||
} | ||
con.end(); | ||
con.destroySoon(); | ||
} | ||
} |
@@ -5,2 +5,3 @@ import { Socket } from 'node:net'; | ||
import { createService } from './service.js'; | ||
import { LINT_COMMAND, SHUTDOWN_COMMAND } from './commands.js'; | ||
@@ -45,2 +46,3 @@ describe('lib/service', () => { | ||
* @param {string} request_token | ||
* @param {string} command | ||
* @param {string} color_level | ||
@@ -51,5 +53,5 @@ * @param {string} cwd | ||
*/ | ||
function send(request_token, color_level, cwd, argv, text) { | ||
function send(request_token, command, color_level, cwd, argv, text) { | ||
const chunks = [ | ||
`["${request_token}",${color_level},`, | ||
`["${request_token}","${command}",${color_level},`, | ||
`${JSON.stringify(cwd)},${JSON.stringify(argv)}]` | ||
@@ -75,3 +77,3 @@ ]; | ||
it('immediately ends connection if token does not match', () => { | ||
send('invalid', '0', '/', []); | ||
send('invalid', LINT_COMMAND, '0', '/', []); | ||
@@ -85,3 +87,3 @@ assert.calledOnceWithExactly(con.end); | ||
it('sets chalk.level to given color and changes directory', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
@@ -94,3 +96,3 @@ await eslint_promise.resolve(0); | ||
it('replaces stdout with a function that write to the socket', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
process.stdout.write('test'); | ||
@@ -103,3 +105,3 @@ | ||
it('replaces stderr with a function that write to the socket', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
process.stderr.write('test'); | ||
@@ -112,3 +114,3 @@ | ||
it('invokes eslint.execute with given args and no text', async () => { | ||
send(token, '3', '/', ['--fix']); | ||
send(token, LINT_COMMAND, '3', '/', ['--fix']); | ||
@@ -120,3 +122,3 @@ await eslint_promise.resolve(0); | ||
it('invokes eslint.execute with given text', async () => { | ||
send(token, '3', '/', [], 'some text'); | ||
send(token, LINT_COMMAND, '3', '/', [], 'some text'); | ||
@@ -128,3 +130,3 @@ await eslint_promise.resolve(0); | ||
it('ends connection with "EXIT000" if eslint returns 0', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
@@ -137,3 +139,3 @@ await eslint_promise.resolve(0); | ||
it('ends connection with "EXIT001" if eslint returns 1', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
@@ -146,3 +148,3 @@ await eslint_promise.resolve(1); | ||
it('ends connection with "EXIT002" if eslint returns 2', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
@@ -155,3 +157,3 @@ await eslint_promise.resolve(2); | ||
it('ends connection with "EXIT123" if eslint returns 123', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
@@ -164,3 +166,3 @@ await eslint_promise.resolve(123); | ||
it('ends connection with "EXIT001" if eslint throws', async () => { | ||
send(token, '3', '/', []); | ||
send(token, LINT_COMMAND, '3', '/', []); | ||
@@ -172,4 +174,4 @@ await eslint_promise.reject(new Error('Ouch!')); | ||
it('shutdown daemon if ESLINT_D_STOP received', async () => { | ||
send(token, '"ESLINT_D_STOP"', '/', []); | ||
it('shutdown daemon if shutdown command received', async () => { | ||
send(token, SHUTDOWN_COMMAND, '3', '/', []); | ||
@@ -176,0 +178,0 @@ await shutdown_promise; |
@@ -0,1 +1,3 @@ | ||
import { isAlive } from './forwarder.js'; | ||
/** | ||
@@ -10,4 +12,6 @@ * @import { Resolver } from './resolver.js' | ||
*/ | ||
export function status(resolver, config) { | ||
const running = config ? `Running (${config.pid})` : 'Not running'; | ||
export async function status(resolver, config) { | ||
const isRunning = config ? await isAlive(config) : false; | ||
const running = | ||
config && isRunning ? `Running (${config.pid})` : 'Not running'; | ||
const eslint_origin = resolver.bundled ? 'bundled' : 'local'; | ||
@@ -14,0 +18,0 @@ const eslint_version = resolver.require( |
@@ -0,1 +1,3 @@ | ||
import net from 'node:net'; | ||
import { PassThrough } from 'node:stream'; | ||
import { assert, sinon } from '@sinonjs/referee-sinon'; | ||
@@ -5,9 +7,34 @@ import { status } from './status.js'; | ||
describe('lib/status', () => { | ||
let socket; | ||
const config = { token: 'token', port: 123, pid: 456, hash: 'hash' }; | ||
beforeEach(() => { | ||
socket = new PassThrough(); | ||
sinon.replace(socket, 'write', sinon.fake()); | ||
sinon.replace(socket, 'end', sinon.fake()); | ||
sinon.replace(net, 'connect', sinon.fake.returns(socket)); | ||
sinon.replace(console, 'log', sinon.fake()); | ||
}); | ||
it('prints status when running and bundled', () => { | ||
/** | ||
* @param {boolean} isAlive | ||
*/ | ||
function setDaemonState(isAlive) { | ||
sinon.replace( | ||
socket, | ||
'on', | ||
sinon.fake((event, cb) => { | ||
if (isAlive && event !== 'error') { | ||
cb(); | ||
} | ||
if (!isAlive && event === 'error') { | ||
cb(); | ||
} | ||
return socket; | ||
}) | ||
); | ||
} | ||
it('prints status when running and bundled', async () => { | ||
setDaemonState(true); | ||
const resolver = { | ||
@@ -19,3 +46,3 @@ base: '/path/to/eslint', | ||
status(resolver, config); | ||
await status(resolver, config); | ||
@@ -28,3 +55,4 @@ assert.calledOnceWith( | ||
it('prints status when running and local', () => { | ||
it('prints status when running and local', async () => { | ||
setDaemonState(true); | ||
const resolver = { | ||
@@ -36,3 +64,3 @@ base: '/path/to/eslint', | ||
status(resolver, config); | ||
await status(resolver, config); | ||
@@ -45,3 +73,4 @@ assert.calledOnceWith( | ||
it('prints status when not running and bundled', () => { | ||
it('prints status when not running and bundled', async () => { | ||
setDaemonState(false); | ||
const resolver = { | ||
@@ -53,3 +82,3 @@ base: '/path/to/eslint', | ||
status(resolver, null); | ||
await status(resolver, null); | ||
@@ -62,3 +91,4 @@ assert.calledOnceWith( | ||
it('prints status when not running and local', () => { | ||
it('prints status when not running and local', async () => { | ||
setDaemonState(false); | ||
const resolver = { | ||
@@ -70,3 +100,3 @@ base: '/path/to/eslint', | ||
status(resolver, null); | ||
await status(resolver, null); | ||
@@ -73,0 +103,0 @@ assert.calledOnceWith( |
{ | ||
"name": "eslint_d", | ||
"version": "14.0.4", | ||
"version": "14.1.0", | ||
"description": "Speed up eslint to accelerate your development workflow", | ||
@@ -24,3 +24,3 @@ "type": "module", | ||
"test": "mocha '**/*.test.js'", | ||
"test:integration": "mocha --bail --slow 1000 test/test.integration.js", | ||
"test:integration": "mocha --bail --slow 1000 --timeout 5000 test/test.integration.js", | ||
"test:coverage": "mcr --filter \"{'**/node_modules/**':false,'**/**':true}\" -r v8,console-details mocha '**/*.test.js'", | ||
@@ -27,0 +27,0 @@ "watch": "chokidar '**/*.js' -c 'npm t' --initial --silent", |
83734
28
2168