From d89390b75e3e9993f347387a49b0ac5550f45c7f Mon Sep 17 00:00:00 2001 From: Barrie Treloar Date: Sun, 29 Sep 2019 13:34:53 +0930 Subject: [PATCH] Fix: use async reading of stdin in bin/eslint.js (fixes #12212) (#12230) --- bin/eslint.js | 37 ++++++++++++++++++++++++++++++++----- tests/bin/eslint.js | 9 +++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/bin/eslint.js b/bin/eslint.js index 82bcc1e033b..a9f51f1d7d4 100755 --- a/bin/eslint.js +++ b/bin/eslint.js @@ -59,13 +59,40 @@ 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; + let contents = "", + chunk = ""; - process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")); + process.stdin.setEncoding("utf8"); + process.stdin.on("readable", () => { + + // Use a loop to make sure we read all available data. + while ((chunk = process.stdin.read()) !== null) { + contents += chunk; + } + }); + + process.stdin.on("end", () => { + process.exitCode = cli.execute(process.argv, contents, "utf8"); + }); } else if (init) { const configInit = require("../lib/init/config-initializer"); diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js index b6254fa5ff1..932dd9ec451 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", () => {