Show More
Commit Description:
fix wrong merge
Commit Description:
fix wrong merge
References:
File last commit:
Show/Diff file:
Action:
node_modules/source-map/lib/source-node.js
| 413 lines
| 13.5 KiB
| application/javascript
| JavascriptLexer
|
r789 | /* -*- Mode: js; js-indent-level: 2; -*- */ | |||
/* | ||||
* Copyright 2011 Mozilla Foundation and contributors | ||||
* Licensed under the New BSD license. See LICENSE or: | ||||
* http://opensource.org/licenses/BSD-3-Clause | ||||
*/ | ||||
var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; | ||||
var util = require('./util'); | ||||
// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other | ||||
// operating systems these days (capturing the result). | ||||
var REGEX_NEWLINE = /(\r?\n)/; | ||||
// Newline character code for charCodeAt() comparisons | ||||
var NEWLINE_CODE = 10; | ||||
// Private symbol for identifying `SourceNode`s when multiple versions of | ||||
// the source-map library are loaded. This MUST NOT CHANGE across | ||||
// versions! | ||||
var isSourceNode = "$$$isSourceNode$$$"; | ||||
/** | ||||
* SourceNodes provide a way to abstract over interpolating/concatenating | ||||
* snippets of generated JavaScript source code while maintaining the line and | ||||
* column information associated with the original source code. | ||||
* | ||||
* @param aLine The original line number. | ||||
* @param aColumn The original column number. | ||||
* @param aSource The original source's filename. | ||||
* @param aChunks Optional. An array of strings which are snippets of | ||||
* generated JS, or other SourceNodes. | ||||
* @param aName The original identifier. | ||||
*/ | ||||
function SourceNode(aLine, aColumn, aSource, aChunks, aName) { | ||||
this.children = []; | ||||
this.sourceContents = {}; | ||||
this.line = aLine == null ? null : aLine; | ||||
this.column = aColumn == null ? null : aColumn; | ||||
this.source = aSource == null ? null : aSource; | ||||
this.name = aName == null ? null : aName; | ||||
this[isSourceNode] = true; | ||||
if (aChunks != null) this.add(aChunks); | ||||
} | ||||
/** | ||||
* Creates a SourceNode from generated code and a SourceMapConsumer. | ||||
* | ||||
* @param aGeneratedCode The generated code | ||||
* @param aSourceMapConsumer The SourceMap for the generated code | ||||
* @param aRelativePath Optional. The path that relative sources in the | ||||
* SourceMapConsumer should be relative to. | ||||
*/ | ||||
SourceNode.fromStringWithSourceMap = | ||||
function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) { | ||||
// The SourceNode we want to fill with the generated code | ||||
// and the SourceMap | ||||
var node = new SourceNode(); | ||||
// All even indices of this array are one line of the generated code, | ||||
// while all odd indices are the newlines between two adjacent lines | ||||
// (since `REGEX_NEWLINE` captures its match). | ||||
// Processed fragments are accessed by calling `shiftNextLine`. | ||||
var remainingLines = aGeneratedCode.split(REGEX_NEWLINE); | ||||
var remainingLinesIndex = 0; | ||||
var shiftNextLine = function() { | ||||
var lineContents = getNextLine(); | ||||
// The last line of a file might not have a newline. | ||||
var newLine = getNextLine() || ""; | ||||
return lineContents + newLine; | ||||
function getNextLine() { | ||||
return remainingLinesIndex < remainingLines.length ? | ||||
remainingLines[remainingLinesIndex++] : undefined; | ||||
} | ||||
}; | ||||
// We need to remember the position of "remainingLines" | ||||
var lastGeneratedLine = 1, lastGeneratedColumn = 0; | ||||
// The generate SourceNodes we need a code range. | ||||
// To extract it current and last mapping is used. | ||||
// Here we store the last mapping. | ||||
var lastMapping = null; | ||||
aSourceMapConsumer.eachMapping(function (mapping) { | ||||
if (lastMapping !== null) { | ||||
// We add the code from "lastMapping" to "mapping": | ||||
// First check if there is a new line in between. | ||||
if (lastGeneratedLine < mapping.generatedLine) { | ||||
// Associate first line with "lastMapping" | ||||
addMappingWithCode(lastMapping, shiftNextLine()); | ||||
lastGeneratedLine++; | ||||
lastGeneratedColumn = 0; | ||||
// The remaining code is added without mapping | ||||
} else { | ||||
// There is no new line in between. | ||||
// Associate the code between "lastGeneratedColumn" and | ||||
// "mapping.generatedColumn" with "lastMapping" | ||||
var nextLine = remainingLines[remainingLinesIndex] || ''; | ||||
var code = nextLine.substr(0, mapping.generatedColumn - | ||||
lastGeneratedColumn); | ||||
remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn - | ||||
lastGeneratedColumn); | ||||
lastGeneratedColumn = mapping.generatedColumn; | ||||
addMappingWithCode(lastMapping, code); | ||||
// No more remaining code, continue | ||||
lastMapping = mapping; | ||||
return; | ||||
} | ||||
} | ||||
// We add the generated code until the first mapping | ||||
// to the SourceNode without any mapping. | ||||
// Each line is added as separate string. | ||||
while (lastGeneratedLine < mapping.generatedLine) { | ||||
node.add(shiftNextLine()); | ||||
lastGeneratedLine++; | ||||
} | ||||
if (lastGeneratedColumn < mapping.generatedColumn) { | ||||
var nextLine = remainingLines[remainingLinesIndex] || ''; | ||||
node.add(nextLine.substr(0, mapping.generatedColumn)); | ||||
remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn); | ||||
lastGeneratedColumn = mapping.generatedColumn; | ||||
} | ||||
lastMapping = mapping; | ||||
}, this); | ||||
// We have processed all mappings. | ||||
if (remainingLinesIndex < remainingLines.length) { | ||||
if (lastMapping) { | ||||
// Associate the remaining code in the current line with "lastMapping" | ||||
addMappingWithCode(lastMapping, shiftNextLine()); | ||||
} | ||||
// and add the remaining lines without any mapping | ||||
node.add(remainingLines.splice(remainingLinesIndex).join("")); | ||||
} | ||||
// Copy sourcesContent into SourceNode | ||||
aSourceMapConsumer.sources.forEach(function (sourceFile) { | ||||
var content = aSourceMapConsumer.sourceContentFor(sourceFile); | ||||
if (content != null) { | ||||
if (aRelativePath != null) { | ||||
sourceFile = util.join(aRelativePath, sourceFile); | ||||
} | ||||
node.setSourceContent(sourceFile, content); | ||||
} | ||||
}); | ||||
return node; | ||||
function addMappingWithCode(mapping, code) { | ||||
if (mapping === null || mapping.source === undefined) { | ||||
node.add(code); | ||||
} else { | ||||
var source = aRelativePath | ||||
? util.join(aRelativePath, mapping.source) | ||||
: mapping.source; | ||||
node.add(new SourceNode(mapping.originalLine, | ||||
mapping.originalColumn, | ||||
source, | ||||
code, | ||||
mapping.name)); | ||||
} | ||||
} | ||||
}; | ||||
/** | ||||
* Add a chunk of generated JS to this source node. | ||||
* | ||||
* @param aChunk A string snippet of generated JS code, another instance of | ||||
* SourceNode, or an array where each member is one of those things. | ||||
*/ | ||||
SourceNode.prototype.add = function SourceNode_add(aChunk) { | ||||
if (Array.isArray(aChunk)) { | ||||
aChunk.forEach(function (chunk) { | ||||
this.add(chunk); | ||||
}, this); | ||||
} | ||||
else if (aChunk[isSourceNode] || typeof aChunk === "string") { | ||||
if (aChunk) { | ||||
this.children.push(aChunk); | ||||
} | ||||
} | ||||
else { | ||||
throw new TypeError( | ||||
"Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk | ||||
); | ||||
} | ||||
return this; | ||||
}; | ||||
/** | ||||
* Add a chunk of generated JS to the beginning of this source node. | ||||
* | ||||
* @param aChunk A string snippet of generated JS code, another instance of | ||||
* SourceNode, or an array where each member is one of those things. | ||||
*/ | ||||
SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { | ||||
if (Array.isArray(aChunk)) { | ||||
for (var i = aChunk.length-1; i >= 0; i--) { | ||||
this.prepend(aChunk[i]); | ||||
} | ||||
} | ||||
else if (aChunk[isSourceNode] || typeof aChunk === "string") { | ||||
this.children.unshift(aChunk); | ||||
} | ||||
else { | ||||
throw new TypeError( | ||||
"Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk | ||||
); | ||||
} | ||||
return this; | ||||
}; | ||||
/** | ||||
* Walk over the tree of JS snippets in this node and its children. The | ||||
* walking function is called once for each snippet of JS and is passed that | ||||
* snippet and the its original associated source's line/column location. | ||||
* | ||||
* @param aFn The traversal function. | ||||
*/ | ||||
SourceNode.prototype.walk = function SourceNode_walk(aFn) { | ||||
var chunk; | ||||
for (var i = 0, len = this.children.length; i < len; i++) { | ||||
chunk = this.children[i]; | ||||
if (chunk[isSourceNode]) { | ||||
chunk.walk(aFn); | ||||
} | ||||
else { | ||||
if (chunk !== '') { | ||||
aFn(chunk, { source: this.source, | ||||
line: this.line, | ||||
column: this.column, | ||||
name: this.name }); | ||||
} | ||||
} | ||||
} | ||||
}; | ||||
/** | ||||
* Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between | ||||
* each of `this.children`. | ||||
* | ||||
* @param aSep The separator. | ||||
*/ | ||||
SourceNode.prototype.join = function SourceNode_join(aSep) { | ||||
var newChildren; | ||||
var i; | ||||
var len = this.children.length; | ||||
if (len > 0) { | ||||
newChildren = []; | ||||
for (i = 0; i < len-1; i++) { | ||||
newChildren.push(this.children[i]); | ||||
newChildren.push(aSep); | ||||
} | ||||
newChildren.push(this.children[i]); | ||||
this.children = newChildren; | ||||
} | ||||
return this; | ||||
}; | ||||
/** | ||||
* Call String.prototype.replace on the very right-most source snippet. Useful | ||||
* for trimming whitespace from the end of a source node, etc. | ||||
* | ||||
* @param aPattern The pattern to replace. | ||||
* @param aReplacement The thing to replace the pattern with. | ||||
*/ | ||||
SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { | ||||
var lastChild = this.children[this.children.length - 1]; | ||||
if (lastChild[isSourceNode]) { | ||||
lastChild.replaceRight(aPattern, aReplacement); | ||||
} | ||||
else if (typeof lastChild === 'string') { | ||||
this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); | ||||
} | ||||
else { | ||||
this.children.push(''.replace(aPattern, aReplacement)); | ||||
} | ||||
return this; | ||||
}; | ||||
/** | ||||
* Set the source content for a source file. This will be added to the SourceMapGenerator | ||||
* in the sourcesContent field. | ||||
* | ||||
* @param aSourceFile The filename of the source file | ||||
* @param aSourceContent The content of the source file | ||||
*/ | ||||
SourceNode.prototype.setSourceContent = | ||||
function SourceNode_setSourceContent(aSourceFile, aSourceContent) { | ||||
this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; | ||||
}; | ||||
/** | ||||
* Walk over the tree of SourceNodes. The walking function is called for each | ||||
* source file content and is passed the filename and source content. | ||||
* | ||||
* @param aFn The traversal function. | ||||
*/ | ||||
SourceNode.prototype.walkSourceContents = | ||||
function SourceNode_walkSourceContents(aFn) { | ||||
for (var i = 0, len = this.children.length; i < len; i++) { | ||||
if (this.children[i][isSourceNode]) { | ||||
this.children[i].walkSourceContents(aFn); | ||||
} | ||||
} | ||||
var sources = Object.keys(this.sourceContents); | ||||
for (var i = 0, len = sources.length; i < len; i++) { | ||||
aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); | ||||
} | ||||
}; | ||||
/** | ||||
* Return the string representation of this source node. Walks over the tree | ||||
* and concatenates all the various snippets together to one string. | ||||
*/ | ||||
SourceNode.prototype.toString = function SourceNode_toString() { | ||||
var str = ""; | ||||
this.walk(function (chunk) { | ||||
str += chunk; | ||||
}); | ||||
return str; | ||||
}; | ||||
/** | ||||
* Returns the string representation of this source node along with a source | ||||
* map. | ||||
*/ | ||||
SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { | ||||
var generated = { | ||||
code: "", | ||||
line: 1, | ||||
column: 0 | ||||
}; | ||||
var map = new SourceMapGenerator(aArgs); | ||||
var sourceMappingActive = false; | ||||
var lastOriginalSource = null; | ||||
var lastOriginalLine = null; | ||||
var lastOriginalColumn = null; | ||||
var lastOriginalName = null; | ||||
this.walk(function (chunk, original) { | ||||
generated.code += chunk; | ||||
if (original.source !== null | ||||
&& original.line !== null | ||||
&& original.column !== null) { | ||||
if(lastOriginalSource !== original.source | ||||
|| lastOriginalLine !== original.line | ||||
|| lastOriginalColumn !== original.column | ||||
|| lastOriginalName !== original.name) { | ||||
map.addMapping({ | ||||
source: original.source, | ||||
original: { | ||||
line: original.line, | ||||
column: original.column | ||||
}, | ||||
generated: { | ||||
line: generated.line, | ||||
column: generated.column | ||||
}, | ||||
name: original.name | ||||
}); | ||||
} | ||||
lastOriginalSource = original.source; | ||||
lastOriginalLine = original.line; | ||||
lastOriginalColumn = original.column; | ||||
lastOriginalName = original.name; | ||||
sourceMappingActive = true; | ||||
} else if (sourceMappingActive) { | ||||
map.addMapping({ | ||||
generated: { | ||||
line: generated.line, | ||||
column: generated.column | ||||
} | ||||
}); | ||||
lastOriginalSource = null; | ||||
sourceMappingActive = false; | ||||
} | ||||
for (var idx = 0, length = chunk.length; idx < length; idx++) { | ||||
if (chunk.charCodeAt(idx) === NEWLINE_CODE) { | ||||
generated.line++; | ||||
generated.column = 0; | ||||
// Mappings end at eol | ||||
if (idx + 1 === length) { | ||||
lastOriginalSource = null; | ||||
sourceMappingActive = false; | ||||
} else if (sourceMappingActive) { | ||||
map.addMapping({ | ||||
source: original.source, | ||||
original: { | ||||
line: original.line, | ||||
column: original.column | ||||
}, | ||||
generated: { | ||||
line: generated.line, | ||||
column: generated.column | ||||
}, | ||||
name: original.name | ||||
}); | ||||
} | ||||
} else { | ||||
generated.column++; | ||||
} | ||||
} | ||||
}); | ||||
this.walkSourceContents(function (sourceFile, sourceContent) { | ||||
map.setSourceContent(sourceFile, sourceContent); | ||||
}); | ||||
return { code: generated.code, map: map }; | ||||
}; | ||||
exports.SourceNode = SourceNode; | ||||