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);
- };
- };
|