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.
		
		
		
		
		
			
		
			
				
					
					
						
							196 lines
						
					
					
						
							5.0 KiB
						
					
					
				
			
		
		
	
	
							196 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); | 
						|
  }; | 
						|
};
 | 
						|
 |