| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 | /** * Filesystem cache * * Given a file and a transform function, cache the result into files * or retrieve the previously cached files if the given file is already known. * * @see https://github.com/babel/babel-loader/issues/34 * @see https://github.com/babel/babel-loader/pull/41 * @see https://github.com/babel/babel-loader/blob/master/src/fs-cache.js */var crypto = require("crypto");var mkdirp = require("mkdirp");var findCacheDir = require("find-cache-dir");var fs = require("fs");var os = require("os");var path = require("path");var zlib = require("zlib");var defaultCacheDirectory = null; // Lazily instantiated when needed/** * Read the contents from the compressed file. * * @async * @params {String} filename * @params {Function} callback */var read = function(filename, callback) {  return fs.readFile(filename, function(err, data) {    if (err) {      return callback(err);    }    return zlib.gunzip(data, function(err, content) {      var result = {};      if (err) {        return callback(err);      }      try {        result = JSON.parse(content);      } catch (e) {        return callback(e);      }      return callback(null, result);    });  });};/** * Write contents into a compressed file. * * @async * @params {String} filename * @params {String} result * @params {Function} callback */var write = function(filename, result, callback) {  var content = JSON.stringify(result);  return zlib.gzip(content, function(err, data) {    if (err) {      return callback(err);    }    return fs.writeFile(filename, data, callback);  });};/** * Build the filename for the cached file * * @params {String} source  File source code * @params {Object} options Options used * * @return {String} */var filename = function(source, identifier, options) {  var hash = crypto.createHash("SHA1");  var contents = JSON.stringify({    source: source,    options: options,    identifier: identifier  });  hash.end(contents);  return hash.read().toString("hex") + ".json.gz";};/** * Handle the cache * * @params {String} directory * @params {Object} params * @params {Function} callback */var handleCache = function(directory, params, callback) {  var source = params.source;  var options = params.options || {};  var transform = params.transform;  var identifier = params.identifier;  var shouldFallback = typeof params.directory !== "string" &&    directory !== os.tmpdir();  // Make sure the directory exists.  mkdirp(directory, function(err) {    // Fallback to tmpdir if node_modules folder not writable    if (err)      return shouldFallback        ? handleCache(os.tmpdir(), params, callback)        : callback(err);    var file = path.join(directory, filename(source, identifier, options));    return read(file, function(err, content) {      var result = {};      // No errors mean that the file was previously cached      // we just need to return it      if (!err) return callback(null, content);      // Otherwise just transform the file      // return it to the user asap and write it in cache      try {        result = transform(source, options);      } catch (error) {        return callback(error);      }      return write(file, result, function(err) {        // Fallback to tmpdir if node_modules folder not writable        if (err)          return shouldFallback            ? handleCache(os.tmpdir(), params, callback)            : callback(err);        callback(null, result);      });    });  });};/** * Retrieve file from cache, or create a new one for future reads * * @async * @param  {Object}   params * @param  {String}   params.directory  Directory to store cached files * @param  {String}   params.identifier Unique identifier to bust cache * @param  {String}   params.source   Original contents of the file to be cached * @param  {Object}   params.options  Options to be given to the transform fn * @param  {Function} params.transform  Function that will transform the *                                      original file and whose result will be *                                      cached * * @param  {Function<err, result>} callback * * @example * *   cache({ *     directory: '.tmp/cache', *     identifier: 'babel-loader-cachefile', *     source: *source code from file*, *     options: { *       experimental: true, *       runtime: true *     }, *     transform: function(source, options) { *       var content = *do what you need with the source* *       return content *     } *   }, function(err, result) { * *   }) */module.exports = function createFsCache(name) {  return function(params, callback) {    var directory;    if (typeof params.directory === "string") {      directory = params.directory;    } else {      if (defaultCacheDirectory === null) {        defaultCacheDirectory = findCacheDir({          name: name        }) ||          os.tmpdir();      }      directory = defaultCacheDirectory;    }    handleCache(directory, params, callback);  };};
 |