UNPKG

9.45 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
6
7const _require = require('source-map'),
8 SourceMapConsumer = _require.SourceMapConsumer,
9 SourceMapGenerator = _require.SourceMapGenerator;
10
11const lineCounter = require('./utils/lineCounter');
12
13class SourceMap {
14 constructor(mappings, sources) {
15 this.mappings = this.purifyMappings(mappings);
16 this.sources = sources || {};
17 this.lineCount = null;
18 }
19
20 purifyMappings(mappings) {
21 if (Array.isArray(mappings)) {
22 return mappings.filter(mapping => mapping && typeof mapping.original === 'object' && (mapping.original === null || typeof mapping.original.line === 'number' && mapping.original.line > 0 && typeof mapping.original.column === 'number' && mapping.source) && mapping.generated && typeof mapping.generated.line === 'number' && mapping.generated.line > 0 && typeof mapping.generated.column === 'number');
23 }
24
25 return [];
26 }
27
28 getConsumer(map) {
29 return (0, _asyncToGenerator2.default)(function* () {
30 if (map instanceof SourceMapConsumer) {
31 return map;
32 }
33
34 map = typeof map === 'string' ? JSON.parse(map) : map;
35 if (map.sourceRoot) delete map.sourceRoot;
36 return new SourceMapConsumer(map);
37 })();
38 }
39
40 addMap(map, lineOffset = 0, columnOffset = 0) {
41 var _this = this;
42
43 return (0, _asyncToGenerator2.default)(function* () {
44 if (typeof map === 'string' || typeof map === 'object' && map.version) {
45 let consumer = yield _this.getConsumer(map);
46 if (!consumer) return _this;
47 consumer.eachMapping(mapping => {
48 _this.addConsumerMapping(mapping, lineOffset, columnOffset);
49
50 if (!_this.sources[mapping.source]) {
51 _this.sources[mapping.source] = consumer.sourceContentFor(mapping.source, true);
52 }
53 });
54
55 if (consumer.destroy) {
56 // Only needs to happen in source-map 0.7
57 consumer.destroy();
58 }
59 } else if (map.mappings && map.sources) {
60 if (!map.eachMapping) {
61 map = new SourceMap(map.mappings, map.sources);
62 }
63
64 if (lineOffset === 0 && columnOffset === 0) {
65 _this.mappings = _this.mappings.concat(map.mappings);
66 } else {
67 map.eachMapping(mapping => {
68 _this.addMapping(mapping, lineOffset, columnOffset);
69 });
70 }
71
72 Object.keys(map.sources).forEach(sourceName => {
73 if (!_this.sources[sourceName]) {
74 _this.sources[sourceName] = map.sources[sourceName];
75 }
76 });
77 }
78
79 return _this;
80 })();
81 }
82
83 addMapping(mapping, lineOffset = 0, columnOffset = 0) {
84 this.mappings.push({
85 source: mapping.source,
86 name: mapping.name,
87 original: mapping.original,
88 generated: {
89 line: mapping.generated.line + lineOffset,
90 column: mapping.generated.column + columnOffset
91 }
92 });
93 }
94
95 addConsumerMapping(mapping, lineOffset = 0, columnOffset = 0) {
96 let original = null;
97
98 if (typeof mapping.originalLine === 'number' && mapping.originalLine > 0 && typeof mapping.originalColumn === 'number') {
99 original = {
100 line: mapping.originalLine,
101 column: mapping.originalColumn
102 };
103 }
104
105 this.mappings.push({
106 source: original ? mapping.source : null,
107 name: mapping.name,
108 original,
109 generated: {
110 line: mapping.generatedLine + lineOffset,
111 column: mapping.generatedColumn + columnOffset
112 }
113 });
114 }
115
116 eachMapping(callback) {
117 this.mappings.forEach(callback);
118 }
119
120 generateEmptyMap(sourceName, sourceContent) {
121 this.sources[sourceName] = sourceContent;
122 this.lineCount = lineCounter(sourceContent);
123
124 for (let line = 1; line < this.lineCount + 1; line++) {
125 this.addMapping({
126 source: sourceName,
127 original: {
128 line: line,
129 column: 0
130 },
131 generated: {
132 line: line,
133 column: 0
134 }
135 });
136 }
137
138 return this;
139 }
140
141 extendSourceMap(original, extension) {
142 var _this2 = this;
143
144 return (0, _asyncToGenerator2.default)(function* () {
145 if (!(extension instanceof SourceMap)) {
146 extension = yield new SourceMap().addMap(extension);
147 }
148
149 if (!(original instanceof SourceMap)) {
150 original = yield _this2.getConsumer(original);
151 }
152
153 extension.eachMapping(mapping => {
154 let originalMapping = original.originalPositionFor({
155 line: mapping.original.line,
156 column: mapping.original.column
157 });
158
159 if (!originalMapping || !originalMapping.line) {
160 return;
161 }
162
163 _this2.addMapping({
164 source: originalMapping.source,
165 name: originalMapping.name,
166 original: {
167 line: originalMapping.line,
168 column: originalMapping.column
169 },
170 generated: {
171 line: mapping.generated.line,
172 column: mapping.generated.column
173 }
174 });
175
176 if (!_this2.sources[originalMapping.source]) {
177 _this2.sources[originalMapping.source] = original.sourceContentFor(originalMapping.source, true);
178 }
179 });
180
181 if (original.destroy) {
182 // Only needs to happen in source-map 0.7
183 original.destroy();
184 }
185
186 return _this2;
187 })();
188 }
189
190 findClosestGenerated(line, column) {
191 if (line < 1) {
192 throw new Error('Line numbers must be >= 1');
193 }
194
195 if (column < 0) {
196 throw new Error('Column numbers must be >= 0');
197 }
198
199 if (this.mappings.length < 1) {
200 return undefined;
201 }
202
203 let startIndex = 0;
204 let stopIndex = this.mappings.length - 1;
205 let middleIndex = stopIndex + startIndex >>> 1;
206
207 while (startIndex < stopIndex && this.mappings[middleIndex].generated.line !== line) {
208 let mid = this.mappings[middleIndex].generated.line;
209
210 if (line < mid) {
211 stopIndex = middleIndex - 1;
212 } else if (line > mid) {
213 startIndex = middleIndex + 1;
214 }
215
216 middleIndex = stopIndex + startIndex >>> 1;
217 }
218
219 let mapping = this.mappings[middleIndex];
220
221 if (!mapping || mapping.generated.line !== line) {
222 return this.mappings.length - 1;
223 }
224
225 while (middleIndex >= 1 && this.mappings[middleIndex - 1].generated.line === line) {
226 middleIndex--;
227 }
228
229 while (middleIndex < this.mappings.length - 1 && this.mappings[middleIndex + 1].generated.line === line && column > this.mappings[middleIndex].generated.column) {
230 middleIndex++;
231 }
232
233 return middleIndex;
234 }
235
236 findClosest(line, column, key) {
237 if (line < 1) {
238 throw new Error('Line numbers must be >= 1');
239 }
240
241 if (column < 0) {
242 throw new Error('Column numbers must be >= 0');
243 }
244
245 if (this.mappings.length < 1) {
246 return undefined;
247 }
248
249 let startIndex = 0;
250 let stopIndex = this.mappings.length - 1;
251 let middleIndex = Math.floor((stopIndex + startIndex) / 2);
252
253 while (startIndex < stopIndex && this.mappings[middleIndex][key].line !== line) {
254 if (line < this.mappings[middleIndex][key].line) {
255 stopIndex = middleIndex - 1;
256 } else if (line > this.mappings[middleIndex][key].line) {
257 startIndex = middleIndex + 1;
258 }
259
260 middleIndex = Math.floor((stopIndex + startIndex) / 2);
261 }
262
263 var mapping = this.mappings[middleIndex];
264
265 if (!mapping || mapping[key].line !== line) {
266 return this.mappings.length - 1;
267 }
268
269 while (middleIndex >= 1 && this.mappings[middleIndex - 1][key].line === line) {
270 middleIndex--;
271 }
272
273 while (middleIndex < this.mappings.length - 1 && this.mappings[middleIndex + 1][key].line === line && column > this.mappings[middleIndex][key].column) {
274 middleIndex++;
275 }
276
277 return middleIndex;
278 }
279
280 originalPositionFor(generatedPosition) {
281 let index = this.findClosestGenerated(generatedPosition.line, generatedPosition.column);
282 let mapping = this.mappings[index];
283
284 if (!mapping || !mapping.original) {
285 return null;
286 }
287
288 return {
289 source: mapping.source,
290 name: mapping.name,
291 line: mapping.original.line,
292 column: mapping.original.column
293 };
294 }
295
296 generatedPositionFor(originalPosition) {
297 let index = this.findClosest(originalPosition.line, originalPosition.column, 'original');
298 let mapping = this.mappings[index];
299 return {
300 source: mapping.source,
301 name: mapping.name,
302 line: mapping.generated.line,
303 column: mapping.generated.column
304 };
305 }
306
307 sourceContentFor(fileName) {
308 return this.sources[fileName];
309 }
310
311 offset(lineOffset = 0, columnOffset = 0) {
312 this.mappings.map(mapping => {
313 mapping.generated.line = mapping.generated.line + lineOffset;
314 mapping.generated.column = mapping.generated.column + columnOffset;
315 return mapping;
316 });
317
318 if (this.lineCount != null) {
319 this.lineCount += lineOffset;
320 }
321 }
322
323 stringify(file, sourceRoot) {
324 let generator = new SourceMapGenerator({
325 file,
326 sourceRoot
327 });
328 this.eachMapping(mapping => generator.addMapping(mapping));
329 Object.keys(this.sources).forEach(sourceName => generator.setSourceContent(sourceName, this.sources[sourceName]));
330 return generator.toString();
331 }
332
333}
334
335module.exports = SourceMap;
\No newline at end of file