RawSource.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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 streamChunksOfRawSource = require("./helpers/streamChunksOfRawSource");
  8. const {
  9. internString,
  10. isDualStringBufferCachingEnabled,
  11. } = require("./helpers/stringBufferUtils");
  12. /** @typedef {import("./Source").ClearCacheOptions} ClearCacheOptions */
  13. /** @typedef {import("./Source").HashLike} HashLike */
  14. /** @typedef {import("./Source").MapOptions} MapOptions */
  15. /** @typedef {import("./Source").RawSourceMap} RawSourceMap */
  16. /** @typedef {import("./Source").SourceValue} SourceValue */
  17. /** @typedef {import("./helpers/getGeneratedSourceInfo").GeneratedSourceInfo} GeneratedSourceInfo */
  18. /** @typedef {import("./helpers/streamChunks").OnChunk} OnChunk */
  19. /** @typedef {import("./helpers/streamChunks").OnName} OnName */
  20. /** @typedef {import("./helpers/streamChunks").OnSource} OnSource */
  21. /** @typedef {import("./helpers/streamChunks").Options} Options */
  22. class RawSource extends Source {
  23. /**
  24. * @param {string | Buffer} value value
  25. * @param {boolean=} convertToString convert to string
  26. */
  27. constructor(value, convertToString = false) {
  28. super();
  29. const isBuffer = Buffer.isBuffer(value);
  30. if (isBuffer) {
  31. /**
  32. * @private
  33. * @type {boolean}
  34. */
  35. this._valueIsBuffer = !convertToString;
  36. /**
  37. * @private
  38. * @type {undefined | string | Buffer}
  39. */
  40. this._value = convertToString ? undefined : value;
  41. /**
  42. * @private
  43. * @type {undefined | Buffer}
  44. */
  45. this._valueAsBuffer = value;
  46. /**
  47. * @private
  48. * @type {undefined | string}
  49. */
  50. this._valueAsString = undefined;
  51. } else if (typeof value === "string") {
  52. const interned = internString(value);
  53. this._valueIsBuffer = false;
  54. this._value = interned;
  55. this._valueAsBuffer = undefined;
  56. this._valueAsString = interned;
  57. } else {
  58. throw new TypeError("argument 'value' must be either string or Buffer");
  59. }
  60. }
  61. isBuffer() {
  62. return this._valueIsBuffer;
  63. }
  64. /**
  65. * @returns {SourceValue} source
  66. */
  67. source() {
  68. if (this._value === undefined) {
  69. const value =
  70. /** @type {Buffer} */
  71. (this._valueAsBuffer).toString("utf8");
  72. if (isDualStringBufferCachingEnabled()) {
  73. this._value = internString(value);
  74. }
  75. return value;
  76. }
  77. return this._value;
  78. }
  79. /**
  80. * @returns {Buffer} buffer
  81. */
  82. buffer() {
  83. if (this._valueAsBuffer === undefined) {
  84. const value = Buffer.from(/** @type {string} */ (this._value), "utf8");
  85. if (isDualStringBufferCachingEnabled()) {
  86. this._valueAsBuffer = value;
  87. }
  88. return value;
  89. }
  90. return this._valueAsBuffer;
  91. }
  92. /**
  93. * @returns {number} size
  94. */
  95. size() {
  96. if (this._cachedSize !== undefined) return this._cachedSize;
  97. if (this._valueAsBuffer !== undefined) {
  98. return (this._cachedSize = this._valueAsBuffer.length);
  99. }
  100. return (this._cachedSize = Buffer.byteLength(
  101. /** @type {string} */ (this._valueAsString),
  102. "utf8",
  103. ));
  104. }
  105. /**
  106. * @param {MapOptions=} options map options
  107. * @returns {RawSourceMap | null} map
  108. */
  109. // eslint-disable-next-line no-unused-vars
  110. map(options) {
  111. return null;
  112. }
  113. /**
  114. * @param {Options} options options
  115. * @param {OnChunk} onChunk called for each chunk of code
  116. * @param {OnSource} onSource called for each source
  117. * @param {OnName} onName called for each name
  118. * @returns {GeneratedSourceInfo} generated source info
  119. */
  120. streamChunks(options, onChunk, onSource, onName) {
  121. let strValue = this._valueAsString;
  122. if (strValue === undefined) {
  123. const value = this.source();
  124. strValue = typeof value === "string" ? value : value.toString("utf8");
  125. if (isDualStringBufferCachingEnabled()) {
  126. this._valueAsString = internString(strValue);
  127. }
  128. }
  129. return streamChunksOfRawSource(
  130. strValue,
  131. onChunk,
  132. onSource,
  133. onName,
  134. Boolean(options && options.finalSource),
  135. );
  136. }
  137. /**
  138. * Release cached data held by this source. clearCache is a memory
  139. * hint: it never affects correctness or output, only how expensive
  140. * the next read is. Subclasses override; the base is a no-op so
  141. * every Source supports the call. Composite sources always recurse
  142. * into wrapped sources. When the same child is reachable via several
  143. * parents (e.g. modules shared across webpack chunks), pass a shared
  144. * `visited` WeakSet so each subtree is walked at most once.
  145. * Not safe to call concurrently with source/map/sourceAndMap/
  146. * streamChunks/updateHash on the same instance.
  147. * @param {ClearCacheOptions=} options selectors
  148. * @param {WeakSet<Source>=} visited de-duplication set shared across calls
  149. * @returns {void}
  150. */
  151. clearCache(options, visited) {
  152. if (visited !== undefined) {
  153. if (visited.has(this)) return;
  154. visited.add(this);
  155. }
  156. if (options !== undefined && options.source === false) return;
  157. if (this._valueIsBuffer) {
  158. // Buffer is the primary representation (and lives in `_value`);
  159. // only the string form, populated lazily by streamChunks, is
  160. // safe to drop.
  161. this._valueAsString = undefined;
  162. } else if (this._valueAsBuffer !== undefined && this._value !== undefined) {
  163. // The source was constructed from a string (so `_value` and
  164. // `_valueAsString` reference the same interned string) and the
  165. // buffer form was materialized later by `buffer()` /
  166. // `updateHash()`. The buffer is therefore safe to drop.
  167. this._valueAsBuffer = undefined;
  168. }
  169. }
  170. /**
  171. * @param {HashLike} hash hash
  172. * @returns {void}
  173. */
  174. updateHash(hash) {
  175. hash.update("RawSource");
  176. hash.update(this.buffer());
  177. }
  178. }
  179. module.exports = RawSource;