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

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

Merged
merged 2 commits into from Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
31 changes: 10 additions & 21 deletions lib/cli-engine/cli-engine.js
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
17 changes: 2 additions & 15 deletions lib/eslint/eslint.js
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
141 changes: 83 additions & 58 deletions tests/lib/cli-engine/cli-engine.js
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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];
}
}
}
}
}
Expand Down Expand Up @@ -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];
}
}
}
}
}
Expand Down Expand Up @@ -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];
}
}
}
}
}
Expand Down Expand Up @@ -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)
}
}
}
});

Expand All @@ -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);
Expand All @@ -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)
}
}
}
});

Expand Down Expand Up @@ -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");
});
Expand Down
30 changes: 30 additions & 0 deletions tests/lib/eslint/eslint.js
Expand Up @@ -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"),
Expand Down