123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- /**
- * @fileoverview An object that caches and applies source code fixes.
- * @author Nicholas C. Zakas
- */
- "use strict";
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
- const debug = require("debug")("eslint:source-code-fixer");
- //------------------------------------------------------------------------------
- // Helpers
- //------------------------------------------------------------------------------
- const BOM = "\uFEFF";
- /**
- * Compares items in a messages array by range.
- * @param {Message} a The first message.
- * @param {Message} b The second message.
- * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
- * @private
- */
- function compareMessagesByFixRange(a, b) {
- return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1];
- }
- /**
- * Compares items in a messages array by line and column.
- * @param {Message} a The first message.
- * @param {Message} b The second message.
- * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
- * @private
- */
- function compareMessagesByLocation(a, b) {
- return a.line - b.line || a.column - b.column;
- }
- //------------------------------------------------------------------------------
- // Public Interface
- //------------------------------------------------------------------------------
- /**
- * Utility for apply fixes to source code.
- * @constructor
- */
- function SourceCodeFixer() {
- Object.freeze(this);
- }
- /**
- * Applies the fixes specified by the messages to the given text. Tries to be
- * smart about the fixes and won't apply fixes over the same area in the text.
- * @param {string} sourceText The text to apply the changes to.
- * @param {Message[]} messages The array of messages reported by ESLint.
- * @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
- * @returns {Object} An object containing the fixed text and any unfixed messages.
- */
- SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
- debug("Applying fixes");
- if (shouldFix === false) {
- debug("shouldFix parameter was false, not attempting fixes");
- return {
- fixed: false,
- messages,
- output: sourceText
- };
- }
- // clone the array
- const remainingMessages = [],
- fixes = [],
- bom = sourceText.startsWith(BOM) ? BOM : "",
- text = bom ? sourceText.slice(1) : sourceText;
- let lastPos = Number.NEGATIVE_INFINITY,
- output = bom;
- /**
- * Try to use the 'fix' from a problem.
- * @param {Message} problem The message object to apply fixes from
- * @returns {boolean} Whether fix was successfully applied
- */
- function attemptFix(problem) {
- const fix = problem.fix;
- const start = fix.range[0];
- const end = fix.range[1];
- // Remain it as a problem if it's overlapped or it's a negative range
- if (lastPos >= start || start > end) {
- remainingMessages.push(problem);
- return false;
- }
- // Remove BOM.
- if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
- output = "";
- }
- // Make output to this fix.
- output += text.slice(Math.max(0, lastPos), Math.max(0, start));
- output += fix.text;
- lastPos = end;
- return true;
- }
- messages.forEach(problem => {
- if (Object.prototype.hasOwnProperty.call(problem, "fix")) {
- fixes.push(problem);
- } else {
- remainingMessages.push(problem);
- }
- });
- if (fixes.length) {
- debug("Found fixes to apply");
- let fixesWereApplied = false;
- for (const problem of fixes.sort(compareMessagesByFixRange)) {
- if (typeof shouldFix !== "function" || shouldFix(problem)) {
- attemptFix(problem);
- /*
- * The only time attemptFix will fail is if a previous fix was
- * applied which conflicts with it. So we can mark this as true.
- */
- fixesWereApplied = true;
- } else {
- remainingMessages.push(problem);
- }
- }
- output += text.slice(Math.max(0, lastPos));
- return {
- fixed: fixesWereApplied,
- messages: remainingMessages.sort(compareMessagesByLocation),
- output
- };
- }
- debug("No fixes to apply");
- return {
- fixed: false,
- messages,
- output: bom + text
- };
- };
- module.exports = SourceCodeFixer;
|