Skip to content

Commit

Permalink
feat: Enable eslint.config.mjs and eslint.config.cjs (#18066)
Browse files Browse the repository at this point in the history
* feat: Enable eslint.config.mjs and eslint.config.cjs

* Update CLI description

(cherry picked from commit 8792464)

* docs: update documentation for eslint.config.mjs & eslint.config.cjs support

---------

Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
  • Loading branch information
snitin315 and nzakas committed Feb 1, 2024
1 parent 4c7e9b0 commit dca7d0f
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 23 deletions.
28 changes: 8 additions & 20 deletions docs/src/use/configure/configuration-files-new.md
Expand Up @@ -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 [
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:

Expand Down
8 changes: 6 additions & 2 deletions lib/eslint/flat-eslint.js
Expand Up @@ -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();
Expand Down Expand Up @@ -248,7 +252,7 @@ function compareResultsByFilePath(a, b) {
*/
function findFlatConfigFile(cwd) {
return findUp(
FLAT_CONFIG_FILENAME,
FLAT_CONFIG_FILENAMES,
{ cwd }
);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/options.js
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions tests/fixtures/cjs-config/eslint.config.cjs
@@ -0,0 +1,5 @@
module.exports = {
rules: {
"no-undef": "warn"
}
};
1 change: 1 addition & 0 deletions tests/fixtures/cjs-config/foo.js
@@ -0,0 +1 @@
foo;
5 changes: 5 additions & 0 deletions tests/fixtures/js-mjs-cjs-config/eslint.config.cjs
@@ -0,0 +1,5 @@
module.exports= {
rules: {
"no-undef": "warn"
}
};
5 changes: 5 additions & 0 deletions tests/fixtures/js-mjs-cjs-config/eslint.config.js
@@ -0,0 +1,5 @@
module.exports = {
rules: {
"no-undef": "off"
}
};
5 changes: 5 additions & 0 deletions tests/fixtures/js-mjs-cjs-config/eslint.config.mjs
@@ -0,0 +1,5 @@
export default {
rules: {
"no-undef": "error"
}
};
1 change: 1 addition & 0 deletions tests/fixtures/js-mjs-cjs-config/foo.js
@@ -0,0 +1 @@
foo;
5 changes: 5 additions & 0 deletions tests/fixtures/mjs-cjs-config/eslint.config.cjs
@@ -0,0 +1,5 @@
module.exports = {
rules: {
"no-undef": "warn"
}
};
5 changes: 5 additions & 0 deletions tests/fixtures/mjs-cjs-config/eslint.config.mjs
@@ -0,0 +1,5 @@
export default {
rules: {
"no-undef": "error"
}
};
1 change: 1 addition & 0 deletions tests/fixtures/mjs-cjs-config/foo.js
@@ -0,0 +1 @@
foo;
5 changes: 5 additions & 0 deletions tests/fixtures/mjs-config/eslint.config.mjs
@@ -0,0 +1,5 @@
export default {
rules: {
"no-undef": "error"
}
};
1 change: 1 addition & 0 deletions tests/fixtures/mjs-config/foo.js
@@ -0,0 +1 @@
foo;
136 changes: 136 additions & 0 deletions tests/lib/eslint/flat-eslint.js
Expand Up @@ -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()", () => {
Expand Down Expand Up @@ -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", () => {
Expand Down

0 comments on commit dca7d0f

Please sign in to comment.