335 lines
No EOL
9.2 KiB
JavaScript
335 lines
No EOL
9.2 KiB
JavaScript
"use strict";
|
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
|
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
|
|
const _require = require('source-map'),
|
|
SourceMapConsumer = _require.SourceMapConsumer,
|
|
SourceMapGenerator = _require.SourceMapGenerator;
|
|
|
|
const lineCounter = require('./utils/lineCounter');
|
|
|
|
class SourceMap {
|
|
constructor(mappings, sources) {
|
|
this.mappings = this.purifyMappings(mappings);
|
|
this.sources = sources || {};
|
|
this.lineCount = null;
|
|
}
|
|
|
|
purifyMappings(mappings) {
|
|
if (Array.isArray(mappings)) {
|
|
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');
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
getConsumer(map) {
|
|
return (0, _asyncToGenerator2.default)(function* () {
|
|
if (map instanceof SourceMapConsumer) {
|
|
return map;
|
|
}
|
|
|
|
map = typeof map === 'string' ? JSON.parse(map) : map;
|
|
if (map.sourceRoot) delete map.sourceRoot;
|
|
return new SourceMapConsumer(map);
|
|
})();
|
|
}
|
|
|
|
addMap(map, lineOffset = 0, columnOffset = 0) {
|
|
var _this = this;
|
|
|
|
return (0, _asyncToGenerator2.default)(function* () {
|
|
if (typeof map === 'string' || typeof map === 'object' && map.version) {
|
|
let consumer = yield _this.getConsumer(map);
|
|
if (!consumer) return _this;
|
|
consumer.eachMapping(mapping => {
|
|
_this.addConsumerMapping(mapping, lineOffset, columnOffset);
|
|
|
|
if (!_this.sources[mapping.source]) {
|
|
_this.sources[mapping.source] = consumer.sourceContentFor(mapping.source, true);
|
|
}
|
|
});
|
|
|
|
if (consumer.destroy) {
|
|
// Only needs to happen in source-map 0.7
|
|
consumer.destroy();
|
|
}
|
|
} else if (map.mappings && map.sources) {
|
|
if (!map.eachMapping) {
|
|
map = new SourceMap(map.mappings, map.sources);
|
|
}
|
|
|
|
if (lineOffset === 0 && columnOffset === 0) {
|
|
_this.mappings = _this.mappings.concat(map.mappings);
|
|
} else {
|
|
map.eachMapping(mapping => {
|
|
_this.addMapping(mapping, lineOffset, columnOffset);
|
|
});
|
|
}
|
|
|
|
Object.keys(map.sources).forEach(sourceName => {
|
|
if (!_this.sources[sourceName]) {
|
|
_this.sources[sourceName] = map.sources[sourceName];
|
|
}
|
|
});
|
|
}
|
|
|
|
return _this;
|
|
})();
|
|
}
|
|
|
|
addMapping(mapping, lineOffset = 0, columnOffset = 0) {
|
|
this.mappings.push({
|
|
source: mapping.source,
|
|
name: mapping.name,
|
|
original: mapping.original,
|
|
generated: {
|
|
line: mapping.generated.line + lineOffset,
|
|
column: mapping.generated.column + columnOffset
|
|
}
|
|
});
|
|
}
|
|
|
|
addConsumerMapping(mapping, lineOffset = 0, columnOffset = 0) {
|
|
let original = null;
|
|
|
|
if (typeof mapping.originalLine === 'number' && mapping.originalLine > 0 && typeof mapping.originalColumn === 'number') {
|
|
original = {
|
|
line: mapping.originalLine,
|
|
column: mapping.originalColumn
|
|
};
|
|
}
|
|
|
|
this.mappings.push({
|
|
source: original ? mapping.source : null,
|
|
name: mapping.name,
|
|
original,
|
|
generated: {
|
|
line: mapping.generatedLine + lineOffset,
|
|
column: mapping.generatedColumn + columnOffset
|
|
}
|
|
});
|
|
}
|
|
|
|
eachMapping(callback) {
|
|
this.mappings.forEach(callback);
|
|
}
|
|
|
|
generateEmptyMap(sourceName, sourceContent) {
|
|
this.sources[sourceName] = sourceContent;
|
|
this.lineCount = lineCounter(sourceContent);
|
|
|
|
for (let line = 1; line < this.lineCount + 1; line++) {
|
|
this.addMapping({
|
|
source: sourceName,
|
|
original: {
|
|
line: line,
|
|
column: 0
|
|
},
|
|
generated: {
|
|
line: line,
|
|
column: 0
|
|
}
|
|
});
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
extendSourceMap(original, extension) {
|
|
var _this2 = this;
|
|
|
|
return (0, _asyncToGenerator2.default)(function* () {
|
|
if (!(extension instanceof SourceMap)) {
|
|
extension = yield new SourceMap().addMap(extension);
|
|
}
|
|
|
|
if (!(original instanceof SourceMap)) {
|
|
original = yield _this2.getConsumer(original);
|
|
}
|
|
|
|
extension.eachMapping(mapping => {
|
|
let originalMapping = original.originalPositionFor({
|
|
line: mapping.original.line,
|
|
column: mapping.original.column
|
|
});
|
|
|
|
if (!originalMapping || !originalMapping.line) {
|
|
return;
|
|
}
|
|
|
|
_this2.addMapping({
|
|
source: originalMapping.source,
|
|
name: originalMapping.name,
|
|
original: {
|
|
line: originalMapping.line,
|
|
column: originalMapping.column
|
|
},
|
|
generated: {
|
|
line: mapping.generated.line,
|
|
column: mapping.generated.column
|
|
}
|
|
});
|
|
|
|
if (!_this2.sources[originalMapping.source]) {
|
|
_this2.sources[originalMapping.source] = original.sourceContentFor(originalMapping.source, true);
|
|
}
|
|
});
|
|
|
|
if (original.destroy) {
|
|
// Only needs to happen in source-map 0.7
|
|
original.destroy();
|
|
}
|
|
|
|
return _this2;
|
|
})();
|
|
}
|
|
|
|
findClosestGenerated(line, column) {
|
|
if (line < 1) {
|
|
throw new Error('Line numbers must be >= 1');
|
|
}
|
|
|
|
if (column < 0) {
|
|
throw new Error('Column numbers must be >= 0');
|
|
}
|
|
|
|
if (this.mappings.length < 1) {
|
|
return undefined;
|
|
}
|
|
|
|
let startIndex = 0;
|
|
let stopIndex = this.mappings.length - 1;
|
|
let middleIndex = stopIndex + startIndex >>> 1;
|
|
|
|
while (startIndex < stopIndex && this.mappings[middleIndex].generated.line !== line) {
|
|
let mid = this.mappings[middleIndex].generated.line;
|
|
|
|
if (line < mid) {
|
|
stopIndex = middleIndex - 1;
|
|
} else if (line > mid) {
|
|
startIndex = middleIndex + 1;
|
|
}
|
|
|
|
middleIndex = stopIndex + startIndex >>> 1;
|
|
}
|
|
|
|
let mapping = this.mappings[middleIndex];
|
|
|
|
if (!mapping || mapping.generated.line !== line) {
|
|
return this.mappings.length - 1;
|
|
}
|
|
|
|
while (middleIndex >= 1 && this.mappings[middleIndex - 1].generated.line === line) {
|
|
middleIndex--;
|
|
}
|
|
|
|
while (middleIndex < this.mappings.length - 1 && this.mappings[middleIndex + 1].generated.line === line && column > this.mappings[middleIndex].generated.column) {
|
|
middleIndex++;
|
|
}
|
|
|
|
return middleIndex;
|
|
}
|
|
|
|
findClosest(line, column, key) {
|
|
if (line < 1) {
|
|
throw new Error('Line numbers must be >= 1');
|
|
}
|
|
|
|
if (column < 0) {
|
|
throw new Error('Column numbers must be >= 0');
|
|
}
|
|
|
|
if (this.mappings.length < 1) {
|
|
return undefined;
|
|
}
|
|
|
|
let startIndex = 0;
|
|
let stopIndex = this.mappings.length - 1;
|
|
let middleIndex = Math.floor((stopIndex + startIndex) / 2);
|
|
|
|
while (startIndex < stopIndex && this.mappings[middleIndex][key].line !== line) {
|
|
if (line < this.mappings[middleIndex][key].line) {
|
|
stopIndex = middleIndex - 1;
|
|
} else if (line > this.mappings[middleIndex][key].line) {
|
|
startIndex = middleIndex + 1;
|
|
}
|
|
|
|
middleIndex = Math.floor((stopIndex + startIndex) / 2);
|
|
}
|
|
|
|
var mapping = this.mappings[middleIndex];
|
|
|
|
if (!mapping || mapping[key].line !== line) {
|
|
return this.mappings.length - 1;
|
|
}
|
|
|
|
while (middleIndex >= 1 && this.mappings[middleIndex - 1][key].line === line) {
|
|
middleIndex--;
|
|
}
|
|
|
|
while (middleIndex < this.mappings.length - 1 && this.mappings[middleIndex + 1][key].line === line && column > this.mappings[middleIndex][key].column) {
|
|
middleIndex++;
|
|
}
|
|
|
|
return middleIndex;
|
|
}
|
|
|
|
originalPositionFor(generatedPosition) {
|
|
let index = this.findClosestGenerated(generatedPosition.line, generatedPosition.column);
|
|
let mapping = this.mappings[index];
|
|
|
|
if (!mapping || !mapping.original) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
source: mapping.source,
|
|
name: mapping.name,
|
|
line: mapping.original.line,
|
|
column: mapping.original.column
|
|
};
|
|
}
|
|
|
|
generatedPositionFor(originalPosition) {
|
|
let index = this.findClosest(originalPosition.line, originalPosition.column, 'original');
|
|
let mapping = this.mappings[index];
|
|
return {
|
|
source: mapping.source,
|
|
name: mapping.name,
|
|
line: mapping.generated.line,
|
|
column: mapping.generated.column
|
|
};
|
|
}
|
|
|
|
sourceContentFor(fileName) {
|
|
return this.sources[fileName];
|
|
}
|
|
|
|
offset(lineOffset = 0, columnOffset = 0) {
|
|
this.mappings.map(mapping => {
|
|
mapping.generated.line = mapping.generated.line + lineOffset;
|
|
mapping.generated.column = mapping.generated.column + columnOffset;
|
|
return mapping;
|
|
});
|
|
|
|
if (this.lineCount != null) {
|
|
this.lineCount += lineOffset;
|
|
}
|
|
}
|
|
|
|
stringify(file, sourceRoot) {
|
|
let generator = new SourceMapGenerator({
|
|
file,
|
|
sourceRoot
|
|
});
|
|
this.eachMapping(mapping => generator.addMapping(mapping));
|
|
Object.keys(this.sources).forEach(sourceName => generator.setSourceContent(sourceName, this.sources[sourceName]));
|
|
return generator.toString();
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = SourceMap; |