Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add loadESLint() API method for v9 #18097

Merged
merged 3 commits into from Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
26 changes: 25 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,27 @@ describe("api", () => {
it("should have SourceCode exposed", () => {
assert.isFunction(api.SourceCode);
});

describe("loadESLint", () => {
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);
});
Comment on lines +68 to +70
Copy link
Contributor

@snitin315 snitin315 Feb 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a similar test case for LegacyESLint here as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup!

});

});
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