From 40a2c7bf6dd338f0444a1ff814e0a23d4895fde6 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 6 Sep 2019 09:24:30 +0930 Subject: [PATCH 1/3] Fix: use async reading of stdin in bin/eslint.js closes eslint/eslint#12212 --- bin/eslint.js | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/bin/eslint.js b/bin/eslint.js index 061e94767f0b..80931451f455 100755 --- a/bin/eslint.js +++ b/bin/eslint.js @@ -60,13 +60,41 @@ process.once("uncaughtException", err => { if (useStdIn) { /* - * Note: `process.stdin.fd` is not used here due to https://github.com/nodejs/node/issues/7439. - * Accessing the `process.stdin` property seems to modify the behavior of file descriptor 0, resulting - * in an error when stdin is piped in asynchronously. + * Note: See + * - https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin + * - https://github.com/nodejs/node/blob/master/doc/api/process.md#a-note-on-process-io + * - https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-01/msg00419.html + * - https://github.com/nodejs/node/issues/7439 (historical) + * + * On Windows using `fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")` seems + * to read 4096 bytes before blocking and never drains to read further data. + * + * The investigation on the Emacs thread indicates: + * + * > Emacs on MS-Windows uses pipes to communicate with subprocesses; a + * > pipe on Windows has a 4K buffer. So as soon as Emacs writes more than + * > 4096 bytes to the pipe, the pipe becomes full, and Emacs then waits for + * > the subprocess to read its end of the pipe, at which time Emacs will + * > write the rest of the stuff. + * + * Using the nodejs code example for reading from stdin. */ - const STDIN_FILE_DESCRIPTOR = 0; + const input = []; - process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")); + process.stdin.on("readable", () => { + let chunk; + + // Use a loop to make sure we read all available data. + while ((chunk = process.stdin.read()) !== null) { + input.push(chunk); + } + }); + + process.stdin.on("end", () => { + const contents = input.join(""); + + process.exitCode = cli.execute(process.argv, contents, "utf8"); + }); } else if (init) { const configInit = require("../lib/init/config-initializer"); From 7c2387e6dbd601509ce6dd0e59c6b1dcfdb6c335 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 27 Sep 2019 10:00:59 +0930 Subject: [PATCH 2/3] Test: add test for stdin file size greater than 4096 --- tests/bin/eslint.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js index b6254fa5ff14..932dd9ec4517 100644 --- a/tests/bin/eslint.js +++ b/tests/bin/eslint.js @@ -163,6 +163,15 @@ describe("bin/eslint.js", () => { return assertExitCode(child, 0); }); }); + + it("successfully handles more than 4k data via stdin", () => { + const child = runESLint(["--stdin", "--no-eslintrc"]); + const large = fs.createReadStream(`${__dirname}/../bench/large.js`, "utf8"); + + large.pipe(child.stdin); + + return assertExitCode(child, 0); + }); }); describe("running on files", () => { From 7d704f4f55ea7be6aa104f9d278383b5a265c6a6 Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Fri, 27 Sep 2019 11:28:11 +0930 Subject: [PATCH 3/3] Fix: set encoding to utf for stdin, use string concat for chunking --- bin/eslint.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bin/eslint.js b/bin/eslint.js index 80931451f455..89ab2cc9c8d0 100755 --- a/bin/eslint.js +++ b/bin/eslint.js @@ -79,20 +79,19 @@ if (useStdIn) { * * Using the nodejs code example for reading from stdin. */ - const input = []; + let contents = "", + chunk = ""; + process.stdin.setEncoding("utf8"); process.stdin.on("readable", () => { - let chunk; // Use a loop to make sure we read all available data. while ((chunk = process.stdin.read()) !== null) { - input.push(chunk); + contents += chunk; } }); process.stdin.on("end", () => { - const contents = input.join(""); - process.exitCode = cli.execute(process.argv, contents, "utf8"); }); } else if (init) {