2 * @fileoverview An object that caches and applies source code fixes.
3 * @author Nicholas C. Zakas
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const debug = require("debug")("eslint:source-code-fixer");
13 //------------------------------------------------------------------------------
15 //------------------------------------------------------------------------------
20 * Compares items in a messages array by range.
21 * @param {Message} a The first message.
22 * @param {Message} b The second message.
23 * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
26 function compareMessagesByFixRange(a, b) {
27 return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1];
31 * Compares items in a messages array by line and column.
32 * @param {Message} a The first message.
33 * @param {Message} b The second message.
34 * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
37 function compareMessagesByLocation(a, b) {
38 return a.line - b.line || a.column - b.column;
41 //------------------------------------------------------------------------------
43 //------------------------------------------------------------------------------
46 * Utility for apply fixes to source code.
49 function SourceCodeFixer() {
54 * Applies the fixes specified by the messages to the given text. Tries to be
55 * smart about the fixes and won't apply fixes over the same area in the text.
56 * @param {string} sourceText The text to apply the changes to.
57 * @param {Message[]} messages The array of messages reported by ESLint.
58 * @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
59 * @returns {Object} An object containing the fixed text and any unfixed messages.
61 SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
62 debug("Applying fixes");
64 if (shouldFix === false) {
65 debug("shouldFix parameter was false, not attempting fixes");
74 const remainingMessages = [],
76 bom = sourceText.startsWith(BOM) ? BOM : "",
77 text = bom ? sourceText.slice(1) : sourceText;
78 let lastPos = Number.NEGATIVE_INFINITY,
82 * Try to use the 'fix' from a problem.
83 * @param {Message} problem The message object to apply fixes from
84 * @returns {boolean} Whether fix was successfully applied
86 function attemptFix(problem) {
87 const fix = problem.fix;
88 const start = fix.range[0];
89 const end = fix.range[1];
91 // Remain it as a problem if it's overlapped or it's a negative range
92 if (lastPos >= start || start > end) {
93 remainingMessages.push(problem);
98 if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
102 // Make output to this fix.
103 output += text.slice(Math.max(0, lastPos), Math.max(0, start));
109 messages.forEach(problem => {
110 if (Object.prototype.hasOwnProperty.call(problem, "fix")) {
113 remainingMessages.push(problem);
118 debug("Found fixes to apply");
119 let fixesWereApplied = false;
121 for (const problem of fixes.sort(compareMessagesByFixRange)) {
122 if (typeof shouldFix !== "function" || shouldFix(problem)) {
126 * The only time attemptFix will fail is if a previous fix was
127 * applied which conflicts with it. So we can mark this as true.
129 fixesWereApplied = true;
131 remainingMessages.push(problem);
134 output += text.slice(Math.max(0, lastPos));
137 fixed: fixesWereApplied,
138 messages: remainingMessages.sort(compareMessagesByLocation),
143 debug("No fixes to apply");
152 module.exports = SourceCodeFixer;