applySourceMap.js 5.5 KB

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