Skip to content
Permalink
Browse files
Fix: allow baseConfig to extend preloaded plugin config (fixes #15079
…) (#15187)

* Fix: allow `baseConfig` to extend preloaded plugin config (fixes #15079)

* Remove CLIEngine#addPlugin
  • Loading branch information
mdjermanovic committed Oct 22, 2021
1 parent 3d370fb commit a1f7ad77e2da00ac7d6daade547fe6bef4ef6003
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 94 deletions.
@@ -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<string,Plugin>|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.
@@ -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<string,Plugin>} [plugins] An array of plugin implementations.
* @property {Record<string,Plugin>|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
@@ -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("<script>foo</script>", "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");
});
@@ -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"),

0 comments on commit a1f7ad7

Please sign in to comment.