OriginalSource.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Source = require("./Source");
  7. const { getMap, getSourceAndMap } = require("./helpers/getFromStreamChunks");
  8. const getGeneratedSourceInfo = require("./helpers/getGeneratedSourceInfo");
  9. const splitIntoLines = require("./helpers/splitIntoLines");
  10. const splitIntoPotentialTokens = require("./helpers/splitIntoPotentialTokens");
  11. const {
  12. isDualStringBufferCachingEnabled,
  13. } = require("./helpers/stringBufferUtils");
  14. /** @typedef {import("./Source").HashLike} HashLike */
  15. /** @typedef {import("./Source").MapOptions} MapOptions */
  16. /** @typedef {import("./Source").RawSourceMap} RawSourceMap */
  17. /** @typedef {import("./Source").SourceAndMap} SourceAndMap */
  18. /** @typedef {import("./Source").SourceValue} SourceValue */
  19. /** @typedef {import("./helpers/getGeneratedSourceInfo").GeneratedSourceInfo} GeneratedSourceInfo */
  20. /** @typedef {import("./helpers/streamChunks").OnChunk} OnChunk */
  21. /** @typedef {import("./helpers/streamChunks").OnName} OnName */
  22. /** @typedef {import("./helpers/streamChunks").OnSource} OnSource */
  23. /** @typedef {import("./helpers/streamChunks").Options} Options */
  24. class OriginalSource extends Source {
  25. /**
  26. * @param {string | Buffer} value value
  27. * @param {string} name name
  28. */
  29. constructor(value, name) {
  30. super();
  31. const isBuffer = Buffer.isBuffer(value);
  32. /**
  33. * @private
  34. * @type {undefined | string}
  35. */
  36. this._value = isBuffer ? undefined : value;
  37. /**
  38. * @private
  39. * @type {undefined | Buffer}
  40. */
  41. this._valueAsBuffer = isBuffer ? value : undefined;
  42. /**
  43. * @private
  44. * @type {string}
  45. */
  46. this._name = name;
  47. }
  48. getName() {
  49. return this._name;
  50. }
  51. /**
  52. * @returns {SourceValue} source
  53. */
  54. source() {
  55. if (this._value === undefined) {
  56. const value =
  57. /** @type {Buffer} */
  58. (this._valueAsBuffer).toString("utf8");
  59. if (isDualStringBufferCachingEnabled()) {
  60. this._value = value;
  61. }
  62. return value;
  63. }
  64. return this._value;
  65. }
  66. buffer() {
  67. if (this._valueAsBuffer === undefined) {
  68. const value = Buffer.from(/** @type {string} */ (this._value), "utf8");
  69. if (isDualStringBufferCachingEnabled()) {
  70. this._valueAsBuffer = value;
  71. }
  72. return value;
  73. }
  74. return this._valueAsBuffer;
  75. }
  76. /**
  77. * @param {MapOptions=} options map options
  78. * @returns {RawSourceMap | null} map
  79. */
  80. map(options) {
  81. return getMap(this, options);
  82. }
  83. /**
  84. * @param {MapOptions=} options map options
  85. * @returns {SourceAndMap} source and map
  86. */
  87. sourceAndMap(options) {
  88. return getSourceAndMap(this, options);
  89. }
  90. /**
  91. * @param {Options} options options
  92. * @param {OnChunk} onChunk called for each chunk of code
  93. * @param {OnSource} onSource called for each source
  94. * @param {OnName} _onName called for each name
  95. * @returns {GeneratedSourceInfo} generated source info
  96. */
  97. streamChunks(options, onChunk, onSource, _onName) {
  98. if (this._value === undefined) {
  99. this._value =
  100. /** @type {Buffer} */
  101. (this._valueAsBuffer).toString("utf8");
  102. }
  103. onSource(0, this._name, this._value);
  104. const finalSource = Boolean(options && options.finalSource);
  105. if (!options || options.columns !== false) {
  106. // With column info we need to read all lines and split them
  107. const matches = splitIntoPotentialTokens(this._value);
  108. let line = 1;
  109. let column = 0;
  110. if (matches !== null) {
  111. for (const match of matches) {
  112. const isEndOfLine = match.endsWith("\n");
  113. if (isEndOfLine && match.length === 1) {
  114. if (!finalSource) onChunk(match, line, column, -1, -1, -1, -1);
  115. } else {
  116. const chunk = finalSource ? undefined : match;
  117. onChunk(chunk, line, column, 0, line, column, -1);
  118. }
  119. if (isEndOfLine) {
  120. line++;
  121. column = 0;
  122. } else {
  123. column += match.length;
  124. }
  125. }
  126. }
  127. return {
  128. generatedLine: line,
  129. generatedColumn: column,
  130. source: finalSource ? this._value : undefined,
  131. };
  132. } else if (finalSource) {
  133. // Without column info and with final source we only
  134. // need meta info to generate mapping
  135. const result = getGeneratedSourceInfo(this._value);
  136. const { generatedLine, generatedColumn } = result;
  137. if (generatedColumn === 0) {
  138. for (
  139. let line = 1;
  140. line < /** @type {number} */ (generatedLine);
  141. line++
  142. ) {
  143. onChunk(undefined, line, 0, 0, line, 0, -1);
  144. }
  145. } else {
  146. for (
  147. let line = 1;
  148. line <= /** @type {number} */ (generatedLine);
  149. line++
  150. ) {
  151. onChunk(undefined, line, 0, 0, line, 0, -1);
  152. }
  153. }
  154. return result;
  155. }
  156. // Without column info, but also without final source
  157. // we need to split source by lines
  158. let line = 1;
  159. const matches = splitIntoLines(this._value);
  160. /** @type {string | undefined} */
  161. let match;
  162. for (match of matches) {
  163. onChunk(finalSource ? undefined : match, line, 0, 0, line, 0, -1);
  164. line++;
  165. }
  166. return matches.length === 0 || /** @type {string} */ (match).endsWith("\n")
  167. ? {
  168. generatedLine: matches.length + 1,
  169. generatedColumn: 0,
  170. source: finalSource ? this._value : undefined,
  171. }
  172. : {
  173. generatedLine: matches.length,
  174. generatedColumn: /** @type {string} */ (match).length,
  175. source: finalSource ? this._value : undefined,
  176. };
  177. }
  178. /**
  179. * @param {HashLike} hash hash
  180. * @returns {void}
  181. */
  182. updateHash(hash) {
  183. hash.update("OriginalSource");
  184. hash.update(this.buffer());
  185. hash.update(this._name || "");
  186. }
  187. }
  188. module.exports = OriginalSource;