UNPKG

3.86 kBJavaScriptView Raw
1/**
2 * @fileoverview An object that caches and applies source code fixes.
3 * @author Nicholas C. Zakas
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const debug = require("debug")("eslint:text-fixer");
12
13//------------------------------------------------------------------------------
14// Helpers
15//------------------------------------------------------------------------------
16
17const BOM = "\uFEFF";
18
19/**
20 * Compares items in a messages array by line and column.
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.
24 * @private
25 */
26function compareMessagesByLocation(a, b) {
27 const lineDiff = a.line - b.line;
28
29 if (lineDiff === 0) {
30 return a.column - b.column;
31 } else {
32 return lineDiff;
33 }
34}
35
36//------------------------------------------------------------------------------
37// Public Interface
38//------------------------------------------------------------------------------
39
40/**
41 * Utility for apply fixes to source code.
42 * @constructor
43 */
44function SourceCodeFixer() {
45 Object.freeze(this);
46}
47
48/**
49 * Applies the fixes specified by the messages to the given text. Tries to be
50 * smart about the fixes and won't apply fixes over the same area in the text.
51 * @param {SourceCode} sourceCode The source code to apply the changes to.
52 * @param {Message[]} messages The array of messages reported by ESLint.
53 * @returns {Object} An object containing the fixed text and any unfixed messages.
54 */
55SourceCodeFixer.applyFixes = function(sourceCode, messages) {
56
57 debug("Applying fixes");
58
59 if (!sourceCode) {
60 debug("No source code to fix");
61 return {
62 fixed: false,
63 messages,
64 output: ""
65 };
66 }
67
68 // clone the array
69 const remainingMessages = [],
70 fixes = [],
71 text = sourceCode.text;
72 let lastFixPos = text.length + 1,
73 prefix = (sourceCode.hasBOM ? BOM : "");
74
75 messages.forEach(function(problem) {
76 if (problem.hasOwnProperty("fix")) {
77 fixes.push(problem);
78 } else {
79 remainingMessages.push(problem);
80 }
81 });
82
83 if (fixes.length) {
84 debug("Found fixes to apply");
85
86 // sort in reverse order of occurrence
87 fixes.sort(function(a, b) {
88 return b.fix.range[1] - a.fix.range[1] || b.fix.range[0] - a.fix.range[0];
89 });
90
91 // split into array of characters for easier manipulation
92 const chars = text.split("");
93
94 fixes.forEach(function(problem) {
95 const fix = problem.fix;
96 let start = fix.range[0];
97 const end = fix.range[1];
98 let insertionText = fix.text;
99
100 if (end < lastFixPos) {
101 if (start < 0) {
102
103 // Remove BOM.
104 prefix = "";
105 start = 0;
106 }
107
108 if (start === 0 && insertionText[0] === BOM) {
109
110 // Set BOM.
111 prefix = BOM;
112 insertionText = insertionText.slice(1);
113 }
114
115 chars.splice(start, end - start, insertionText);
116 lastFixPos = start;
117 } else {
118 remainingMessages.push(problem);
119 }
120 });
121
122 return {
123 fixed: true,
124 messages: remainingMessages.sort(compareMessagesByLocation),
125 output: prefix + chars.join("")
126 };
127 } else {
128 debug("No fixes to apply");
129 return {
130 fixed: false,
131 messages,
132 output: prefix + text
133 };
134 }
135};
136
137module.exports = SourceCodeFixer;