Skip to content

Commit 53f0f47

Browse files
authored
feat: Add loadESLint() API method for v9 (#18097)
* feat: Add loadESLint() API method for v9 refs #18075 * Fix docs * Add more tests using environment variables
1 parent f1c7e6f commit 53f0f47

File tree

7 files changed

+130
-2
lines changed

7 files changed

+130
-2
lines changed

docs/src/integrate/nodejs-api.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,49 @@ The `LoadedFormatter` value is the object to convert the [LintResult] objects to
442442

443443
---
444444

445+
## loadESLint()
446+
447+
The `loadESLint()` function is used for integrations that wish to support both the current configuration system (flat config) and the old configuration system (eslintrc). This function returns the correct `ESLint` class implementation based on the arguments provided:
448+
449+
```js
450+
const { loadESLint } = require("eslint");
451+
452+
// loads the default ESLint that the CLI would use based on process.cwd()
453+
const DefaultESLint = await loadESLint();
454+
455+
// loads the default ESLint that the CLI would use based on the provided cwd
456+
const CwdDefaultESLint = await loadESLint({ cwd: "/foo/bar" });
457+
458+
// loads the flat config version specifically
459+
const FlatESLint = await loadESLint({ useFlatConfig: true });
460+
461+
// loads the legacy version specifically
462+
const LegacyESLint = await loadESLint({ useFlatConfig: false });
463+
```
464+
465+
You can then use the returned constructor to instantiate a new `ESLint` instance, like this:
466+
467+
```js
468+
// loads the default ESLint that the CLI would use based on process.cwd()
469+
const DefaultESLint = await loadESLint();
470+
const eslint = new DefaultESLint();
471+
```
472+
473+
If you're ever unsure which config system the returned constructor uses, check the `configType` property, which is either `"flat"` or `"eslintrc"`:
474+
475+
```js
476+
// loads the default ESLint that the CLI would use based on process.cwd()
477+
const DefaultESLint = await loadESLint();
478+
479+
if (DefaultESLint.configType === "flat") {
480+
// do something specific to flat config
481+
}
482+
```
483+
484+
If you don't need to support both the old and new configuration systems, then it's recommended to just use the `ESLint` constructor directly.
485+
486+
---
487+
445488
## SourceCode
446489

447490
The `SourceCode` type represents the parsed source code that ESLint executes on. It's used internally in ESLint and is also available so that already-parsed code can be used. You can create a new instance of `SourceCode` by passing in the text string representing the code and an abstract syntax tree (AST) in [ESTree](https://github.com/estree/estree) format (including location information, range information, comments, and tokens):

lib/api.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,41 @@
99
// Requirements
1010
//-----------------------------------------------------------------------------
1111

12-
const { ESLint } = require("./eslint/eslint");
12+
const { ESLint, shouldUseFlatConfig } = require("./eslint/eslint");
13+
const { LegacyESLint } = require("./eslint/legacy-eslint");
1314
const { Linter } = require("./linter");
1415
const { RuleTester } = require("./rule-tester");
1516
const { SourceCode } = require("./source-code");
1617

18+
//-----------------------------------------------------------------------------
19+
// Functions
20+
//-----------------------------------------------------------------------------
21+
22+
/**
23+
* Loads the correct ESLint constructor given the options.
24+
* @param {Object} [options] The options object
25+
* @param {boolean} [options.useFlatConfig] Whether or not to use a flat config
26+
* @returns {Promise<ESLint|LegacyESLint>} The ESLint constructor
27+
*/
28+
async function loadESLint({ useFlatConfig } = {}) {
29+
30+
/*
31+
* Note: The v8.x version of this function also accepted a `cwd` option, but
32+
* it is not used in this implementation so we silently ignore it.
33+
*/
34+
35+
const shouldESLintUseFlatConfig = useFlatConfig ?? (await shouldUseFlatConfig());
36+
37+
return shouldESLintUseFlatConfig ? ESLint : LegacyESLint;
38+
}
39+
1740
//-----------------------------------------------------------------------------
1841
// Exports
1942
//-----------------------------------------------------------------------------
2043

2144
module.exports = {
2245
Linter,
46+
loadESLint,
2347
ESLint,
2448
RuleTester,
2549
SourceCode

lib/eslint/eslint.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,12 @@ function createExtraneousResultsError() {
565565
*/
566566
class ESLint {
567567

568+
/**
569+
* The type of configuration used by this class.
570+
* @type {string}
571+
*/
572+
static configType = "flat";
573+
568574
/**
569575
* Creates a new instance of the main ESLint API.
570576
* @param {ESLintOptions} options The options for this instance.

lib/eslint/legacy-eslint.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,12 @@ function compareResultsByFilePath(a, b) {
438438
*/
439439
class LegacyESLint {
440440

441+
/**
442+
* The type of configuration used by this class.
443+
* @type {string}
444+
*/
445+
static configType = "eslintrc";
446+
441447
/**
442448
* Creates a new instance of the main ESLint API.
443449
* @param {LegacyESLintOptions} options The options for this instance.

tests/lib/api.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
//-----------------------------------------------------------------------------
1111

1212
const assert = require("chai").assert,
13-
api = require("../../lib/api");
13+
api = require("../../lib/api"),
14+
{ LegacyESLint } = require("../../lib/eslint/legacy-eslint");
1415

1516
//-----------------------------------------------------------------------------
1617
// Tests
@@ -41,4 +42,42 @@ describe("api", () => {
4142
it("should have SourceCode exposed", () => {
4243
assert.isFunction(api.SourceCode);
4344
});
45+
46+
describe("loadESLint", () => {
47+
48+
afterEach(() => {
49+
delete process.env.ESLINT_USE_FLAT_CONFIG;
50+
});
51+
52+
it("should be a function", () => {
53+
assert.isFunction(api.loadESLint);
54+
});
55+
56+
it("should return a Promise", () => {
57+
assert.instanceOf(api.loadESLint(), Promise);
58+
});
59+
60+
it("should return ESLint when useFlatConfig is true", async () => {
61+
assert.strictEqual(await api.loadESLint({ useFlatConfig: true }), api.ESLint);
62+
});
63+
64+
it("should return LegacyESLint when useFlatConfig is false", async () => {
65+
assert.strictEqual(await api.loadESLint({ useFlatConfig: false }), LegacyESLint);
66+
});
67+
68+
it("should return ESLint when useFlatConfig is not provided", async () => {
69+
assert.strictEqual(await api.loadESLint(), api.ESLint);
70+
});
71+
72+
it("should return LegacyESLint when useFlatConfig is not provided and ESLINT_USE_FLAT_CONFIG is false", async () => {
73+
process.env.ESLINT_USE_FLAT_CONFIG = "false";
74+
assert.strictEqual(await api.loadESLint(), LegacyESLint);
75+
});
76+
77+
it("should return ESLint when useFlatConfig is not provided and ESLINT_USE_FLAT_CONFIG is true", async () => {
78+
process.env.ESLINT_USE_FLAT_CONFIG = "true";
79+
assert.strictEqual(await api.loadESLint(), api.ESLint);
80+
});
81+
});
82+
4483
});

tests/lib/eslint/eslint.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ describe("ESLint", () => {
134134
});
135135

136136
describe("ESLint constructor function", () => {
137+
138+
it("should have a static property indicating the configType being used", () => {
139+
assert.strictEqual(ESLint.configType, "flat");
140+
});
141+
137142
it("the default value of 'options.cwd' should be the current working directory.", async () => {
138143
process.chdir(__dirname);
139144
try {

tests/lib/eslint/legacy-eslint.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ describe("LegacyESLint", () => {
114114
});
115115

116116
describe("ESLint constructor function", () => {
117+
118+
it("should have a static property indicating the configType being used", () => {
119+
assert.strictEqual(LegacyESLint.configType, "eslintrc");
120+
});
121+
117122
it("the default value of 'options.cwd' should be the current working directory.", async () => {
118123
process.chdir(__dirname);
119124
try {

0 commit comments

Comments
 (0)