Projekt

Obecné

Profil

Stáhnout (4.78 KB) Statistiky
| Větev: | Revize:
1
"use strict";
2

    
3
/**
4
 * Filesystem cache
5
 *
6
 * Given a file and a transform function, cache the result into files
7
 * or retrieve the previously cached files if the given file is already known.
8
 *
9
 * @see https://github.com/babel/babel-loader/issues/34
10
 * @see https://github.com/babel/babel-loader/pull/41
11
 */
12
var crypto = require("crypto");
13
var mkdirp = require("mkdirp");
14
var findCacheDir = require("find-cache-dir");
15
var fs = require("fs");
16
var os = require("os");
17
var path = require("path");
18
var zlib = require("zlib");
19

    
20
var defaultCacheDirectory = null; // Lazily instantiated when needed
21

    
22
/**
23
 * Read the contents from the compressed file.
24
 *
25
 * @async
26
 * @params {String} filename
27
 * @params {Function} callback
28
 */
29
var read = function read(filename, callback) {
30
  return fs.readFile(filename, function (err, data) {
31
    if (err) return callback(err);
32

    
33
    return zlib.gunzip(data, function (err, content) {
34
      if (err) return callback(err);
35

    
36
      var result = {};
37

    
38
      try {
39
        result = JSON.parse(content);
40
      } catch (e) {
41
        return callback(e);
42
      }
43

    
44
      return callback(null, result);
45
    });
46
  });
47
};
48

    
49
/**
50
 * Write contents into a compressed file.
51
 *
52
 * @async
53
 * @params {String} filename
54
 * @params {String} result
55
 * @params {Function} callback
56
 */
57
var write = function write(filename, result, callback) {
58
  var content = JSON.stringify(result);
59

    
60
  return zlib.gzip(content, function (err, data) {
61
    if (err) return callback(err);
62

    
63
    return fs.writeFile(filename, data, callback);
64
  });
65
};
66

    
67
/**
68
 * Build the filename for the cached file
69
 *
70
 * @params {String} source  File source code
71
 * @params {Object} options Options used
72
 *
73
 * @return {String}
74
 */
75
var filename = function filename(source, identifier, options) {
76
  var hash = crypto.createHash("md4");
77
  var contents = JSON.stringify({
78
    source: source,
79
    options: options,
80
    identifier: identifier
81
  });
82

    
83
  hash.update(contents);
84

    
85
  return hash.digest("hex") + ".json.gz";
86
};
87

    
88
/**
89
 * Handle the cache
90
 *
91
 * @params {String} directory
92
 * @params {Object} params
93
 * @params {Function} callback
94
 */
95
var handleCache = function handleCache(directory, params, callback) {
96
  var source = params.source;
97
  var options = params.options || {};
98
  var transform = params.transform;
99
  var identifier = params.identifier;
100
  var shouldFallback = typeof params.directory !== "string" && directory !== os.tmpdir();
101

    
102
  // Make sure the directory exists.
103
  mkdirp(directory, function (err) {
104
    // Fallback to tmpdir if node_modules folder not writable
105
    if (err) return shouldFallback ? handleCache(os.tmpdir(), params, callback) : callback(err);
106

    
107
    var file = path.join(directory, filename(source, identifier, options));
108

    
109
    return read(file, function (err, content) {
110
      var result = {};
111
      // No errors mean that the file was previously cached
112
      // we just need to return it
113
      if (!err) return callback(null, content);
114

    
115
      // Otherwise just transform the file
116
      // return it to the user asap and write it in cache
117
      try {
118
        result = transform(source, options);
119
      } catch (error) {
120
        return callback(error);
121
      }
122

    
123
      return write(file, result, function (err) {
124
        // Fallback to tmpdir if node_modules folder not writable
125
        if (err) return shouldFallback ? handleCache(os.tmpdir(), params, callback) : callback(err);
126

    
127
        callback(null, result);
128
      });
129
    });
130
  });
131
};
132

    
133
/**
134
 * Retrieve file from cache, or create a new one for future reads
135
 *
136
 * @async
137
 * @param  {Object}   params
138
 * @param  {String}   params.directory  Directory to store cached files
139
 * @param  {String}   params.identifier Unique identifier to bust cache
140
 * @param  {String}   params.source   Original contents of the file to be cached
141
 * @param  {Object}   params.options  Options to be given to the transform fn
142
 * @param  {Function} params.transform  Function that will transform the
143
 *                                      original file and whose result will be
144
 *                                      cached
145
 *
146
 * @param  {Function<err, result>} callback
147
 *
148
 * @example
149
 *
150
 *   cache({
151
 *     directory: '.tmp/cache',
152
 *     identifier: 'babel-loader-cachefile',
153
 *     source: *source code from file*,
154
 *     options: {
155
 *       experimental: true,
156
 *       runtime: true
157
 *     },
158
 *     transform: function(source, options) {
159
 *       var content = *do what you need with the source*
160
 *       return content;
161
 *     }
162
 *   }, function(err, result) {
163
 *
164
 *   });
165
 */
166

    
167
module.exports = function (params, callback) {
168
  var directory = void 0;
169

    
170
  if (typeof params.directory === "string") {
171
    directory = params.directory;
172
  } else {
173
    if (defaultCacheDirectory === null) {
174
      defaultCacheDirectory = findCacheDir({ name: "babel-loader" }) || os.tmpdir();
175
    }
176
    directory = defaultCacheDirectory;
177
  }
178

    
179
  handleCache(directory, params, callback);
180
};
(1-1/3)