[6a3a178] | 1 | /*
|
---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
| 3 | Author Tobias Koppers @sokra
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | "use strict";
|
---|
| 7 |
|
---|
| 8 | var SourceNode = require("source-map").SourceNode;
|
---|
| 9 | var SourceMapConsumer = require("source-map").SourceMapConsumer;
|
---|
| 10 |
|
---|
| 11 | var applySourceMap = function(
|
---|
| 12 | sourceNode,
|
---|
| 13 | sourceMapConsumer,
|
---|
| 14 | sourceFile,
|
---|
| 15 | removeGeneratedCodeForSourceFile
|
---|
| 16 | ) {
|
---|
| 17 | // The following notations are used to name stuff:
|
---|
| 18 | // Left <------------> Middle <-------------------> Right
|
---|
| 19 | // Input arguments:
|
---|
| 20 | // sourceNode - Code mapping from Left to Middle
|
---|
| 21 | // sourceFile - Name of a Middle file
|
---|
| 22 | // sourceMapConsumer - Code mapping from Middle to Right
|
---|
| 23 | // Variables:
|
---|
| 24 | // l2m m2r
|
---|
| 25 | // Left <-----------------------------------------> Right
|
---|
| 26 | // Variables:
|
---|
| 27 | // l2r
|
---|
| 28 |
|
---|
| 29 | var l2rResult = new SourceNode();
|
---|
| 30 | var l2rOutput = [];
|
---|
| 31 |
|
---|
| 32 | var middleSourceContents = {};
|
---|
| 33 |
|
---|
| 34 | var m2rMappingsByLine = {};
|
---|
| 35 |
|
---|
| 36 | var rightSourceContentsSet = {};
|
---|
| 37 | var rightSourceContentsLines = {};
|
---|
| 38 |
|
---|
| 39 | // Store all mappings by generated line
|
---|
| 40 | sourceMapConsumer.eachMapping(
|
---|
| 41 | function(mapping) {
|
---|
| 42 | (m2rMappingsByLine[mapping.generatedLine] =
|
---|
| 43 | m2rMappingsByLine[mapping.generatedLine] || []).push(mapping);
|
---|
| 44 | },
|
---|
| 45 | null,
|
---|
| 46 | SourceMapConsumer.GENERATED_ORDER
|
---|
| 47 | );
|
---|
| 48 |
|
---|
| 49 | // Store all source contents
|
---|
| 50 | sourceNode.walkSourceContents(function(source, content) {
|
---|
| 51 | middleSourceContents["$" + source] = content;
|
---|
| 52 | });
|
---|
| 53 |
|
---|
| 54 | var middleSource = middleSourceContents["$" + sourceFile];
|
---|
| 55 | var middleSourceLines = middleSource ? middleSource.split("\n") : undefined;
|
---|
| 56 |
|
---|
| 57 | // Walk all left to middle mappings
|
---|
| 58 | sourceNode.walk(function(chunk, middleMapping) {
|
---|
| 59 | var source;
|
---|
| 60 |
|
---|
| 61 | // Find a mapping from middle to right
|
---|
| 62 | if(
|
---|
| 63 | middleMapping.source === sourceFile &&
|
---|
| 64 | middleMapping.line &&
|
---|
| 65 | m2rMappingsByLine[middleMapping.line]
|
---|
| 66 | ) {
|
---|
| 67 | var m2rBestFit;
|
---|
| 68 | var m2rMappings = m2rMappingsByLine[middleMapping.line];
|
---|
| 69 | // Note: if this becomes a performance problem, use binary search
|
---|
| 70 | for(var i = 0; i < m2rMappings.length; i++) {
|
---|
| 71 | if(m2rMappings[i].generatedColumn <= middleMapping.column) {
|
---|
| 72 | m2rBestFit = m2rMappings[i];
|
---|
| 73 | }
|
---|
| 74 | }
|
---|
| 75 | if(m2rBestFit) {
|
---|
| 76 | var allowMiddleName = false;
|
---|
| 77 | var middleLine;
|
---|
| 78 | var rightSourceContent;
|
---|
| 79 | var rightSourceContentLines;
|
---|
| 80 | var rightSource = m2rBestFit.source;
|
---|
| 81 | // Check if we have middle and right source for this mapping
|
---|
| 82 | // Then we could have an "identify" mapping
|
---|
| 83 | if(
|
---|
| 84 | middleSourceLines &&
|
---|
| 85 | rightSource &&
|
---|
| 86 | (middleLine = middleSourceLines[m2rBestFit.generatedLine - 1]) &&
|
---|
| 87 | ((rightSourceContentLines = rightSourceContentsLines[rightSource]) ||
|
---|
| 88 | (rightSourceContent = sourceMapConsumer.sourceContentFor(
|
---|
| 89 | rightSource,
|
---|
| 90 | true
|
---|
| 91 | )))
|
---|
| 92 | ) {
|
---|
| 93 | if(!rightSourceContentLines) {
|
---|
| 94 | rightSourceContentLines = rightSourceContentsLines[
|
---|
| 95 | rightSource
|
---|
| 96 | ] = rightSourceContent.split("\n");
|
---|
| 97 | }
|
---|
| 98 | var rightLine = rightSourceContentLines[m2rBestFit.originalLine - 1];
|
---|
| 99 | if(rightLine) {
|
---|
| 100 | var offset = middleMapping.column - m2rBestFit.generatedColumn;
|
---|
| 101 | if(offset > 0) {
|
---|
| 102 | var middlePart = middleLine.slice(
|
---|
| 103 | m2rBestFit.generatedColumn,
|
---|
| 104 | middleMapping.column
|
---|
| 105 | );
|
---|
| 106 | var rightPart = rightLine.slice(
|
---|
| 107 | m2rBestFit.originalColumn,
|
---|
| 108 | m2rBestFit.originalColumn + offset
|
---|
| 109 | );
|
---|
| 110 | if(middlePart === rightPart) {
|
---|
| 111 | // When original and generated code is equal we assume we have an "identity" mapping
|
---|
| 112 | // In this case we can offset the original position
|
---|
| 113 | m2rBestFit = Object.assign({}, m2rBestFit, {
|
---|
| 114 | originalColumn: m2rBestFit.originalColumn + offset,
|
---|
| 115 | generatedColumn: middleMapping.column
|
---|
| 116 | });
|
---|
| 117 | }
|
---|
| 118 | }
|
---|
| 119 | if(!m2rBestFit.name && middleMapping.name) {
|
---|
| 120 | allowMiddleName =
|
---|
| 121 | rightLine.slice(
|
---|
| 122 | m2rBestFit.originalColumn,
|
---|
| 123 | m2rBestFit.originalColumn + middleMapping.name.length
|
---|
| 124 | ) === middleMapping.name;
|
---|
| 125 | }
|
---|
| 126 | }
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | // Construct a left to right node from the found middle to right mapping
|
---|
| 130 | source = m2rBestFit.source;
|
---|
| 131 | l2rOutput.push(
|
---|
| 132 | new SourceNode(
|
---|
| 133 | m2rBestFit.originalLine,
|
---|
| 134 | m2rBestFit.originalColumn,
|
---|
| 135 | source,
|
---|
| 136 | chunk,
|
---|
| 137 | allowMiddleName ? middleMapping.name : m2rBestFit.name
|
---|
| 138 | )
|
---|
| 139 | );
|
---|
| 140 |
|
---|
| 141 | // Set the source contents once
|
---|
| 142 | if(!("$" + source in rightSourceContentsSet)) {
|
---|
| 143 | rightSourceContentsSet["$" + source] = true;
|
---|
| 144 | var sourceContent = sourceMapConsumer.sourceContentFor(source, true);
|
---|
| 145 | if(sourceContent) {
|
---|
| 146 | l2rResult.setSourceContent(source, sourceContent);
|
---|
| 147 | }
|
---|
| 148 | }
|
---|
| 149 | return;
|
---|
| 150 | }
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | if((removeGeneratedCodeForSourceFile && middleMapping.source === sourceFile) || !middleMapping.source) {
|
---|
| 154 | // Construct a left to middle node with only generated code
|
---|
| 155 | // Because user do not want mappings to middle sources
|
---|
| 156 | // Or this chunk has no mapping
|
---|
| 157 | l2rOutput.push(chunk);
|
---|
| 158 | return;
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | // Construct a left to middle node
|
---|
| 162 | source = middleMapping.source;
|
---|
| 163 | l2rOutput.push(
|
---|
| 164 | new SourceNode(
|
---|
| 165 | middleMapping.line,
|
---|
| 166 | middleMapping.column,
|
---|
| 167 | source,
|
---|
| 168 | chunk,
|
---|
| 169 | middleMapping.name
|
---|
| 170 | )
|
---|
| 171 | );
|
---|
| 172 | if("$" + source in middleSourceContents) {
|
---|
| 173 | if(!("$" + source in rightSourceContentsSet)) {
|
---|
| 174 | l2rResult.setSourceContent(source, middleSourceContents["$" + source]);
|
---|
| 175 | delete middleSourceContents["$" + source];
|
---|
| 176 | }
|
---|
| 177 | }
|
---|
| 178 | });
|
---|
| 179 |
|
---|
| 180 | // Put output into the resulting SourceNode
|
---|
| 181 | l2rResult.add(l2rOutput);
|
---|
| 182 | return l2rResult;
|
---|
| 183 | };
|
---|
| 184 |
|
---|
| 185 | module.exports = applySourceMap;
|
---|