New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

eslint_d

Package Overview
Dependencies
Maintainers
2
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint_d - npm Package Compare versions

Comparing version 14.0.1 to 14.0.2

7

lib/config.js

@@ -20,4 +20,9 @@ import fs from 'node:fs/promises';

export async function loadConfig(resolver) {
const filename = configFile(resolver);
try {
const raw = await fs.readFile(configFile(resolver), 'utf8');
let raw = await fs.readFile(filename, 'utf8');
if (!raw) {
await new Promise((resolve) => setTimeout(resolve, 50));
raw = await fs.readFile(filename, 'utf8');
}
const [token, port, pid, hash] = raw.split(' ');

@@ -24,0 +29,0 @@ return { token, port: Number(port), pid: Number(pid), hash };

@@ -42,2 +42,25 @@ import fs from 'node:fs/promises';

});
it('retries reading the file if content was empty', async () => {
const clock = sinon.useFakeTimers();
const contents = ['', 'token 123 456 hash'];
sinon.replace(
fs,
'readFile',
sinon.fake(() => Promise.resolve(contents.shift()))
);
const promise = loadConfig(resolver);
await Promise.resolve();
assert.calledOnce(fs.readFile);
clock.tick(50);
await assert.resolves(promise, {
token: 'token',
port: 123,
pid: 456,
hash: 'hash'
});
assert.calledTwice(fs.readFile);
});
});

@@ -44,0 +67,0 @@

@@ -10,2 +10,5 @@ import net from 'node:net';

const EXIT_TOKEN_REGEXP = new RegExp(/EXIT([0-9]{3})/);
const EXIT_TOKEN_LENGTH = 7;
/**

@@ -16,5 +19,25 @@ * @param {Resolver} resolver

export async function forwardToDaemon(resolver, config) {
const eslint_args = process.argv.slice();
const text = process.argv.includes('--stdin') ? await readStdin() : null;
const { stdout } = supportsColor;
const fix_to_stdout_index = eslint_args.indexOf('--fix-to-stdout');
const fix_to_stdout = fix_to_stdout_index !== -1;
if (fix_to_stdout) {
if (!eslint_args.includes('--stdin')) {
console.error('--fix-to-stdout requires passing --stdin as well');
// eslint-disable-next-line require-atomic-updates
process.exitCode = 1;
return;
}
eslint_args.splice(
fix_to_stdout_index,
1,
'--fix-dry-run',
'--format',
'json'
);
}
const socket = net.connect(config.port, '127.0.0.1');

@@ -24,6 +47,6 @@ const args = [

stdout ? stdout.level : 0,
JSON.stringify(process.cwd()),
JSON.stringify(process.argv)
process.cwd(),
eslint_args
];
socket.write(args.join(' '));
socket.write(JSON.stringify(args));
if (text) {

@@ -42,5 +65,4 @@ socket.write('\n');

content += chunk;
if (content.length > 5) {
process.stdout.write(content.substring(0, content.length - 5));
content = content.substring(content.length - 5);
if (!fix_to_stdout && content.length > EXIT_TOKEN_LENGTH) {
process.stdout.write(flushMessage());
}

@@ -50,9 +72,24 @@ }

.on('end', () => {
if (content.startsWith('EXIT')) {
process.exitCode = Number(content.slice(4));
} else {
process.stdout.write(content);
console.error('eslint_d: unexpected response');
process.exitCode = 1;
if (fix_to_stdout) {
try {
const { output } = JSON.parse(flushMessage())[0];
process.stdout.write(output || text);
} catch (err) {
process.stdout.write(text);
console.error(`eslint_d: ${err}`);
process.exitCode = 1;
return;
}
}
// The remaining 'content' must be the termination code:
const match = content.match(EXIT_TOKEN_REGEXP);
if (match) {
process.exitCode = Number(match[1]);
return;
}
process.stdout.write(content);
console.error('eslint_d: unexpected response');
process.exitCode = 1;
})

@@ -68,2 +105,14 @@ .on('error', async (err) => {

});
/**
* @returns {string}
*/
function flushMessage() {
const message_length = content.length - EXIT_TOKEN_LENGTH;
// Extract everything we are sure doesn't contain the termination code:
const message = content.substring(0, message_length);
// Keep only what we haven't written yet:
content = content.substring(message_length);
return message;
}
}

@@ -70,0 +119,0 @@

@@ -15,2 +15,3 @@ import net from 'node:net';

const config = { token: 'token', port: 123, pid: 456, hash: 'hash' };
const color_level = supportsColor.stdout?.['level'] || 0;
let socket;

@@ -24,2 +25,8 @@ let argv;

function fakeStdin(text) {
const stdin = new PassThrough();
stdin.end(text);
sinon.replaceGetter(process, 'stdin', () => stdin);
}
beforeEach(() => {

@@ -50,3 +57,3 @@ socket = new PassThrough();

socket.write,
`token ${supportsColor.stdout?.['level'] || 0} "the/cwd" ["node","eslint_d"]`
`["token",${color_level},"the/cwd",["node","eslint_d"]]`
);

@@ -77,9 +84,6 @@ assert.calledOnce(socket.end);

it('writes text from stdin to socket', async () => {
const text = 'text from stdin';
const stdin = new PassThrough();
fakeStdin('text from stdin');
argv.push('--stdin');
sinon.replaceGetter(process, 'stdin', () => stdin);
forwardToDaemon(resolver, config);
stdin.end(text);
await new Promise(setImmediate);

@@ -89,6 +93,6 @@

assert.calledWith(socket.write, '\n');
assert.calledWith(socket.write, text);
assert.calledWith(socket.write, 'text from stdin');
});
it('forwards socket response to stdout, except for the last 5 characters', () => {
it('forwards socket response to stdout', () => {
const chunks = ['response ', 'from daemon'];

@@ -104,10 +108,12 @@ sinon.replace(

socket.on.firstCall.callback(); // readable
socket.on.secondCall.callback(); // end
assert.calledTwice(process.stdout.write);
assert.calledWith(process.stdout.write, 'resp');
assert.calledWith(process.stdout.write, 'onse from d');
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 EXIT0 from response', () => {
const chunks = ['response from daemonEXIT0'];
it('handles "EXIT000" from response', () => {
const chunks = ['response from daemonEXIT000'];
sinon.replace(

@@ -124,3 +130,3 @@ socket,

assert.calledOnceWith(process.stdout.write, 'response from daemon');
assert.calledWith(process.stdout.write, 'response from daemon');
assert.equals(process.exitCode, 0);

@@ -130,4 +136,4 @@ refute.called(console.error);

it('handles EXIT1 from response', () => {
const chunks = ['response from daemonEXIT1'];
it('handles "EXIT001" from response', () => {
const chunks = ['response from daemonEXIT001'];
sinon.replace(

@@ -149,2 +155,39 @@ socket,

it('handles "EXIT123" from response', () => {
const chunks = ['response from daemonEXIT123'];
sinon.replace(
socket,
'read',
sinon.fake(() => (chunks.length ? chunks.shift() : null))
);
sinon.replace(process.stdout, 'write', sinon.fake());
forwardToDaemon(resolver, config);
socket.on.firstCall.callback(); // readable
socket.on.secondCall.callback(); // end
assert.calledWith(process.stdout.write, 'response from daemon');
assert.equals(process.exitCode, 123);
refute.called(console.error);
});
it('handles "EXIT001" inside response', () => {
const chunks = ['response EXIT001', ' from daemonEXIT002'];
sinon.replace(
socket,
'read',
sinon.fake(() => (chunks.length ? chunks.shift() : null))
);
sinon.replace(process.stdout, 'write', sinon.fake());
forwardToDaemon(resolver, config);
socket.on.firstCall.callback(); // readable
socket.on.secondCall.callback(); // end
assert.calledWith(process.stdout.write, 'response ');
assert.calledWith(process.stdout.write, 'EXIT001 from daemon');
assert.equals(process.exitCode, 2);
refute.called(console.error);
});
it('logs error and sets exitCode to 1 if response does not end with EXIT marker', () => {

@@ -163,4 +206,4 @@ const chunks = ['response from daemon'];

assert.calledWith(process.stdout.write, 'response from d');
assert.calledWith(process.stdout.write, 'aemon');
assert.calledWith(process.stdout.write, 'response from');
assert.calledWith(process.stdout.write, ' daemon');
assert.equals(process.exitCode, 1);

@@ -196,3 +239,104 @@ assert.calledOnceWith(console.error, 'eslint_d: unexpected response');

});
context('--fix-to-stdout', () => {
beforeEach(() => {
sinon.replace(process, 'cwd', sinon.fake.returns('cwd'));
fakeStdin('text from stdin');
});
it('throws if --stdin is absent', async () => {
argv.push('--fix-to-stdout');
await forwardToDaemon(resolver, config);
assert.equals(process.exitCode, 1);
assert.calledOnceWith(
console.error,
'--fix-to-stdout requires passing --stdin as well'
);
});
it('replaces the option with --fix-dry-run --format json', async () => {
argv.push('--stdin', '--fix-to-stdout', '--other', '--options');
forwardToDaemon(resolver, config);
await new Promise(setImmediate);
assert.calledThrice(socket.write);
assert.calledWith(
socket.write,
`["token",${color_level},"cwd",["node","eslint_d","--stdin","--fix-dry-run","--format","json","--other","--options"]]`
);
assert.calledWith(socket.write, '\n');
assert.calledWith(socket.write, 'text from stdin');
assert.calledOnce(socket.end);
});
it('prints fixed output to stdout', async () => {
argv.push('--stdin', '--fix-to-stdout');
const chunks = ['[{"output":"response from daemon"}]EXIT001'];
sinon.replace(
socket,
'read',
sinon.fake(() => (chunks.length ? chunks.shift() : null))
);
sinon.replace(process.stdout, 'write', sinon.fake());
forwardToDaemon(resolver, config);
await new Promise(setImmediate);
socket.on.firstCall.callback(); // readable
socket.on.secondCall.callback(); // end
assert.calledWith(process.stdout.write, 'response from daemon');
assert.equals(process.exitCode, 1);
refute.called(console.error);
});
it('prints original input to stdout if no output', async () => {
argv.push('--stdin', '--fix-to-stdout');
const chunks = ['[{}]EXIT000'];
sinon.replace(
socket,
'read',
sinon.fake(() => (chunks.length ? chunks.shift() : null))
);
sinon.replace(process.stdout, 'write', sinon.fake());
forwardToDaemon(resolver, config);
await new Promise(setImmediate);
socket.on.firstCall.callback(); // readable
socket.on.secondCall.callback(); // end
assert.calledWith(process.stdout.write, 'text from stdin');
assert.equals(process.exitCode, 0);
refute.called(console.error);
});
it('prints error to stderr and original input to stdout if output cannot be parsed', async () => {
argv.push('--stdin', '--fix-to-stdout');
const chunks = ['NotJSON!EXIT000'];
sinon.replace(
socket,
'read',
sinon.fake(() => (chunks.length ? chunks.shift() : null))
);
sinon.replace(process.stdout, 'write', sinon.fake());
forwardToDaemon(resolver, config);
await new Promise(setImmediate);
socket.on.firstCall.callback(); // readable
socket.on.secondCall.callback(); // end
assert.calledWith(process.stdout.write, 'text from stdin');
assert.equals(process.exitCode, 1);
let error;
try {
JSON.parse('NotJSON!');
} catch (err) {
error = err;
}
assert.calledOnceWith(console.error, `eslint_d: ${error}`);
});
});
});
});

@@ -10,2 +10,3 @@ export function help() {

status Show daemon status, process id and resolved eslint version
--fix-to-stdout Print fixed file to stdout (requires --stdin)
--help, -h Show this help

@@ -12,0 +13,0 @@ --version, -v Show version number of eslint_d and bundled eslint

7

lib/resolver.js
import { createRequire } from 'node:module';
import { dirname } from 'node:path';

@@ -19,3 +20,3 @@ /**

try {
path = require.resolve('eslint', { paths: [process.cwd()] });
path = require.resolve('eslint/package.json', { paths: [process.cwd()] });
} catch (err) {

@@ -30,7 +31,7 @@ if (local === 'ignore') {

// Fallback to bundled eslint
path = require.resolve('eslint');
path = require.resolve('eslint/package.json');
bundled = true;
}
return {
base: path.substring(0, path.lastIndexOf('/eslint/') + 7),
base: dirname(path),
bundled,

@@ -37,0 +38,0 @@ require

@@ -67,3 +67,3 @@ import { resolve } from 'node:path';

match(
"eslint_d: Failed to resolve eslint - Error: Cannot find module 'eslint'"
"eslint_d: Failed to resolve eslint - Error: Cannot find module 'eslint/package.json'"
)

@@ -70,0 +70,0 @@ );

@@ -42,3 +42,3 @@ import { createRequire } from 'node:module';

}
const [request_token, color_level, cwd, argv] = content.split(' ');
const [request_token, color_level, cwd, argv] = JSON.parse(content);
if (request_token !== token) {

@@ -50,3 +50,3 @@ con.end();

chalk.level = color_level;
process.chdir(JSON.parse(cwd));
process.chdir(cwd);

@@ -57,3 +57,3 @@ process.stdout.write = (chunk) => con.write(chunk);

try {
code = await eslint.execute(JSON.parse(argv), text, true);
code = await eslint.execute(argv, text, true);
} catch (e) {

@@ -66,3 +66,3 @@ con.write(String(e));

/* eslint-enable require-atomic-updates */
con.end(`EXIT${code}`);
con.end(`EXIT${String(code).padStart(3, '0')}`);
}

@@ -69,0 +69,0 @@ })

@@ -47,4 +47,4 @@ import { Socket } from 'node:net';

const chunks = [
`${request_token} ${color_level} `,
`${JSON.stringify(cwd)} ${JSON.stringify(argv)}`
`["${request_token}",${color_level},`,
`${JSON.stringify(cwd)},${JSON.stringify(argv)}]`
];

@@ -81,3 +81,3 @@ if (text !== undefined) {

await eslint_promise.resolve(0);
assert.equals(chalk.level, '3');
assert.equals(chalk.level, 3);
assert.calledOnceWith(process.chdir, '/');

@@ -116,3 +116,3 @@ });

it('ends connection with "EXIT0" if eslint returns 0', async () => {
it('ends connection with "EXIT000" if eslint returns 0', async () => {
send(token, '3', '/', []);

@@ -122,6 +122,6 @@

refute.called(con.write);
assert.calledOnceWith(con.end, 'EXIT0');
assert.calledOnceWith(con.end, 'EXIT000');
});
it('ends connection with "EXIT1" if eslint returns 1', async () => {
it('ends connection with "EXIT001" if eslint returns 1', async () => {
send(token, '3', '/', []);

@@ -131,6 +131,6 @@

refute.called(con.write);
assert.calledOnceWith(con.end, 'EXIT1');
assert.calledOnceWith(con.end, 'EXIT001');
});
it('ends connection with "EXIT2" if eslint returns 2', async () => {
it('ends connection with "EXIT002" if eslint returns 2', async () => {
send(token, '3', '/', []);

@@ -140,13 +140,21 @@

refute.called(con.write);
assert.calledOnceWith(con.end, 'EXIT2');
assert.calledOnceWith(con.end, 'EXIT002');
});
it('ends connection with "EXIT1" if eslint throws', async () => {
it('ends connection with "EXIT123" if eslint returns 123', async () => {
send(token, '3', '/', []);
await eslint_promise.resolve(123);
refute.called(con.write);
assert.calledOnceWith(con.end, 'EXIT123');
});
it('ends connection with "EXIT001" if eslint throws', async () => {
send(token, '3', '/', []);
await eslint_promise.reject(new Error('Ouch!'));
assert.calledOnceWith(con.write, 'Error: Ouch!');
assert.calledOnceWith(con.end, 'EXIT1');
assert.calledOnceWith(con.end, 'EXIT001');
});
});
});
{
"name": "eslint_d",
"version": "14.0.1",
"version": "14.0.2",
"description": "Speed up eslint to accelerate your development workflow",

@@ -5,0 +5,0 @@ "type": "module",

@@ -111,2 +111,3 @@ <h1 align="center">

--version, -v Show version number of eslint_d and bundled eslint
--fix-to-stdout Print fixed file to stdout (requires --stdin)
```

@@ -125,2 +126,25 @@

## Automatic fixing
`eslint_d` has an additional option that `eslint` does not have,
`--fix-to-stdout` which prints the fixed file to stdout. This allows editors to
add before save hooks to automatically fix a file prior to saving. It must be
used with `--stdin`.
### Vim
Add this to your `.vimrc` to lint the current buffer or visual selection on
`<leader>f`:
```vim
" Autofix entire buffer with eslint_d:
nnoremap <leader>f mF:%!eslint_d --stdin --fix-to-stdout --stdin-filename %<CR>`F
" Autofix visual selection with eslint_d:
vnoremap <leader>f :!eslint_d --stdin --fix-to-stdout<CR>gv
```
### Emacs
See [eslintd-fix](https://github.com/aaronjensen/eslintd-fix)
## How does this work?

@@ -157,3 +181,3 @@

- `14.0.0`: eslint 4 - 8, node 18 - 22 (ships with eslint 9)
- `14.0.0`: eslint 4 - 9, node 18 - 22 (ships with eslint 9) (see [^1])
- `13.0.0`: eslint 4 - 8, node 12 - 20 (ships with eslint 8)

@@ -183,1 +207,3 @@ - `12.0.0`: eslint 4 - 8, node 12 - 16 (ships with eslint 8)

[SublimeLinter-eslint]: https://github.com/SublimeLinter/SublimeLinter-eslint
[^1]: The support for `--fix-to-stdout` is only provided with eslint 5 and beyond.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc