cache.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. "use strict";
  2. function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
  3. function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
  4. /**
  5. * Filesystem Cache
  6. *
  7. * Given a file and a transform function, cache the result into files
  8. * or retrieve the previously cached files if the given file is already known.
  9. *
  10. * @see https://github.com/babel/babel-loader/issues/34
  11. * @see https://github.com/babel/babel-loader/pull/41
  12. */
  13. const fs = require("fs");
  14. const os = require("os");
  15. const path = require("path");
  16. const zlib = require("zlib");
  17. const crypto = require("crypto");
  18. const findCacheDir = require("find-cache-dir");
  19. const {
  20. promisify
  21. } = require("util");
  22. const transform = require("./transform");
  23. // Lazily instantiated when needed
  24. let defaultCacheDirectory = null;
  25. let hashType = "sha256";
  26. // use md5 hashing if sha256 is not available
  27. try {
  28. crypto.createHash(hashType);
  29. } catch (err) {
  30. hashType = "md5";
  31. }
  32. const readFile = promisify(fs.readFile);
  33. const writeFile = promisify(fs.writeFile);
  34. const gunzip = promisify(zlib.gunzip);
  35. const gzip = promisify(zlib.gzip);
  36. const makeDir = require("make-dir");
  37. /**
  38. * Read the contents from the compressed file.
  39. *
  40. * @async
  41. * @params {String} filename
  42. * @params {Boolean} compress
  43. */
  44. const read = /*#__PURE__*/function () {
  45. var _ref = _asyncToGenerator(function* (filename, compress) {
  46. const data = yield readFile(filename + (compress ? ".gz" : ""));
  47. const content = compress ? yield gunzip(data) : data;
  48. return JSON.parse(content.toString());
  49. });
  50. return function read(_x, _x2) {
  51. return _ref.apply(this, arguments);
  52. };
  53. }();
  54. /**
  55. * Write contents into a compressed file.
  56. *
  57. * @async
  58. * @params {String} filename
  59. * @params {Boolean} compress
  60. * @params {String} result
  61. */
  62. const write = /*#__PURE__*/function () {
  63. var _ref2 = _asyncToGenerator(function* (filename, compress, result) {
  64. const content = JSON.stringify(result);
  65. const data = compress ? yield gzip(content) : content;
  66. return yield writeFile(filename + (compress ? ".gz" : ""), data);
  67. });
  68. return function write(_x3, _x4, _x5) {
  69. return _ref2.apply(this, arguments);
  70. };
  71. }();
  72. /**
  73. * Build the filename for the cached file
  74. *
  75. * @params {String} source File source code
  76. * @params {Object} options Options used
  77. *
  78. * @return {String}
  79. */
  80. const filename = function (source, identifier, options) {
  81. const hash = crypto.createHash(hashType);
  82. const contents = JSON.stringify({
  83. source,
  84. options,
  85. identifier
  86. });
  87. hash.update(contents);
  88. return hash.digest("hex") + ".json";
  89. };
  90. /**
  91. * Handle the cache
  92. *
  93. * @params {String} directory
  94. * @params {Object} params
  95. */
  96. const handleCache = /*#__PURE__*/function () {
  97. var _ref3 = _asyncToGenerator(function* (directory, params) {
  98. const {
  99. source,
  100. options = {},
  101. cacheIdentifier,
  102. cacheDirectory,
  103. cacheCompression
  104. } = params;
  105. const file = path.join(directory, filename(source, cacheIdentifier, options));
  106. try {
  107. // No errors mean that the file was previously cached
  108. // we just need to return it
  109. return yield read(file, cacheCompression);
  110. } catch (err) {}
  111. const fallback = typeof cacheDirectory !== "string" && directory !== os.tmpdir();
  112. // Make sure the directory exists.
  113. try {
  114. yield makeDir(directory);
  115. } catch (err) {
  116. if (fallback) {
  117. return handleCache(os.tmpdir(), params);
  118. }
  119. throw err;
  120. }
  121. // Otherwise just transform the file
  122. // return it to the user asap and write it in cache
  123. const result = yield transform(source, options);
  124. // Do not cache if there are external dependencies,
  125. // since they might change and we cannot control it.
  126. if (!result.externalDependencies.length) {
  127. try {
  128. yield write(file, cacheCompression, result);
  129. } catch (err) {
  130. if (fallback) {
  131. // Fallback to tmpdir if node_modules folder not writable
  132. return handleCache(os.tmpdir(), params);
  133. }
  134. throw err;
  135. }
  136. }
  137. return result;
  138. });
  139. return function handleCache(_x6, _x7) {
  140. return _ref3.apply(this, arguments);
  141. };
  142. }();
  143. /**
  144. * Retrieve file from cache, or create a new one for future reads
  145. *
  146. * @async
  147. * @param {Object} params
  148. * @param {String} params.cacheDirectory Directory to store cached files
  149. * @param {String} params.cacheIdentifier Unique identifier to bust cache
  150. * @param {Boolean} params.cacheCompression Whether compressing cached files
  151. * @param {String} params.source Original contents of the file to be cached
  152. * @param {Object} params.options Options to be given to the transform fn
  153. *
  154. * @example
  155. *
  156. * const result = await cache({
  157. * cacheDirectory: '.tmp/cache',
  158. * cacheIdentifier: 'babel-loader-cachefile',
  159. * cacheCompression: false,
  160. * source: *source code from file*,
  161. * options: {
  162. * experimental: true,
  163. * runtime: true
  164. * },
  165. * });
  166. */
  167. module.exports = /*#__PURE__*/function () {
  168. var _ref4 = _asyncToGenerator(function* (params) {
  169. let directory;
  170. if (typeof params.cacheDirectory === "string") {
  171. directory = params.cacheDirectory;
  172. } else {
  173. if (defaultCacheDirectory === null) {
  174. defaultCacheDirectory = findCacheDir({
  175. name: "babel-loader"
  176. }) || os.tmpdir();
  177. }
  178. directory = defaultCacheDirectory;
  179. }
  180. return yield handleCache(directory, params);
  181. });
  182. return function (_x8) {
  183. return _ref4.apply(this, arguments);
  184. };
  185. }();