You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.5 KiB
185 lines
5.5 KiB
/* |
|
MIT License http://www.opensource.org/licenses/mit-license.php |
|
Author Tobias Koppers @sokra |
|
*/ |
|
|
|
"use strict"; |
|
|
|
var SourceNode = require("source-map").SourceNode; |
|
var SourceMapConsumer = require("source-map").SourceMapConsumer; |
|
|
|
var applySourceMap = function( |
|
sourceNode, |
|
sourceMapConsumer, |
|
sourceFile, |
|
removeGeneratedCodeForSourceFile |
|
) { |
|
// The following notations are used to name stuff: |
|
// Left <------------> Middle <-------------------> Right |
|
// Input arguments: |
|
// sourceNode - Code mapping from Left to Middle |
|
// sourceFile - Name of a Middle file |
|
// sourceMapConsumer - Code mapping from Middle to Right |
|
// Variables: |
|
// l2m m2r |
|
// Left <-----------------------------------------> Right |
|
// Variables: |
|
// l2r |
|
|
|
var l2rResult = new SourceNode(); |
|
var l2rOutput = []; |
|
|
|
var middleSourceContents = {}; |
|
|
|
var m2rMappingsByLine = {}; |
|
|
|
var rightSourceContentsSet = {}; |
|
var rightSourceContentsLines = {}; |
|
|
|
// Store all mappings by generated line |
|
sourceMapConsumer.eachMapping( |
|
function(mapping) { |
|
(m2rMappingsByLine[mapping.generatedLine] = |
|
m2rMappingsByLine[mapping.generatedLine] || []).push(mapping); |
|
}, |
|
null, |
|
SourceMapConsumer.GENERATED_ORDER |
|
); |
|
|
|
// Store all source contents |
|
sourceNode.walkSourceContents(function(source, content) { |
|
middleSourceContents["$" + source] = content; |
|
}); |
|
|
|
var middleSource = middleSourceContents["$" + sourceFile]; |
|
var middleSourceLines = middleSource ? middleSource.split("\n") : undefined; |
|
|
|
// Walk all left to middle mappings |
|
sourceNode.walk(function(chunk, middleMapping) { |
|
var source; |
|
|
|
// Find a mapping from middle to right |
|
if( |
|
middleMapping.source === sourceFile && |
|
middleMapping.line && |
|
m2rMappingsByLine[middleMapping.line] |
|
) { |
|
var m2rBestFit; |
|
var m2rMappings = m2rMappingsByLine[middleMapping.line]; |
|
// Note: if this becomes a performance problem, use binary search |
|
for(var i = 0; i < m2rMappings.length; i++) { |
|
if(m2rMappings[i].generatedColumn <= middleMapping.column) { |
|
m2rBestFit = m2rMappings[i]; |
|
} |
|
} |
|
if(m2rBestFit) { |
|
var allowMiddleName = false; |
|
var middleLine; |
|
var rightSourceContent; |
|
var rightSourceContentLines; |
|
var rightSource = m2rBestFit.source; |
|
// Check if we have middle and right source for this mapping |
|
// Then we could have an "identify" mapping |
|
if( |
|
middleSourceLines && |
|
rightSource && |
|
(middleLine = middleSourceLines[m2rBestFit.generatedLine - 1]) && |
|
((rightSourceContentLines = rightSourceContentsLines[rightSource]) || |
|
(rightSourceContent = sourceMapConsumer.sourceContentFor( |
|
rightSource, |
|
true |
|
))) |
|
) { |
|
if(!rightSourceContentLines) { |
|
rightSourceContentLines = rightSourceContentsLines[ |
|
rightSource |
|
] = rightSourceContent.split("\n"); |
|
} |
|
var rightLine = rightSourceContentLines[m2rBestFit.originalLine - 1]; |
|
if(rightLine) { |
|
var offset = middleMapping.column - m2rBestFit.generatedColumn; |
|
if(offset > 0) { |
|
var middlePart = middleLine.slice( |
|
m2rBestFit.generatedColumn, |
|
middleMapping.column |
|
); |
|
var rightPart = rightLine.slice( |
|
m2rBestFit.originalColumn, |
|
m2rBestFit.originalColumn + offset |
|
); |
|
if(middlePart === rightPart) { |
|
// When original and generated code is equal we assume we have an "identity" mapping |
|
// In this case we can offset the original position |
|
m2rBestFit = Object.assign({}, m2rBestFit, { |
|
originalColumn: m2rBestFit.originalColumn + offset, |
|
generatedColumn: middleMapping.column |
|
}); |
|
} |
|
} |
|
if(!m2rBestFit.name && middleMapping.name) { |
|
allowMiddleName = |
|
rightLine.slice( |
|
m2rBestFit.originalColumn, |
|
m2rBestFit.originalColumn + middleMapping.name.length |
|
) === middleMapping.name; |
|
} |
|
} |
|
} |
|
|
|
// Construct a left to right node from the found middle to right mapping |
|
source = m2rBestFit.source; |
|
l2rOutput.push( |
|
new SourceNode( |
|
m2rBestFit.originalLine, |
|
m2rBestFit.originalColumn, |
|
source, |
|
chunk, |
|
allowMiddleName ? middleMapping.name : m2rBestFit.name |
|
) |
|
); |
|
|
|
// Set the source contents once |
|
if(!("$" + source in rightSourceContentsSet)) { |
|
rightSourceContentsSet["$" + source] = true; |
|
var sourceContent = sourceMapConsumer.sourceContentFor(source, true); |
|
if(sourceContent) { |
|
l2rResult.setSourceContent(source, sourceContent); |
|
} |
|
} |
|
return; |
|
} |
|
} |
|
|
|
if((removeGeneratedCodeForSourceFile && middleMapping.source === sourceFile) || !middleMapping.source) { |
|
// Construct a left to middle node with only generated code |
|
// Because user do not want mappings to middle sources |
|
// Or this chunk has no mapping |
|
l2rOutput.push(chunk); |
|
return; |
|
} |
|
|
|
// Construct a left to middle node |
|
source = middleMapping.source; |
|
l2rOutput.push( |
|
new SourceNode( |
|
middleMapping.line, |
|
middleMapping.column, |
|
source, |
|
chunk, |
|
middleMapping.name |
|
) |
|
); |
|
if("$" + source in middleSourceContents) { |
|
if(!("$" + source in rightSourceContentsSet)) { |
|
l2rResult.setSourceContent(source, middleSourceContents["$" + source]); |
|
delete middleSourceContents["$" + source]; |
|
} |
|
} |
|
}); |
|
|
|
// Put output into the resulting SourceNode |
|
l2rResult.add(l2rOutput); |
|
return l2rResult; |
|
}; |
|
|
|
module.exports = applySourceMap;
|
|
|