source-maps.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. 'use strict';
  2. var fs = require('fs');
  3. var path = require('path');
  4. var define = require('define-property');
  5. var utils = require('./utils');
  6. /**
  7. * Expose `mixin()`.
  8. * This code is based on `source-maps-support.js` in reworkcss/css
  9. * https://github.com/reworkcss/css/blob/master/lib/stringify/source-map-support.js
  10. * Copyright (c) 2012 TJ Holowaychuk <[email protected]>
  11. */
  12. module.exports = mixin;
  13. /**
  14. * Mixin source map support into `compiler`.
  15. *
  16. * @param {Object} `compiler`
  17. * @api public
  18. */
  19. function mixin(compiler) {
  20. define(compiler, '_comment', compiler.comment);
  21. compiler.map = new utils.SourceMap.SourceMapGenerator();
  22. compiler.position = { line: 1, column: 1 };
  23. compiler.content = {};
  24. compiler.files = {};
  25. for (var key in exports) {
  26. define(compiler, key, exports[key]);
  27. }
  28. }
  29. /**
  30. * Update position.
  31. *
  32. * @param {String} str
  33. */
  34. exports.updatePosition = function(str) {
  35. var lines = str.match(/\n/g);
  36. if (lines) this.position.line += lines.length;
  37. var i = str.lastIndexOf('\n');
  38. this.position.column = ~i ? str.length - i : this.position.column + str.length;
  39. };
  40. /**
  41. * Emit `str` with `position`.
  42. *
  43. * @param {String} str
  44. * @param {Object} [pos]
  45. * @return {String}
  46. */
  47. exports.emit = function(str, node) {
  48. var position = node.position || {};
  49. var source = position.source;
  50. if (source) {
  51. if (position.filepath) {
  52. source = utils.unixify(position.filepath);
  53. }
  54. this.map.addMapping({
  55. source: source,
  56. generated: {
  57. line: this.position.line,
  58. column: Math.max(this.position.column - 1, 0)
  59. },
  60. original: {
  61. line: position.start.line,
  62. column: position.start.column - 1
  63. }
  64. });
  65. if (position.content) {
  66. this.addContent(source, position);
  67. }
  68. if (position.filepath) {
  69. this.addFile(source, position);
  70. }
  71. this.updatePosition(str);
  72. this.output += str;
  73. }
  74. return str;
  75. };
  76. /**
  77. * Adds a file to the source map output if it has not already been added
  78. * @param {String} `file`
  79. * @param {Object} `pos`
  80. */
  81. exports.addFile = function(file, position) {
  82. if (typeof position.content !== 'string') return;
  83. if (Object.prototype.hasOwnProperty.call(this.files, file)) return;
  84. this.files[file] = position.content;
  85. };
  86. /**
  87. * Adds a content source to the source map output if it has not already been added
  88. * @param {String} `source`
  89. * @param {Object} `position`
  90. */
  91. exports.addContent = function(source, position) {
  92. if (typeof position.content !== 'string') return;
  93. if (Object.prototype.hasOwnProperty.call(this.content, source)) return;
  94. this.map.setSourceContent(source, position.content);
  95. };
  96. /**
  97. * Applies any original source maps to the output and embeds the source file
  98. * contents in the source map.
  99. */
  100. exports.applySourceMaps = function() {
  101. Object.keys(this.files).forEach(function(file) {
  102. var content = this.files[file];
  103. this.map.setSourceContent(file, content);
  104. if (this.options.inputSourcemaps === true) {
  105. var originalMap = utils.sourceMapResolve.resolveSync(content, file, fs.readFileSync);
  106. if (originalMap) {
  107. var map = new utils.SourceMap.SourceMapConsumer(originalMap.map);
  108. var relativeTo = originalMap.sourcesRelativeTo;
  109. this.map.applySourceMap(map, file, utils.unixify(path.dirname(relativeTo)));
  110. }
  111. }
  112. }, this);
  113. };
  114. /**
  115. * Process comments, drops sourceMap comments.
  116. * @param {Object} node
  117. */
  118. exports.comment = function(node) {
  119. if (/^# sourceMappingURL=/.test(node.comment)) {
  120. return this.emit('', node.position);
  121. }
  122. return this._comment(node);
  123. };