Skip to content

Commit

Permalink
Add support for --fix-to-stdout
Browse files Browse the repository at this point in the history
This option was lost in the recent rewrite.
  • Loading branch information
DamienCassou committed Aug 1, 2024
1 parent 0c63d32 commit 7d902ab
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 4 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- Automatically starts, stops and restarts the background server.
- Binds to parent process, editor process or exits after IDLE time.
- Falls back to bundled eslint if local eslint is missing.
- Prints fixed file on stdout with `--fix-to-stdout`

## Setup

Expand Down Expand Up @@ -108,6 +109,7 @@ All arguments are passed to eslint, except for the following commands:
status Show daemon status, process id and resolved eslint version
--help, -h Show this help
--version, -v Show version number of eslint_d and bundled eslint
--fix-to-stdout Print fixed file to stdout (requires --stdin)
```

## Environment variables
Expand Down Expand Up @@ -152,7 +154,7 @@ changed: `package.json`, `package-lock.json`, `npm-shrinkwrap.json`,

## Compatibility

- `14.0.0`: eslint 4 - 9, 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)
- `12.0.0`: eslint 4 - 8, node 12 - 16 (ships with eslint 8)
- `11.0.0`: eslint 4 - 8, node 12 - 16 (ships with eslint 7)
Expand All @@ -177,3 +179,5 @@ MIT
[syntastic]: https://github.com/scrooloose/syntastic
[flycheck]: http://www.flycheck.org/
[SublimeLinter-eslint]: https://github.com/SublimeLinter/SublimeLinter-eslint

[^1]: The support for `--fix-to-stdout` is only provided with eslint 5 and beyond.
32 changes: 30 additions & 2 deletions lib/forwarder.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,35 @@ const DAEMON_TERMINATION_CODE_REGEXP = new RegExp(
* @param {Config} config
*/
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 -- not quite sure which execution flow could be problematic here
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');
const args = [
config.token,
stdout ? stdout.level : 0,
JSON.stringify(process.cwd()),
JSON.stringify(process.argv)
JSON.stringify(eslint_args)
];
socket.write(args.join(' '));
if (text) {
Expand All @@ -48,7 +68,10 @@ export async function forwardToDaemon(resolver, config) {
let chunk = '';
while ((chunk = socket.read()) !== null) {
content += chunk;
if (content.length > DAEMON_TERMINATION_CODE_MAX_LENGTH) {
if (
!fix_to_stdout &&
content.length > DAEMON_TERMINATION_CODE_MAX_LENGTH
) {
const message_length =
content.length - DAEMON_TERMINATION_CODE_MAX_LENGTH;
// write everything we are sure doesn't contain the termination code:
Expand Down Expand Up @@ -78,6 +101,11 @@ export async function forwardToDaemon(resolver, config) {

process.exitCode = Number(match[1]);

if (fix_to_stdout) {
const object = JSON.parse(content);
content = object[0].output || text;
}

if (content) {
process.stdout.write(content);
}
Expand Down
14 changes: 14 additions & 0 deletions lib/forwarder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,5 +225,19 @@ describe('lib/forwarder', () => {
assert.equals(process.exitCode, 1);
assert.calledOnceWith(fs.unlink, `${resolver.base}/.eslint_d`);
});

context('--fix-to-stdout', () => {
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'
);
});
});
});
});
1 change: 1 addition & 0 deletions lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All arguments are passed to eslint, except for the following commands:
stop Stop the daemon
restart Restart the daemon
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
--version, -v Show version number of eslint_d and bundled eslint
Expand Down
101 changes: 100 additions & 1 deletion test/test.integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('integration tests', () => {
});
});

[SUPPORTED_ESLINT_VERSIONS].forEach((fixture) => {
SUPPORTED_ESLINT_VERSIONS.forEach((fixture) => {
context(fixture, () => {
const cwd = path.resolve(`test/fixture/${fixture}`);
const { version: eslint_version } = require(
Expand Down Expand Up @@ -176,4 +176,103 @@ describe('integration tests', () => {
});
});
});

context('--fix-to-stdout', () => {
SUPPORTED_ESLINT_VERSIONS.filter(
(fixture) => fixture !== 'v4.0.x' // v4 misses --fix-dry-run
).forEach((fixture) => {
context(fixture, () => {
const cwd = path.resolve(`test/fixture/${fixture}`);
const config = `${cwd}/node_modules/eslint/.eslint_d`;

beforeEach(async () => {
await killDaemon();
await startDaemon();
});

afterEach(killDaemon);

const run_args = `--fix-to-stdout --stdin --stdin-filename ${cwd}/../foo.js`;

context('when file only contains fixable problems', () => {
it('prints input if no change is needed', async () => {
const stdin = `console.log('Hello eslint');`;
const { error, stdout, stderr } = await run(run_args, {
cwd,
stdin
});

assert.equals(stdout, stdin);
assert.equals(stderr, '');
assert.isNull(error);
});

it('prints fixed output if change is needed', async () => {
const { error, stdout, stderr } = await run(run_args, {
cwd,
stdin: `console.log("Hello eslint");`
});

assert.equals(stdout, `console.log('Hello eslint');`);
assert.equals(stderr, '');
assert.isNull(error);
});
});

context('when file contains non-fixable problems', () => {
it('prints input if no change is needed', async () => {
const stdin = `/* eslint radix: "error" */
console.log('Hello' + parseInt('087'))`;

const { error, stdout, stderr } = await run(run_args, {
cwd,
stdin
});

assert.equals(
stdout,
`/* eslint radix: "error" */
console.log('Hello' + parseInt('087'))`
);
assert.equals(stderr, '');
refute.isNull(error);
assert.equals(error?.['code'], 1);
});

it('prints fixed output if change is needed', async () => {
const stdin = `/* eslint radix: "error" */
console.log("Hello" + parseInt('087'))`;

const { error, stdout, stderr } = await run(run_args, {
cwd,
stdin
});

assert.equals(
stdout,
`/* eslint radix: "error" */
console.log('Hello' + parseInt('087'))`
);
assert.equals(stderr, '');
refute.isNull(error);
assert.equals(error?.['code'], 1);
});
});

async function killDaemon() {
try {
const raw = await fs.readFile(config, 'utf8');
const [, , pid] = raw.split(' ');
process.kill(Number(pid), 0);
} catch {
// nothing to kill
}
}

async function startDaemon() {
await run('start', { cwd });
}
});
});
});
});

0 comments on commit 7d902ab

Please sign in to comment.