cache.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. "use strict";
  2. var _fs = _interopRequireDefault(require("fs"));
  3. var _os = _interopRequireDefault(require("os"));
  4. var _path = require("path");
  5. var _util = require("util");
  6. var _zlib = _interopRequireDefault(require("zlib"));
  7. var _crypto = require("crypto");
  8. var _findCacheDir = _interopRequireDefault(require("find-cache-dir"));
  9. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  10. /**
  11. * Original Filesystem Cache implementation by babel-loader
  12. * Licensed under the MIT License
  13. *
  14. * @see https://github.com/babel/babel-loader/commits/master/src/fs-cache.js
  15. * @see https://github.com/babel/babel-loader/commits/master/src/cache.js
  16. */
  17. /**
  18. * Filesystem Cache
  19. *
  20. * Given a file and a transform function, cache the result into files
  21. * or retrieve the previously cached files if the given file is already known.
  22. *
  23. * @see https://github.com/babel/babel-loader/issues/34
  24. * @see https://github.com/babel/babel-loader/pull/41
  25. */
  26. // Lazily instantiated when needed
  27. let defaultCacheDirectory = null;
  28. const readFile = (0, _util.promisify)(_fs.default.readFile);
  29. const writeFile = (0, _util.promisify)(_fs.default.writeFile);
  30. const gunzip = (0, _util.promisify)(_zlib.default.gunzip);
  31. const gzip = (0, _util.promisify)(_zlib.default.gzip);
  32. /**
  33. * Read the contents from the compressed file.
  34. *
  35. * @async
  36. * @params {String} filename
  37. * @params {Boolean} compress
  38. */
  39. const read = async (filename, compress) => {
  40. const data = await readFile(filename + (compress ? '.gz' : ''));
  41. const content = compress ? await gunzip(data) : data;
  42. return JSON.parse(content.toString());
  43. };
  44. /**
  45. * Write contents into a compressed file.
  46. *
  47. * @async
  48. * @params {String} filename
  49. * @params {Boolean} compress
  50. * @params {String} result
  51. */
  52. const write = async (filename, compress, result) => {
  53. const content = JSON.stringify(result);
  54. const data = compress ? await gzip(content) : content;
  55. return writeFile(filename + (compress ? '.gz' : ''), data);
  56. };
  57. /**
  58. * Build the filename for the cached file
  59. *
  60. * @params {String} source File source code
  61. * @params {String} identifier
  62. * @params {Object} options Options used
  63. *
  64. * @return {String}
  65. */
  66. const filename = (source, identifier, options) => {
  67. const hash = (0, _crypto.createHash)('md4');
  68. const contents = JSON.stringify({
  69. source,
  70. options,
  71. identifier
  72. });
  73. hash.update(contents);
  74. return `${hash.digest('hex')}.json`;
  75. };
  76. /**
  77. * Handle the cache
  78. *
  79. * @params {String} directory
  80. * @params {Object} params
  81. */
  82. const handleCache = async (directory, params) => {
  83. const {
  84. source,
  85. options = {},
  86. transform,
  87. cacheIdentifier,
  88. cacheDirectory,
  89. cacheCompression
  90. } = params;
  91. const file = (0, _path.join)(directory, filename(source, cacheIdentifier, options));
  92. try {
  93. // No errors mean that the file was previously cached
  94. // we just need to return it
  95. return await read(file, cacheCompression); // eslint-disable-next-line no-empty
  96. } catch (err) {}
  97. const fallback = typeof cacheDirectory !== 'string' && directory !== _os.default.tmpdir(); // Make sure the directory exists.
  98. try {
  99. _fs.default.mkdirSync(directory, {
  100. recursive: true
  101. });
  102. } catch (err) {
  103. if (fallback) {
  104. return handleCache(_os.default.tmpdir(), params);
  105. }
  106. throw err;
  107. } // Otherwise just transform the file
  108. // return it to the user asap and write it in cache
  109. const result = await transform(source, options);
  110. try {
  111. await write(file, cacheCompression, result);
  112. } catch (err) {
  113. if (fallback) {
  114. // Fallback to tmpdir if node_modules folder not writable
  115. return handleCache(_os.default.tmpdir(), params);
  116. }
  117. throw err;
  118. }
  119. return result;
  120. };
  121. /**
  122. * Retrieve file from cache, or create a new one for future reads
  123. *
  124. * @async
  125. * @param {Object} params
  126. * @param {String} params.cacheDirectory Directory to store cached files
  127. * @param {String} params.cacheIdentifier Unique identifier to bust cache
  128. * @param {Boolean} params.cacheCompression
  129. * @param {String} params.source Original contents of the file to be cached
  130. * @param {Object} params.options Options to be given to the transform fn
  131. * @param {Function} params.transform Function that will transform the
  132. * original file and whose result will be
  133. * cached
  134. *
  135. * @example
  136. *
  137. * cache({
  138. * cacheDirectory: '.tmp/cache',
  139. * cacheIdentifier: 'babel-loader-cachefile',
  140. * cacheCompression: true,
  141. * source: *source code from file*,
  142. * options: {
  143. * experimental: true,
  144. * runtime: true
  145. * },
  146. * transform: function(source, options) {
  147. * var content = *do what you need with the source*
  148. * return content;
  149. * }
  150. * });
  151. */
  152. module.exports = async params => {
  153. let directory;
  154. if (typeof params.cacheDirectory === 'string') {
  155. directory = params.cacheDirectory;
  156. } else {
  157. if (defaultCacheDirectory === null) {
  158. defaultCacheDirectory = (0, _findCacheDir.default)({
  159. name: 'eslint-loader'
  160. }) || _os.default.tmpdir();
  161. }
  162. directory = defaultCacheDirectory;
  163. }
  164. return handleCache(directory, params);
  165. };