]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | #!/usr/bin/env node |
2 | ||
3 | /** | |
4 | * @fileoverview Main CLI that is run via the eslint command. | |
5 | * @author Nicholas C. Zakas | |
6 | */ | |
7 | ||
8 | /* eslint no-console:off */ | |
9 | ||
10 | "use strict"; | |
11 | ||
12 | // to use V8's code cache to speed up instantiation time | |
13 | require("v8-compile-cache"); | |
14 | ||
eb39fafa | 15 | // must do this initialization *before* other requires in order to work |
56c4a2cb | 16 | if (process.argv.includes("--debug")) { |
456be15e | 17 | require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*"); |
eb39fafa DC |
18 | } |
19 | ||
20 | //------------------------------------------------------------------------------ | |
56c4a2cb | 21 | // Helpers |
eb39fafa DC |
22 | //------------------------------------------------------------------------------ |
23 | ||
56c4a2cb DC |
24 | /** |
25 | * Read data from stdin til the end. | |
26 | * | |
27 | * Note: See | |
28 | * - https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin | |
29 | * - https://github.com/nodejs/node/blob/master/doc/api/process.md#a-note-on-process-io | |
30 | * - https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-01/msg00419.html | |
31 | * - https://github.com/nodejs/node/issues/7439 (historical) | |
32 | * | |
33 | * On Windows using `fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")` seems | |
34 | * to read 4096 bytes before blocking and never drains to read further data. | |
35 | * | |
36 | * The investigation on the Emacs thread indicates: | |
37 | * | |
38 | * > Emacs on MS-Windows uses pipes to communicate with subprocesses; a | |
39 | * > pipe on Windows has a 4K buffer. So as soon as Emacs writes more than | |
40 | * > 4096 bytes to the pipe, the pipe becomes full, and Emacs then waits for | |
41 | * > the subprocess to read its end of the pipe, at which time Emacs will | |
42 | * > write the rest of the stuff. | |
43 | * @returns {Promise<string>} The read text. | |
44 | */ | |
45 | function readStdin() { | |
46 | return new Promise((resolve, reject) => { | |
47 | let content = ""; | |
48 | let chunk = ""; | |
49 | ||
50 | process.stdin | |
51 | .setEncoding("utf8") | |
52 | .on("readable", () => { | |
53 | while ((chunk = process.stdin.read()) !== null) { | |
54 | content += chunk; | |
55 | } | |
56 | }) | |
57 | .on("end", () => resolve(content)) | |
58 | .on("error", reject); | |
59 | }); | |
60 | } | |
eb39fafa | 61 | |
56c4a2cb DC |
62 | /** |
63 | * Get the error message of a given value. | |
64 | * @param {any} error The value to get. | |
65 | * @returns {string} The error message. | |
66 | */ | |
67 | function getErrorMessage(error) { | |
eb39fafa | 68 | |
5422a9cc | 69 | // Lazy loading because this is used only if an error happened. |
56c4a2cb | 70 | const util = require("util"); |
eb39fafa | 71 | |
56c4a2cb DC |
72 | // Foolproof -- thirdparty module might throw non-object. |
73 | if (typeof error !== "object" || error === null) { | |
74 | return String(error); | |
75 | } | |
76 | ||
77 | // Use templates if `error.messageTemplate` is present. | |
78 | if (typeof error.messageTemplate === "string") { | |
79 | try { | |
5422a9cc | 80 | const template = require(`../messages/${error.messageTemplate}.js`); |
56c4a2cb DC |
81 | |
82 | return template(error.messageData || {}); | |
83 | } catch { | |
84 | ||
85 | // Ignore template error then fallback to use `error.stack`. | |
86 | } | |
87 | } | |
eb39fafa | 88 | |
56c4a2cb DC |
89 | // Use the stacktrace if it's an error object. |
90 | if (typeof error.stack === "string") { | |
91 | return error.stack; | |
eb39fafa DC |
92 | } |
93 | ||
56c4a2cb DC |
94 | // Otherwise, dump the object. |
95 | return util.format("%o", error); | |
96 | } | |
97 | ||
98 | /** | |
99 | * Catch and report unexpected error. | |
100 | * @param {any} error The thrown error object. | |
101 | * @returns {void} | |
102 | */ | |
103 | function onFatalError(error) { | |
eb39fafa | 104 | process.exitCode = 2; |
eb39fafa | 105 | |
56c4a2cb DC |
106 | const { version } = require("../package.json"); |
107 | const message = getErrorMessage(error); | |
108 | ||
109 | console.error(` | |
110 | Oops! Something went wrong! :( | |
111 | ||
112 | ESLint: ${version} | |
113 | ||
114 | ${message}`); | |
eb39fafa | 115 | } |
56c4a2cb DC |
116 | |
117 | //------------------------------------------------------------------------------ | |
118 | // Execution | |
119 | //------------------------------------------------------------------------------ | |
120 | ||
121 | (async function main() { | |
122 | process.on("uncaughtException", onFatalError); | |
123 | process.on("unhandledRejection", onFatalError); | |
124 | ||
125 | // Call the config initializer if `--init` is present. | |
126 | if (process.argv.includes("--init")) { | |
127 | await require("../lib/init/config-initializer").initializeConfig(); | |
128 | return; | |
129 | } | |
130 | ||
131 | // Otherwise, call the CLI. | |
132 | process.exitCode = await require("../lib/cli").execute( | |
133 | process.argv, | |
134 | process.argv.includes("--stdin") ? await readStdin() : null | |
135 | ); | |
136 | }()).catch(onFatalError); |