diff --git a/docs/src/integrate/nodejs-api.md b/docs/src/integrate/nodejs-api.md index ce0890c79b6..5d081df3d3b 100644 --- a/docs/src/integrate/nodejs-api.md +++ b/docs/src/integrate/nodejs-api.md @@ -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): diff --git a/lib/api.js b/lib/api.js index 4a689250af7..ab0ec2fcd31 100644 --- a/lib/api.js +++ b/lib/api.js @@ -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} 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 diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js index 49bc0e7579a..97102d3fe0e 100644 --- a/lib/eslint/eslint.js +++ b/lib/eslint/eslint.js @@ -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. diff --git a/lib/eslint/legacy-eslint.js b/lib/eslint/legacy-eslint.js index 251a3890db8..9c86163ef63 100644 --- a/lib/eslint/legacy-eslint.js +++ b/lib/eslint/legacy-eslint.js @@ -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. diff --git a/tests/lib/api.js b/tests/lib/api.js index abcbea5aef1..71a5f42930a 100644 --- a/tests/lib/api.js +++ b/tests/lib/api.js @@ -10,7 +10,8 @@ //----------------------------------------------------------------------------- const assert = require("chai").assert, - api = require("../../lib/api"); + api = require("../../lib/api"), + { LegacyESLint } = require("../../lib/eslint/legacy-eslint"); //----------------------------------------------------------------------------- // Tests @@ -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); + }); + }); + }); diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index 3bec83d441e..9360d39449d 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -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 { diff --git a/tests/lib/eslint/legacy-eslint.js b/tests/lib/eslint/legacy-eslint.js index 7bc70d4be93..60b40cb5cd6 100644 --- a/tests/lib/eslint/legacy-eslint.js +++ b/tests/lib/eslint/legacy-eslint.js @@ -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 {