You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							197 lines
						
					
					
						
							5.0 KiB
						
					
					
				
			
		
		
	
	
							197 lines
						
					
					
						
							5.0 KiB
						
					
					
				/**
 | 
						|
 * 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);
 | 
						|
  };
 | 
						|
};
 |