source-map-output.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. export default environment => {
  2. class SourceMapOutput {
  3. constructor(options) {
  4. this._css = [];
  5. this._rootNode = options.rootNode;
  6. this._contentsMap = options.contentsMap;
  7. this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap;
  8. if (options.sourceMapFilename) {
  9. this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/');
  10. }
  11. this._outputFilename = options.outputFilename;
  12. this.sourceMapURL = options.sourceMapURL;
  13. if (options.sourceMapBasepath) {
  14. this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
  15. }
  16. if (options.sourceMapRootpath) {
  17. this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/');
  18. if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') {
  19. this._sourceMapRootpath += '/';
  20. }
  21. } else {
  22. this._sourceMapRootpath = '';
  23. }
  24. this._outputSourceFiles = options.outputSourceFiles;
  25. this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator();
  26. this._lineNumber = 0;
  27. this._column = 0;
  28. }
  29. removeBasepath(path) {
  30. if (this._sourceMapBasepath && path.indexOf(this._sourceMapBasepath) === 0) {
  31. path = path.substring(this._sourceMapBasepath.length);
  32. if (path.charAt(0) === '\\' || path.charAt(0) === '/') {
  33. path = path.substring(1);
  34. }
  35. }
  36. return path;
  37. }
  38. normalizeFilename(filename) {
  39. filename = filename.replace(/\\/g, '/');
  40. filename = this.removeBasepath(filename);
  41. return (this._sourceMapRootpath || '') + filename;
  42. }
  43. add(chunk, fileInfo, index, mapLines) {
  44. // ignore adding empty strings
  45. if (!chunk) {
  46. return;
  47. }
  48. let lines;
  49. let sourceLines;
  50. let columns;
  51. let sourceColumns;
  52. let i;
  53. if (fileInfo && fileInfo.filename) {
  54. let inputSource = this._contentsMap[fileInfo.filename];
  55. // remove vars/banner added to the top of the file
  56. if (this._contentsIgnoredCharsMap[fileInfo.filename]) {
  57. // adjust the index
  58. index -= this._contentsIgnoredCharsMap[fileInfo.filename];
  59. if (index < 0) { index = 0; }
  60. // adjust the source
  61. inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]);
  62. }
  63. // ignore empty content
  64. if (inputSource === undefined) {
  65. return;
  66. }
  67. inputSource = inputSource.substring(0, index);
  68. sourceLines = inputSource.split('\n');
  69. sourceColumns = sourceLines[sourceLines.length - 1];
  70. }
  71. lines = chunk.split('\n');
  72. columns = lines[lines.length - 1];
  73. if (fileInfo && fileInfo.filename) {
  74. if (!mapLines) {
  75. this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column},
  76. original: { line: sourceLines.length, column: sourceColumns.length},
  77. source: this.normalizeFilename(fileInfo.filename)});
  78. } else {
  79. for (i = 0; i < lines.length; i++) {
  80. this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0},
  81. original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0},
  82. source: this.normalizeFilename(fileInfo.filename)});
  83. }
  84. }
  85. }
  86. if (lines.length === 1) {
  87. this._column += columns.length;
  88. } else {
  89. this._lineNumber += lines.length - 1;
  90. this._column = columns.length;
  91. }
  92. this._css.push(chunk);
  93. }
  94. isEmpty() {
  95. return this._css.length === 0;
  96. }
  97. toCSS(context) {
  98. this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
  99. if (this._outputSourceFiles) {
  100. for (const filename in this._contentsMap) {
  101. if (this._contentsMap.hasOwnProperty(filename)) {
  102. let source = this._contentsMap[filename];
  103. if (this._contentsIgnoredCharsMap[filename]) {
  104. source = source.slice(this._contentsIgnoredCharsMap[filename]);
  105. }
  106. this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
  107. }
  108. }
  109. }
  110. this._rootNode.genCSS(context, this);
  111. if (this._css.length > 0) {
  112. let sourceMapURL;
  113. const sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
  114. if (this.sourceMapURL) {
  115. sourceMapURL = this.sourceMapURL;
  116. } else if (this._sourceMapFilename) {
  117. sourceMapURL = this._sourceMapFilename;
  118. }
  119. this.sourceMapURL = sourceMapURL;
  120. this.sourceMap = sourceMapContent;
  121. }
  122. return this._css.join('');
  123. }
  124. }
  125. return SourceMapOutput;
  126. };