Skip to content

Commit

Permalink
feat: Add loadESLint() API method for v9 (#18097)
Browse files Browse the repository at this point in the history
* feat: Add loadESLint() API method for v9

refs #18075

* Fix docs

* Add more tests using environment variables
  • Loading branch information
nzakas committed Feb 9, 2024
1 parent f1c7e6f commit 53f0f47
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 2 deletions.
43 changes: 43 additions & 0 deletions docs/src/integrate/nodejs-api.md
Expand Up @@ -442,6 +442,49 @@ The `LoadedFormatter` value is the object to convert the [LintResult] objects to

---

## loadESLint()

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:

```js
const { loadESLint } = require("eslint");

// loads the default ESLint that the CLI would use based on process.cwd()
const DefaultESLint = await loadESLint();

// loads the default ESLint that the CLI would use based on the provided cwd
const CwdDefaultESLint = await loadESLint({ cwd: "/foo/bar" });

// loads the flat config version specifically
const FlatESLint = await loadESLint({ useFlatConfig: true });

// loads the legacy version specifically
const LegacyESLint = await loadESLint({ useFlatConfig: false });
```

You can then use the returned constructor to instantiate a new `ESLint` instance, like this:

```js
// loads the default ESLint that the CLI would use based on process.cwd()
const DefaultESLint = await loadESLint();
const eslint = new DefaultESLint();
```

If you're ever unsure which config system the returned constructor uses, check the `configType` property, which is either `"flat"` or `"eslintrc"`:

```js
// loads the default ESLint that the CLI would use based on process.cwd()
const DefaultESLint = await loadESLint();

if (DefaultESLint.configType === "flat") {
// do something specific to flat config
}
```

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.

---

## SourceCode

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):
Expand Down
26 changes: 25 additions & 1 deletion lib/api.js
Expand Up @@ -9,17 +9,41 @@
// Requirements
//-----------------------------------------------------------------------------

const { ESLint } = require("./eslint/eslint");
const { ESLint, shouldUseFlatConfig } = require("./eslint/eslint");
const { LegacyESLint } = require("./eslint/legacy-eslint");
const { Linter } = require("./linter");
const { RuleTester } = require("./rule-tester");
const { SourceCode } = require("./source-code");

//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------

/**
* Loads the correct ESLint constructor given the options.
* @param {Object} [options] The options object
* @param {boolean} [options.useFlatConfig] Whether or not to use a flat config
* @returns {Promise<ESLint|LegacyESLint>} The ESLint constructor
*/
async function loadESLint({ useFlatConfig } = {}) {

/*
* Note: The v8.x version of this function also accepted a `cwd` option, but
* it is not used in this implementation so we silently ignore it.
*/

const shouldESLintUseFlatConfig = useFlatConfig ?? (await shouldUseFlatConfig());

return shouldESLintUseFlatConfig ? ESLint : LegacyESLint;
}

//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------

module.exports = {
Linter,
loadESLint,
ESLint,
RuleTester,
SourceCode
Expand Down
6 changes: 6 additions & 0 deletions lib/eslint/eslint.js
Expand Up @@ -565,6 +565,12 @@ function createExtraneousResultsError() {
*/
class ESLint {

/**
* The type of configuration used by this class.
* @type {string}
*/
static configType = "flat";

/**
* Creates a new instance of the main ESLint API.
* @param {ESLintOptions} options The options for this instance.
Expand Down
6 changes: 6 additions & 0 deletions lib/eslint/legacy-eslint.js
Expand Up @@ -438,6 +438,12 @@ function compareResultsByFilePath(a, b) {
*/
class LegacyESLint {

/**
* The type of configuration used by this class.
* @type {string}
*/
static configType = "eslintrc";

/**
* Creates a new instance of the main ESLint API.
* @param {LegacyESLintOptions} options The options for this instance.
Expand Down
41 changes: 40 additions & 1 deletion tests/lib/api.js
Expand Up @@ -10,7 +10,8 @@
//-----------------------------------------------------------------------------

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

//-----------------------------------------------------------------------------
// Tests
Expand Down Expand Up @@ -41,4 +42,42 @@ describe("api", () => {
it("should have SourceCode exposed", () => {
assert.isFunction(api.SourceCode);
});

describe("loadESLint", () => {

afterEach(() => {
delete process.env.ESLINT_USE_FLAT_CONFIG;
});

it("should be a function", () => {
assert.isFunction(api.loadESLint);
});

it("should return a Promise", () => {
assert.instanceOf(api.loadESLint(), Promise);
});

it("should return ESLint when useFlatConfig is true", async () => {
assert.strictEqual(await api.loadESLint({ useFlatConfig: true }), api.ESLint);
});

it("should return LegacyESLint when useFlatConfig is false", async () => {
assert.strictEqual(await api.loadESLint({ useFlatConfig: false }), LegacyESLint);
});

it("should return ESLint when useFlatConfig is not provided", async () => {
assert.strictEqual(await api.loadESLint(), api.ESLint);
});

it("should return LegacyESLint when useFlatConfig is not provided and ESLINT_USE_FLAT_CONFIG is false", async () => {
process.env.ESLINT_USE_FLAT_CONFIG = "false";
assert.strictEqual(await api.loadESLint(), LegacyESLint);
});

it("should return ESLint when useFlatConfig is not provided and ESLINT_USE_FLAT_CONFIG is true", async () => {
process.env.ESLINT_USE_FLAT_CONFIG = "true";
assert.strictEqual(await api.loadESLint(), api.ESLint);
});
});

});
5 changes: 5 additions & 0 deletions tests/lib/eslint/eslint.js
Expand Up @@ -134,6 +134,11 @@ describe("ESLint", () => {
});

describe("ESLint constructor function", () => {

it("should have a static property indicating the configType being used", () => {
assert.strictEqual(ESLint.configType, "flat");
});

it("the default value of 'options.cwd' should be the current working directory.", async () => {
process.chdir(__dirname);
try {
Expand Down
5 changes: 5 additions & 0 deletions tests/lib/eslint/legacy-eslint.js
Expand Up @@ -114,6 +114,11 @@ describe("LegacyESLint", () => {
});

describe("ESLint constructor function", () => {

it("should have a static property indicating the configType being used", () => {
assert.strictEqual(LegacyESLint.configType, "eslintrc");
});

it("the default value of 'options.cwd' should be the current working directory.", async () => {
process.chdir(__dirname);
try {
Expand Down

0 comments on commit 53f0f47

Please sign in to comment.