From 5dbacb25c65ec2b359a7c278b77180397e6ecb77 Mon Sep 17 00:00:00 2001 From: Gyandeep Singh Date: Sun, 21 May 2017 14:29:18 -0500 Subject: [PATCH 1/3] Update: Add Fixer method to Linter API --- docs/developer-guide/nodejs-api.md | 32 ++++++++++++ lib/cli-engine.js | 80 +----------------------------- lib/linter.js | 74 ++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 80 deletions(-) diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md index 03a1dd794c3..06ed5e6e2a6 100644 --- a/docs/developer-guide/nodejs-api.md +++ b/docs/developer-guide/nodejs-api.md @@ -147,6 +147,38 @@ console.log(code.text); // "var foo = bar;" In this way, you can retrieve the text and AST used for the last run of `linter.verify()`. +### verifyAndFix() + +This method is similar to `verify` method when it comes to verifying the code against the rules but it also runs the fixer logic from all the fix compatible rules and return the object which has the fixed source code in it. +The lifecycle is that it verifies the code and then runs the fix on it and this is done at most 10 times right now. + +```js +var Linter = require("eslint").Linter; +var linter = new Linter(); + +var messages = linter.verifyAndFix("var foo", { + rules: { + semi: 2 + } +}, { filename: "foo.js" }); +``` + +Output object from this method: + +```js +{ + fixed: true, + text: "var foo;", + messages: [] +} +``` + +The information available is: + +* `fixed` - True, if the code was fixed. +* `text` - Fixed code text (might be the same as input if no fixes were applied). +* `messages` - Collection of all messages for the given code (It has the same information as explained above under `verify` block). + ## linter The `eslint.linter` object (deprecated) is an instance of the `Linter` class as defined [above](#Linter). `eslint.linter` exists for backwards compatibility, but we do not recommend using it because any mutations to it are shared among every module that uses `eslint`. Instead, please create your own instance of `eslint.Linter`. diff --git a/lib/cli-engine.js b/lib/cli-engine.js index 46628c1e4a2..48f484a79b4 100644 --- a/lib/cli-engine.js +++ b/lib/cli-engine.js @@ -23,11 +23,9 @@ const fs = require("fs"), Config = require("./config"), fileEntryCache = require("file-entry-cache"), globUtil = require("./util/glob-util"), - SourceCodeFixer = require("./util/source-code-fixer"), validator = require("./config/config-validator"), stringify = require("json-stable-stringify"), hash = require("./util/hash"), - pkg = require("../package.json"); const debug = require("debug")("eslint:cli-engine"); @@ -132,80 +130,6 @@ function calculateStatsPerRun(results) { }); } -/** - * Performs multiple autofix passes over the text until as many fixes as possible - * have been applied. - * @param {string} text The source text to apply fixes to. - * @param {Object} config The ESLint config object to use. - * @param {Object} options The ESLint options object to use. - * @param {string} options.filename The filename from which the text was read. - * @param {boolean} options.allowInlineConfig Flag indicating if inline comments - * should be allowed. - * @param {Linter} linter Linter context - * @returns {Object} The result of the fix operation as returned from the - * SourceCodeFixer. - * @private - */ -function multipassFix(text, config, options, linter) { - const MAX_PASSES = 10; - let messages = [], - fixedResult, - fixed = false, - passNumber = 0; - - /** - * This loop continues until one of the following is true: - * - * 1. No more fixes have been applied. - * 2. Ten passes have been made. - * - * That means anytime a fix is successfully applied, there will be another pass. - * Essentially, guaranteeing a minimum of two passes. - */ - do { - passNumber++; - - debug(`Linting code for ${options.filename} (pass ${passNumber})`); - messages = linter.verify(text, config, options); - - debug(`Generating fixed text for ${options.filename} (pass ${passNumber})`); - fixedResult = SourceCodeFixer.applyFixes(linter.getSourceCode(), messages); - - // stop if there are any syntax errors. - // 'fixedResult.output' is a empty string. - if (messages.length === 1 && messages[0].fatal) { - break; - } - - // keep track if any fixes were ever applied - important for return value - fixed = fixed || fixedResult.fixed; - - // update to use the fixed output instead of the original text - text = fixedResult.output; - - } while ( - fixedResult.fixed && - passNumber < MAX_PASSES - ); - - - /* - * If the last result had fixes, we need to lint again to be sure we have - * the most up-to-date information. - */ - if (fixedResult.fixed) { - fixedResult.messages = linter.verify(text, config, options); - } - - - // ensure the last result properly reflects if fixes were done - fixedResult.fixed = fixed; - fixedResult.output = text; - - return fixedResult; - -} - /** * Processes an source code using ESLint. * @param {string} text The source code to check. @@ -269,10 +193,10 @@ function processText(text, configHelper, filename, fix, allowInlineConfig, linte } else { if (fix) { - fixedResult = multipassFix(text, config, { + fixedResult = linter.verifyAndFix(text, config, { filename, allowInlineConfig - }, linter); + }); messages = fixedResult.messages; } else { messages = linter.verify(text, config, { diff --git a/lib/linter.js b/lib/linter.js index 25af05223d0..0b0d06a8663 100755 --- a/lib/linter.js +++ b/lib/linter.js @@ -27,9 +27,11 @@ const assert = require("assert"), Rules = require("./rules"), timing = require("./timing"), astUtils = require("./ast-utils"), + pkg = require("../package.json"), + SourceCodeFixer = require("./util/source-code-fixer"); - pkg = require("../package.json"); - +const debug = require("debug")("eslint:linter"); +const MAX_AUTOFIX_PASSES = 10; //------------------------------------------------------------------------------ // Typedefs @@ -1185,6 +1187,74 @@ class Linter extends EventEmitter { getDeclaredVariables(node) { return (this.scopeManager && this.scopeManager.getDeclaredVariables(node)) || []; } + + /** + * Performs multiple autofix passes over the text until as many fixes as possible + * have been applied. + * @param {string} text The source text to apply fixes to. + * @param {Object} config The ESLint config object to use. + * @param {Object} options The ESLint options object to use. + * @param {string} options.filename The filename from which the text was read. + * @param {boolean} options.allowInlineConfig Flag indicating if inline comments + * should be allowed. + * @returns {Object} The result of the fix operation as returned from the + * SourceCodeFixer. + */ + verifyAndFix(text, config, options) { + let messages = [], + fixedResult, + fixed = false, + passNumber = 0; + + /** + * This loop continues until one of the following is true: + * + * 1. No more fixes have been applied. + * 2. Ten passes have been made. + * + * That means anytime a fix is successfully applied, there will be another pass. + * Essentially, guaranteeing a minimum of two passes. + */ + do { + passNumber++; + + debug(`Linting code for ${options.filename} (pass ${passNumber})`); + messages = this.verify(text, config, options); + + debug(`Generating fixed text for ${options.filename} (pass ${passNumber})`); + fixedResult = SourceCodeFixer.applyFixes(this.getSourceCode(), messages); + + // stop if there are any syntax errors. + // 'fixedResult.output' is a empty string. + if (messages.length === 1 && messages[0].fatal) { + break; + } + + // keep track if any fixes were ever applied - important for return value + fixed = fixed || fixedResult.fixed; + + // update to use the fixed output instead of the original text + text = fixedResult.output; + + } while ( + fixedResult.fixed && + passNumber < MAX_AUTOFIX_PASSES + ); + + /* + * If the last result had fixes, we need to lint again to be sure we have + * the most up-to-date information. + */ + if (fixedResult.fixed) { + fixedResult.messages = this.verify(text, config, options); + } + + // ensure the last result properly reflects if fixes were done + fixedResult.fixed = fixed; + fixedResult.output = text; + + return fixedResult; + } } // methods that exist on SourceCode object From 0a38c728ab80e964aa1ddd7106eb480b00a30e6e Mon Sep 17 00:00:00 2001 From: Gyandeep Singh Date: Sat, 3 Jun 2017 11:14:46 -0500 Subject: [PATCH 2/3] fix wording in doc --- docs/developer-guide/nodejs-api.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md index 06ed5e6e2a6..f48670896be 100644 --- a/docs/developer-guide/nodejs-api.md +++ b/docs/developer-guide/nodejs-api.md @@ -149,8 +149,7 @@ In this way, you can retrieve the text and AST used for the last run of `linter. ### verifyAndFix() -This method is similar to `verify` method when it comes to verifying the code against the rules but it also runs the fixer logic from all the fix compatible rules and return the object which has the fixed source code in it. -The lifecycle is that it verifies the code and then runs the fix on it and this is done at most 10 times right now. +This method is similar to verify except that it also runs autofixing logic, similar to the `--fix` flag on the command line. The result object will contain the autofixed code, along with any remaining linting messages for the code that were not autofixed. ```js var Linter = require("eslint").Linter; From c5e388959df0aa3c97ea5affe11d4eb8623345a3 Mon Sep 17 00:00:00 2001 From: Gyandeep Singh Date: Wed, 7 Jun 2017 15:35:34 -0500 Subject: [PATCH 3/3] add a test for linter's verifyAndFix method --- tests/lib/linter.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/lib/linter.js b/tests/lib/linter.js index ed5fb2238cc..9c47fafe521 100644 --- a/tests/lib/linter.js +++ b/tests/lib/linter.js @@ -70,4 +70,18 @@ describe("Linter", () => { }); }); }); + + describe("verifyAndFix", () => { + it("Fixes the code", () => { + const linter = new Linter(); + const messages = linter.verifyAndFix("var a", { + rules: { + semi: 2 + } + }, { filename: "test.js" }); + + assert.equal(messages.output, "var a;", "Fixes were applied correctly"); + assert.isTrue(messages.fixed); + }); + }); });