From a1f7ad77e2da00ac7d6daade547fe6bef4ef6003 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Fri, 22 Oct 2021 22:06:14 +0200 Subject: [PATCH] Fix: allow `baseConfig` to extend preloaded plugin config (fixes #15079) (#15187) * Fix: allow `baseConfig` to extend preloaded plugin config (fixes #15079) * Remove CLIEngine#addPlugin --- lib/cli-engine/cli-engine.js | 31 ++----- lib/eslint/eslint.js | 17 +--- tests/lib/cli-engine/cli-engine.js | 141 +++++++++++++++++------------ tests/lib/eslint/eslint.js | 30 ++++++ 4 files changed, 125 insertions(+), 94 deletions(-) diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js index aae71607d2c..e3647018d26 100644 --- a/lib/cli-engine/cli-engine.js +++ b/lib/cli-engine/cli-engine.js @@ -570,8 +570,10 @@ class CLIEngine { /** * Creates a new instance of the core CLI engine. * @param {CLIEngineOptions} providedOptions The options for this instance. + * @param {Object} [additionalData] Additional settings that are not CLIEngineOptions. + * @param {Record|null} [additionalData.preloadedPlugins] Preloaded plugins. */ - constructor(providedOptions) { + constructor(providedOptions, { preloadedPlugins } = {}) { const options = Object.assign( Object.create(null), defaultOptions, @@ -584,6 +586,13 @@ class CLIEngine { } const additionalPluginPool = new Map(); + + if (preloadedPlugins) { + for (const [id, plugin] of Object.entries(preloadedPlugins)) { + additionalPluginPool.set(id, plugin); + } + } + const cacheFilePath = getCacheFile( options.cacheLocation || options.cacheFile, options.cwd @@ -698,26 +707,6 @@ class CLIEngine { }); } - - /** - * Add a plugin by passing its configuration - * @param {string} name Name of the plugin. - * @param {Plugin} pluginObject Plugin configuration object. - * @returns {void} - */ - addPlugin(name, pluginObject) { - const { - additionalPluginPool, - configArrayFactory, - lastConfigArrays - } = internalSlotsMap.get(this); - - additionalPluginPool.set(name, pluginObject); - configArrayFactory.clearCache(); - lastConfigArrays.length = 1; - lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile(); - } - /** * Resolves the patterns passed into executeOnFiles() into glob-based patterns * for easier handling. diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js index b4a1d028db9..8886d45ab43 100644 --- a/lib/eslint/eslint.js +++ b/lib/eslint/eslint.js @@ -54,7 +54,7 @@ const { version } = require("../../package.json"); * @property {string} [ignorePath] The ignore file to use instead of .eslintignore. * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance * @property {string} [overrideConfigFile] The configuration file to use. - * @property {Record} [plugins] An array of plugin implementations. + * @property {Record|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation. * @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives. * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD. * @property {string[]} [rulePaths] An array of directories to load custom rules from. @@ -433,26 +433,13 @@ class ESLint { */ constructor(options = {}) { const processedOptions = processOptions(options); - const cliEngine = new CLIEngine(processedOptions); + const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins }); const { - additionalPluginPool, configArrayFactory, lastConfigArrays } = getCLIEngineInternalSlots(cliEngine); let updated = false; - /* - * Address `plugins` to add plugin implementations. - * Operate the `additionalPluginPool` internal slot directly to avoid - * using `addPlugin(id, plugin)` method that resets cache everytime. - */ - if (options.plugins) { - for (const [id, plugin] of Object.entries(options.plugins)) { - additionalPluginPool.set(id, plugin); - updated = true; - } - } - /* * Address `overrideConfig` to set override config. * Operate the `configArrayFactory` internal slot directly because this diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 864bd7dda3d..31f59bf51b4 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -73,14 +73,13 @@ describe("CLIEngine", () => { * @private */ function cliEngineWithPlugins(options) { - const engine = new CLIEngine(options); - - // load the mocked plugins - engine.addPlugin(examplePluginName, examplePlugin); - engine.addPlugin(examplePluginNameWithNamespace, examplePlugin); - engine.addPlugin(examplePreprocessorName, require("../../fixtures/processors/custom-processor")); - - return engine; + return new CLIEngine(options, { + preloadedPlugins: { + [examplePluginName]: examplePlugin, + [examplePluginNameWithNamespace]: examplePlugin, + [examplePreprocessorName]: require("../../fixtures/processors/custom-processor") + } + }); } // copy into clean area so as not to get "infected" by this project's .eslintrc files @@ -2151,10 +2150,16 @@ describe("CLIEngine", () => { useEslintrc: false, plugins: ["test"], rules: { "test/example-rule": 1 } + }, { + preloadedPlugins: { + "eslint-plugin-test": { + rules: { + "example-rule": require("../../fixtures/rules/custom-rule") + } + } + } }); - engine.addPlugin("eslint-plugin-test", { rules: { "example-rule": require("../../fixtures/rules/custom-rule") } }); - const report = engine.executeOnFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); assert.strictEqual(report.results.length, 1); @@ -2868,16 +2873,18 @@ describe("CLIEngine", () => { }, extensions: ["js", "txt"], cwd: path.join(fixtureDir, "..") - }); - - engine.addPlugin("test-processor", { - processors: { - ".txt": { - preprocess(text) { - return [text]; - }, - postprocess(messages) { - return messages[0]; + }, { + preloadedPlugins: { + "test-processor": { + processors: { + ".txt": { + preprocess(text) { + return [text]; + }, + postprocess(messages) { + return messages[0]; + } + } } } } @@ -2911,17 +2918,19 @@ describe("CLIEngine", () => { }, extensions: ["js", "txt"], cwd: path.join(fixtureDir, "..") - }); - - engine.addPlugin("test-processor", { - processors: { - ".txt": { - preprocess(text) { - return [text.replace("a()", "b()")]; - }, - postprocess(messages) { - messages[0][0].ruleId = "post-processed"; - return messages[0]; + }, { + preloadedPlugins: { + "test-processor": { + processors: { + ".txt": { + preprocess(text) { + return [text.replace("a()", "b()")]; + }, + postprocess(messages) { + messages[0][0].ruleId = "post-processed"; + return messages[0]; + } + } } } } @@ -2955,17 +2964,19 @@ describe("CLIEngine", () => { }, extensions: ["js", "txt"], ignore: false - }); - - engine.addPlugin("test-processor", { - processors: { - ".txt": { - preprocess(text) { - return [text.replace("a()", "b()")]; - }, - postprocess(messages) { - messages[0][0].ruleId = "post-processed"; - return messages[0]; + }, { + preloadedPlugins: { + "test-processor": { + processors: { + ".txt": { + preprocess(text) { + return [text.replace("a()", "b()")]; + }, + postprocess(messages) { + messages[0][0].ruleId = "post-processed"; + return messages[0]; + } + } } } } @@ -3007,11 +3018,13 @@ describe("CLIEngine", () => { extensions: ["js", "txt"], ignore: false, fix: true - }); - - engine.addPlugin("test-processor", { - processors: { - ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) + }, { + preloadedPlugins: { + "test-processor": { + processors: { + ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) + } + } } }); @@ -3031,10 +3044,16 @@ describe("CLIEngine", () => { extensions: ["js", "txt"], ignore: false, fix: true + }, { + preloadedPlugins: { + "test-processor": { + processors: { + ".html": HTML_PROCESSOR + } + } + } }); - engine.addPlugin("test-processor", { processors: { ".html": HTML_PROCESSOR } }); - const report = engine.executeOnText("", "foo.html"); assert.strictEqual(report.results[0].messages.length, 1); @@ -3050,11 +3069,13 @@ describe("CLIEngine", () => { }, extensions: ["js", "txt"], ignore: false - }); - - engine.addPlugin("test-processor", { - processors: { - ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) + }, { + preloadedPlugins: { + "test-processor": { + processors: { + ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) + } + } } }); @@ -4897,10 +4918,14 @@ describe("CLIEngine", () => { assert(engine.getRules().has("node/no-deprecated-api"), "node/no-deprecated-api is present"); }); - it("should expose the rules of the plugin that is added by 'addPlugin'.", () => { - const engine = new CLIEngine({ plugins: ["foo"] }); - - engine.addPlugin("foo", require("eslint-plugin-node")); + it("should expose the list of rules from a preloaded plugin", () => { + const engine = new CLIEngine({ + plugins: ["foo"] + }, { + preloadedPlugins: { + foo: require("eslint-plugin-node") + } + }); assert(engine.getRules().has("foo/no-deprecated-api"), "foo/no-deprecated-api is present"); }); diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index 1a51a65d841..eaf4aaaaf05 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -2095,6 +2095,36 @@ describe("ESLint", () => { assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule"); }); + it("should return two messages when executing with `baseConfig` that extends preloaded plugin config", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + baseConfig: { + extends: ["plugin:test/preset"] + }, + plugins: { + test: { + rules: { + "example-rule": require("../../fixtures/rules/custom-rule") + }, + configs: { + preset: { + rules: { + "test/example-rule": 1 + }, + plugins: ["test"] + } + } + } + } + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule"); + }); + it("should load plugins from the `loadPluginsRelativeTo` directory, if specified", async () => { eslint = new ESLint({ resolvePluginsRelativeTo: getFixturePath("plugins"),