153 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * @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;
 |