cache.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. logger
  105. } = params;
  106. const file = path.join(directory, filename(source, cacheIdentifier, options));
  107. try {
  108. // No errors mean that the file was previously cached
  109. // we just need to return it
  110. logger.debug(`reading cache file '${file}'`);
  111. return yield read(file, cacheCompression);
  112. } catch (err) {
  113. // conitnue if cache can't be read
  114. logger.debug(`discarded cache as it can not be read`);
  115. }
  116. const fallback = typeof cacheDirectory !== "string" && directory !== os.tmpdir();
  117. // Make sure the directory exists.
  118. try {
  119. logger.debug(`creating cache folder '${directory}'`);
  120. yield makeDir(directory);
  121. } catch (err) {
  122. if (fallback) {
  123. return handleCache(os.tmpdir(), params);
  124. }
  125. throw err;
  126. }
  127. // Otherwise just transform the file
  128. // return it to the user asap and write it in cache
  129. logger.debug(`applying Babel transform`);
  130. const result = yield transform(source, options);
  131. // Do not cache if there are external dependencies,
  132. // since they might change and we cannot control it.
  133. if (!result.externalDependencies.length) {
  134. try {
  135. logger.debug(`writing result to cache file '${file}'`);
  136. yield write(file, cacheCompression, result);
  137. } catch (err) {
  138. if (fallback) {
  139. // Fallback to tmpdir if node_modules folder not writable
  140. return handleCache(os.tmpdir(), params);
  141. }
  142. throw err;
  143. }
  144. }
  145. return result;
  146. });
  147. return function handleCache(_x6, _x7) {
  148. return _ref3.apply(this, arguments);
  149. };
  150. }();
  151. /**
  152. * Retrieve file from cache, or create a new one for future reads
  153. *
  154. * @async
  155. * @param {Object} params
  156. * @param {String} params.cacheDirectory Directory to store cached files
  157. * @param {String} params.cacheIdentifier Unique identifier to bust cache
  158. * @param {Boolean} params.cacheCompression Whether compressing cached files
  159. * @param {String} params.source Original contents of the file to be cached
  160. * @param {Object} params.options Options to be given to the transform fn
  161. *
  162. * @example
  163. *
  164. * const result = await cache({
  165. * cacheDirectory: '.tmp/cache',
  166. * cacheIdentifier: 'babel-loader-cachefile',
  167. * cacheCompression: false,
  168. * source: *source code from file*,
  169. * options: {
  170. * experimental: true,
  171. * runtime: true
  172. * },
  173. * });
  174. */
  175. module.exports = /*#__PURE__*/function () {
  176. var _ref4 = _asyncToGenerator(function* (params) {
  177. let directory;
  178. if (typeof params.cacheDirectory === "string") {
  179. directory = params.cacheDirectory;
  180. } else {
  181. if (defaultCacheDirectory === null) {
  182. defaultCacheDirectory = findCacheDir({
  183. name: "babel-loader"
  184. }) || os.tmpdir();
  185. }
  186. directory = defaultCacheDirectory;
  187. }
  188. return yield handleCache(directory, params);
  189. });
  190. return function (_x8) {
  191. return _ref4.apply(this, arguments);
  192. };
  193. }();