From c2cd7b4a18057ca6067bdfc16de771dc5d90c0ea Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 18 Jun 2021 12:57:51 -0700 Subject: [PATCH] New: Add ESLint#getRulesMetaForResults() (refs #13654) (#14716) * New: Add ESLint#getRulesMetaForResults() (refs #13654) * Update docs * Update docs/developer-guide/nodejs-api.md Co-authored-by: Brandon Mills Co-authored-by: Brandon Mills --- docs/developer-guide/nodejs-api.md | 21 +++++++++ lib/eslint/eslint.js | 33 +++++++++++++ tests/lib/eslint/eslint.js | 75 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md index f1e21584e32..28b0fff9070 100644 --- a/docs/developer-guide/nodejs-api.md +++ b/docs/developer-guide/nodejs-api.md @@ -10,6 +10,7 @@ While ESLint is designed to be run on the command line, it's possible to use ESL * [constructor()][eslint-constructor] * [lintFiles()][eslint-lintfiles] * [lintText()][eslint-linttext] + * [getRulesMetaForResults()](eslint-getrulesmetaforresults) * [calculateConfigForFile()][eslint-calculateconfigforfile] * [isPathIgnored()][eslint-ispathignored] * [loadFormatter()][eslint-loadformatter] @@ -205,6 +206,25 @@ The second parameter `options` is omittable. * (`Promise`)
The promise that will be fulfilled with an array of [LintResult] objects. This is an array (despite there being only one lint result) in order to keep the interfaces between this and the [`eslint.lintFiles()`][eslint-lintfiles] method similar. +### ◆ eslint.getRulesMetaForResults(results) + +```js +const results = await eslint.lintFiles(patterns); +const rulesMeta = eslint.getRulesMetaForResults(results); +``` + +This method returns an object containing meta information for each rule that triggered a lint error in the given `results`. + +#### Parameters + +* `results` (`LintResult[]`)
+ An array of [LintResult] objects returned from a call to `ESLint#lintFiles()` or `ESLint#lintText()`. + +#### Return Value + +* (`Object`)
+ An object whose property names are the rule IDs from the `results` and whose property values are the rule's meta information (if available). + ### ◆ eslint.calculateConfigForFile(filePath) ```js @@ -1389,6 +1409,7 @@ ruleTester.run("my-rule", myRule, { [eslint-constructor]: #-new-eslintoptions [eslint-lintfiles]: #-eslintlintfilespatterns [eslint-linttext]: #-eslintlinttextcode-options +[eslint-getrulesmetaforresults]: #-eslintgetrulesmetaforresultsresults [eslint-calculateconfigforfile]: #-eslintcalculateconfigforfilefilepath [eslint-ispathignored]: #-eslintispathignoredfilepath [eslint-loadformatter]: #-eslintloadformatternameorpath diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js index c387ca72c3a..056e04b5945 100644 --- a/lib/eslint/eslint.js +++ b/lib/eslint/eslint.js @@ -514,6 +514,39 @@ class ESLint { return CLIEngine.getErrorResults(results); } + /** + * Returns meta objects for each rule represented in the lint results. + * @param {LintResult[]} results The results to fetch rules meta for. + * @returns {Object} A mapping of ruleIds to rule meta objects. + */ + getRulesMetaForResults(results) { + + const resultRuleIds = new Set(); + + // first gather all ruleIds from all results + + for (const result of results) { + for (const { ruleId } of result.messages) { + resultRuleIds.add(ruleId); + } + } + + // create a map of all rules in the results + + const { cliEngine } = privateMembersMap.get(this); + const rules = cliEngine.getRules(); + const resultRules = new Map(); + + for (const [ruleId, rule] of rules) { + if (resultRuleIds.has(ruleId)) { + resultRules.set(ruleId, rule); + } + } + + return createRulesMeta(resultRules); + + } + /** * Executes the current configuration on an array of file and directory names. * @param {string[]} patterns An array of file and directory names. diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index 0845d697083..c5a1b7360c0 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -22,6 +22,7 @@ const shell = require("shelljs"); const { CascadingConfigArrayFactory } = require("@eslint/eslintrc/lib/cascading-config-array-factory"); const hash = require("../../../lib/cli-engine/hash"); const { unIndent, createCustomTeardown } = require("../../_utils"); +const coreRules = require("../../../lib/rules"); //------------------------------------------------------------------------------ // Tests @@ -4790,6 +4791,80 @@ describe("ESLint", () => { }); }); + describe("getRulesMetaForResults()", () => { + it("should return empty object when there are no linting errors", async () => { + const engine = new ESLint({ + useEslintrc: false + }); + + const rulesMeta = engine.getRulesMetaForResults([]); + + assert.strictEqual(Object.keys(rulesMeta).length, 0); + }); + + it("should return one rule meta when there is a linting error", async () => { + const engine = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { + semi: 2 + } + } + }); + + const results = await engine.lintText("a"); + const rulesMeta = engine.getRulesMetaForResults(results); + + assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); + }); + + it("should return multiple rule meta when there are multiple linting errors", async () => { + const engine = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { + semi: 2, + quotes: [2, "double"] + } + } + }); + + const results = await engine.lintText("'a'"); + const rulesMeta = engine.getRulesMetaForResults(results); + + assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); + assert.strictEqual(rulesMeta.quotes, coreRules.get("quotes").meta); + }); + + it("should return multiple rule meta when there are multiple linting errors from a plugin", async () => { + const nodePlugin = require("eslint-plugin-node"); + const engine = new ESLint({ + useEslintrc: false, + plugins: { + node: nodePlugin + }, + overrideConfig: { + plugins: ["node"], + rules: { + "node/no-new-require": 2, + semi: 2, + quotes: [2, "double"] + } + } + }); + + const results = await engine.lintText("new require('hi')"); + const rulesMeta = engine.getRulesMetaForResults(results); + + assert.strictEqual(rulesMeta.semi, coreRules.get("semi").meta); + assert.strictEqual(rulesMeta.quotes, coreRules.get("quotes").meta); + assert.strictEqual( + rulesMeta["node/no-new-require"], + nodePlugin.rules["no-new-require"].meta + ); + }); + }); + describe("outputFixes()", () => { afterEach(() => { sinon.verifyAndRestore();