UNPKG

ng-annotate

Version:

add, remove and rebuild angularjs dependency injection annotations

124 lines (99 loc) 5.14 kB
"use strict"; var os = require("os"); var convertSourceMap = require("convert-source-map"); var SourceMapConsumer = require("source-map").SourceMapConsumer; var SourceMapGenerator = require("source-map").SourceMapGenerator; var stableSort = require("stable"); function SourceMapper(src, nodePositions, fragments, inFile, sourceRoot) { this.generator = new SourceMapGenerator({ sourceRoot: sourceRoot }); this.src = src; // stableSort does not mutate input array so no need to copy it this.nodePositions = stableSort(nodePositions, compareLoc); this.fragments = stableSort(fragments, function(a, b) { return a.start - b.start }); this.inFile = inFile; this.generator.setSourceContent(this.inFile, src); } SourceMapper.prototype.calculateMappings = function() { var self = this; // These offsets represent the difference in coordinates between a node in the source // and the corresponding position in the output. var lineOffset = 0; var columnOffset = 0; // Since the column position resets to zero after each newline, we have to keep track // of the current line that columnOffset refers to in order to know whether to reset it var currentLine = 0; var frag = 0; var pos = 0; while (pos < self.nodePositions.length) { while (frag < self.fragments.length && compareLoc(self.fragments[frag].loc.start, self.nodePositions[pos]) < 1) { var fragmentLines = self.fragments[frag].str.split("\n"); var addedNewlines = fragmentLines.length - 1; var replacedLines = self.fragments[frag].loc.end.line - self.fragments[frag].loc.start.line; var replacedColumns = self.fragments[frag].loc.end.column - self.fragments[frag].loc.start.column; // If there were any lines added by the fragment string, the line offset should increase; // If there were any lines removed by the fragment replacement then the line offset should decrease lineOffset = lineOffset + addedNewlines - replacedLines; // The column position needs to reset after each newline. So if the fragment added any // newlines then the column offset is the difference between the column of the last line of // the fragment, and the column of the end of the replaced section of the source. // Otherwise we increment or decrement the column offset just like how the line offset works. // Note that "replacedColumns" might be negative in some cases (if the beginning of the source // was further right than the end due to a newline); the math still works out. columnOffset = fragmentLines.length > 1 ? fragmentLines[fragmentLines.length - 1].length - self.fragments[frag].loc.end.column : columnOffset + self.fragments[frag].str.length - replacedColumns; currentLine = self.fragments[frag].loc.end.line; // Skip creating mappings for any source nodes that were replaced by this fragment (and are thus // no longer a part of the output) while (pos < self.nodePositions.length && compareLoc(self.fragments[frag].loc.end, self.nodePositions[pos]) > 0) { ++pos; } ++frag; } if (pos < self.nodePositions.length) { if (currentLine < self.nodePositions[pos].line) columnOffset = 0; self.addMapping(self.nodePositions[pos], { line: self.nodePositions[pos].line + lineOffset, column: self.nodePositions[pos].column + columnOffset }); ++pos; } } } SourceMapper.prototype.addMapping = function(input, output) { this.generator.addMapping({ source: this.inFile, original: input, generated: output }); } SourceMapper.prototype.applySourceMap = function (consumer) { this.generator.applySourceMap(consumer); } SourceMapper.prototype.generate = function () { return this.generator.toString(); } function compareLoc(a, b) { return (a.line - b.line) || (a.column - b.column); } module.exports = function generateSourcemap(result, src, nodePositions, fragments, mapOpts) { var existingMap = convertSourceMap.fromSource(src); var existingMapObject = existingMap && existingMap.toObject(); var inFile = (existingMapObject && existingMapObject.file) || mapOpts.inFile || "source.js"; var sourceRoot = (existingMapObject && existingMapObject.sourceRoot) || mapOpts.sourceRoot; src = convertSourceMap.removeMapFileComments(src); var mapper = new SourceMapper(src, nodePositions, fragments, inFile, sourceRoot); mapper.calculateMappings(); if (mapOpts.inline) { if (existingMapObject) mapper.applySourceMap(new SourceMapConsumer(existingMapObject)); result.src = convertSourceMap.removeMapFileComments(result.src) + os.EOL + convertSourceMap.fromJSON(mapper.generate()).toComment(); } else { result.map = mapper.generate(); } }