From dca7d0f1c262bc72310147bcefe1d04ecf60acbc Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 1 Feb 2024 17:37:48 +0530 Subject: [PATCH] feat: Enable `eslint.config.mjs` and `eslint.config.cjs` (#18066) * feat: Enable eslint.config.mjs and eslint.config.cjs * Update CLI description (cherry picked from commit 8792464ee7956af82dab582ca9ee59da596a608e) * docs: update documentation for eslint.config.mjs & eslint.config.cjs support --------- Co-authored-by: Nicholas C. Zakas --- .../use/configure/configuration-files-new.md | 28 ++-- lib/eslint/flat-eslint.js | 8 +- lib/options.js | 2 +- tests/fixtures/cjs-config/eslint.config.cjs | 5 + tests/fixtures/cjs-config/foo.js | 1 + .../js-mjs-cjs-config/eslint.config.cjs | 5 + .../js-mjs-cjs-config/eslint.config.js | 5 + .../js-mjs-cjs-config/eslint.config.mjs | 5 + tests/fixtures/js-mjs-cjs-config/foo.js | 1 + .../fixtures/mjs-cjs-config/eslint.config.cjs | 5 + .../fixtures/mjs-cjs-config/eslint.config.mjs | 5 + tests/fixtures/mjs-cjs-config/foo.js | 1 + tests/fixtures/mjs-config/eslint.config.mjs | 5 + tests/fixtures/mjs-config/foo.js | 1 + tests/lib/eslint/flat-eslint.js | 136 ++++++++++++++++++ 15 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 tests/fixtures/cjs-config/eslint.config.cjs create mode 100644 tests/fixtures/cjs-config/foo.js create mode 100644 tests/fixtures/js-mjs-cjs-config/eslint.config.cjs create mode 100644 tests/fixtures/js-mjs-cjs-config/eslint.config.js create mode 100644 tests/fixtures/js-mjs-cjs-config/eslint.config.mjs create mode 100644 tests/fixtures/js-mjs-cjs-config/foo.js create mode 100644 tests/fixtures/mjs-cjs-config/eslint.config.cjs create mode 100644 tests/fixtures/mjs-cjs-config/eslint.config.mjs create mode 100644 tests/fixtures/mjs-cjs-config/foo.js create mode 100644 tests/fixtures/mjs-config/eslint.config.mjs create mode 100644 tests/fixtures/mjs-config/foo.js diff --git a/docs/src/use/configure/configuration-files-new.md b/docs/src/use/configure/configuration-files-new.md index a79fc110a4c..685d0752fcb 100644 --- a/docs/src/use/configure/configuration-files-new.md +++ b/docs/src/use/configure/configuration-files-new.md @@ -16,7 +16,13 @@ You can put your ESLint project configuration in a configuration file. You can i ## Configuration File -The ESLint configuration file is named `eslint.config.js`. It should be placed in the root directory of your project and export an array of [configuration objects](#configuration-objects). Here's an example: +The ESLint configuration file may be named any of the following: + +* `eslint.config.js` +* `eslint.config.mjs` +* `eslint.config.cjs` + +It should be placed in the root directory of your project and export an array of [configuration objects](#configuration-objects). Here's an example: ```js export default [ @@ -44,24 +50,6 @@ module.exports = [ ]; ``` -The configuration file can also export a promise that resolves to the configuration array. This can be useful for using ESM dependencies in CommonJS configuration files, as in this example: - -```js -module.exports = (async () => { - - const someDependency = await import("some-esm-dependency"); - - return [ - // ... use `someDependency` here - ]; - -})(); -``` - -::: warning -ESLint only automatically looks for a config file named `eslint.config.js` and does not look for `eslint.config.cjs` or `eslint.config.mjs`. If you'd like to specify a different config filename than the default, use the `--config` command line option. -::: - ## Configuration Objects Each configuration object contains all of the information ESLint needs to execute on a set of files. Each configuration object is made up of these properties: @@ -668,7 +656,7 @@ export default [ ## Configuration File Resolution -When ESLint is run on the command line, it first checks the current working directory for `eslint.config.js`. If the file is not found, it looks to the next parent directory for the file. This search continues until either the file is found or the root directory is reached. +When ESLint is run on the command line, it first checks the current working directory for `eslint.config.js`. If that file is found, then the search stops, otherwise it checks for `eslint.config.mjs`. If that file is found, then the search stops, otherwise it checks for `eslint.config.cjs`. If none of the files are not found, it checks the parent directory for each file. This search continues until either a config file is found or the root directory is reached. You can prevent this search for `eslint.config.js` by setting the `ESLINT_USE_FLAT_CONFIG` environment variable to `true` and using the `-c` or `--config` option on the command line to specify an alternate configuration file, such as: diff --git a/lib/eslint/flat-eslint.js b/lib/eslint/flat-eslint.js index 785f41edf8d..06b41c726c8 100644 --- a/lib/eslint/flat-eslint.js +++ b/lib/eslint/flat-eslint.js @@ -91,7 +91,11 @@ const LintResultCache = require("../cli-engine/lint-result-cache"); // Helpers //------------------------------------------------------------------------------ -const FLAT_CONFIG_FILENAME = "eslint.config.js"; +const FLAT_CONFIG_FILENAMES = [ + "eslint.config.js", + "eslint.config.mjs", + "eslint.config.cjs" +]; const debug = require("debug")("eslint:flat-eslint"); const removedFormatters = new Set(["table", "codeframe"]); const privateMembers = new WeakMap(); @@ -248,7 +252,7 @@ function compareResultsByFilePath(a, b) { */ function findFlatConfigFile(cwd) { return findUp( - FLAT_CONFIG_FILENAME, + FLAT_CONFIG_FILENAMES, { cwd } ); } diff --git a/lib/options.js b/lib/options.js index dd67c399e64..089f347430a 100644 --- a/lib/options.js +++ b/lib/options.js @@ -168,7 +168,7 @@ module.exports = function(usingFlatConfig) { alias: "c", type: "path::String", description: usingFlatConfig - ? "Use this configuration instead of eslint.config.js" + ? "Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs" : "Use this configuration, overriding .eslintrc.* config options if present" }, envFlag, diff --git a/tests/fixtures/cjs-config/eslint.config.cjs b/tests/fixtures/cjs-config/eslint.config.cjs new file mode 100644 index 00000000000..c66a491b8a0 --- /dev/null +++ b/tests/fixtures/cjs-config/eslint.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + rules: { + "no-undef": "warn" + } +}; diff --git a/tests/fixtures/cjs-config/foo.js b/tests/fixtures/cjs-config/foo.js new file mode 100644 index 00000000000..e901f01b487 --- /dev/null +++ b/tests/fixtures/cjs-config/foo.js @@ -0,0 +1 @@ +foo; diff --git a/tests/fixtures/js-mjs-cjs-config/eslint.config.cjs b/tests/fixtures/js-mjs-cjs-config/eslint.config.cjs new file mode 100644 index 00000000000..b4a52eb90f3 --- /dev/null +++ b/tests/fixtures/js-mjs-cjs-config/eslint.config.cjs @@ -0,0 +1,5 @@ +module.exports= { + rules: { + "no-undef": "warn" + } +}; diff --git a/tests/fixtures/js-mjs-cjs-config/eslint.config.js b/tests/fixtures/js-mjs-cjs-config/eslint.config.js new file mode 100644 index 00000000000..c09a3815b57 --- /dev/null +++ b/tests/fixtures/js-mjs-cjs-config/eslint.config.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + "no-undef": "off" + } +}; diff --git a/tests/fixtures/js-mjs-cjs-config/eslint.config.mjs b/tests/fixtures/js-mjs-cjs-config/eslint.config.mjs new file mode 100644 index 00000000000..2ca4f1fc93b --- /dev/null +++ b/tests/fixtures/js-mjs-cjs-config/eslint.config.mjs @@ -0,0 +1,5 @@ +export default { + rules: { + "no-undef": "error" + } +}; diff --git a/tests/fixtures/js-mjs-cjs-config/foo.js b/tests/fixtures/js-mjs-cjs-config/foo.js new file mode 100644 index 00000000000..e901f01b487 --- /dev/null +++ b/tests/fixtures/js-mjs-cjs-config/foo.js @@ -0,0 +1 @@ +foo; diff --git a/tests/fixtures/mjs-cjs-config/eslint.config.cjs b/tests/fixtures/mjs-cjs-config/eslint.config.cjs new file mode 100644 index 00000000000..c66a491b8a0 --- /dev/null +++ b/tests/fixtures/mjs-cjs-config/eslint.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + rules: { + "no-undef": "warn" + } +}; diff --git a/tests/fixtures/mjs-cjs-config/eslint.config.mjs b/tests/fixtures/mjs-cjs-config/eslint.config.mjs new file mode 100644 index 00000000000..2ca4f1fc93b --- /dev/null +++ b/tests/fixtures/mjs-cjs-config/eslint.config.mjs @@ -0,0 +1,5 @@ +export default { + rules: { + "no-undef": "error" + } +}; diff --git a/tests/fixtures/mjs-cjs-config/foo.js b/tests/fixtures/mjs-cjs-config/foo.js new file mode 100644 index 00000000000..e901f01b487 --- /dev/null +++ b/tests/fixtures/mjs-cjs-config/foo.js @@ -0,0 +1 @@ +foo; diff --git a/tests/fixtures/mjs-config/eslint.config.mjs b/tests/fixtures/mjs-config/eslint.config.mjs new file mode 100644 index 00000000000..2ca4f1fc93b --- /dev/null +++ b/tests/fixtures/mjs-config/eslint.config.mjs @@ -0,0 +1,5 @@ +export default { + rules: { + "no-undef": "error" + } +}; diff --git a/tests/fixtures/mjs-config/foo.js b/tests/fixtures/mjs-config/foo.js new file mode 100644 index 00000000000..e901f01b487 --- /dev/null +++ b/tests/fixtures/mjs-config/foo.js @@ -0,0 +1 @@ +foo; diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index ee417830788..ad055a31673 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -821,6 +821,73 @@ describe("FlatESLint", () => { assert.strictEqual(results[0].messages[0].severity, 2); assert.strictEqual(results[0].messages[0].ruleId, "quotes"); }); + + describe("Alternate config files", () => { + + it("should find eslint.config.mjs when present", async () => { + + const cwd = getFixturePath("mjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintText("foo"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + + }); + + it("should find eslint.config.cjs when present", async () => { + + const cwd = getFixturePath("cjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintText("foo"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].severity, 1); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + + }); + + it("should favor eslint.config.js when eslint.config.mjs and eslint.config.cjs are present", async () => { + + const cwd = getFixturePath("js-mjs-cjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintText("foo"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should favor eslint.config.mjs when eslint.config.cjs is present", async () => { + + const cwd = getFixturePath("mjs-cjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintText("foo"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + }); + }); }); describe("lintFiles()", () => { @@ -4110,6 +4177,75 @@ describe("FlatESLint", () => { await assert.rejects(() => eslint.lintFiles(777), /'patterns' must be a non-empty string or an array of non-empty strings/u); await assert.rejects(() => eslint.lintFiles([null]), /'patterns' must be a non-empty string or an array of non-empty strings/u); }); + + describe("Alternate config files", () => { + + it("should find eslint.config.mjs when present", async () => { + + const cwd = getFixturePath("mjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintFiles("foo.js"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + + }); + + it("should find eslint.config.cjs when present", async () => { + + const cwd = getFixturePath("cjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintFiles("foo.js"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].severity, 1); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + + }); + + it("should favor eslint.config.js when eslint.config.mjs and eslint.config.cjs are present", async () => { + + const cwd = getFixturePath("js-mjs-cjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintFiles("foo.js"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should favor eslint.config.mjs when eslint.config.cjs is present", async () => { + + const cwd = getFixturePath("mjs-cjs-config"); + + eslint = new FlatESLint({ + cwd + }); + + const results = await eslint.lintFiles("foo.js"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + }); + }); + + }); describe("Fix Types", () => {