1 | ;
|
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 fs = require('fs');
|
15 | var os = require('os');
|
16 | var path = require('path');
|
17 | var zlib = require('zlib');
|
18 |
|
19 | /**
|
20 | * Read the contents from the compressed file.
|
21 | *
|
22 | * @async
|
23 | * @params {String} filename
|
24 | * @params {Function} callback
|
25 | */
|
26 | var read = function(filename, callback) {
|
27 | return fs.readFile(filename, function(err, data) {
|
28 | if (err) { return callback(err); }
|
29 |
|
30 | return zlib.gunzip(data, function(err, content) {
|
31 | var result = {};
|
32 |
|
33 | if (err) { return callback(err); }
|
34 |
|
35 | try {
|
36 | result = JSON.parse(content);
|
37 | } catch (e) {
|
38 | return callback(e);
|
39 | }
|
40 |
|
41 | return callback(null, result);
|
42 | });
|
43 | });
|
44 | };
|
45 |
|
46 |
|
47 | /**
|
48 | * Write contents into a compressed file.
|
49 | *
|
50 | * @async
|
51 | * @params {String} filename
|
52 | * @params {String} result
|
53 | * @params {Function} callback
|
54 | */
|
55 | var write = function(filename, result, callback) {
|
56 | var content = JSON.stringify(result);
|
57 |
|
58 | return zlib.gzip(content, function(err, data) {
|
59 | if (err) { return callback(err); }
|
60 |
|
61 | return fs.writeFile(filename, data, callback);
|
62 | });
|
63 | };
|
64 |
|
65 |
|
66 | /**
|
67 | * Build the filename for the cached file
|
68 | *
|
69 | * @params {String} source File source code
|
70 | * @params {Object} options Options used
|
71 | *
|
72 | * @return {String}
|
73 | */
|
74 | var filename = function(source, identifier, options) {
|
75 | var hash = crypto.createHash('SHA1');
|
76 | var contents = JSON.stringify({
|
77 | source: source,
|
78 | options: options,
|
79 | identifier: identifier,
|
80 | });
|
81 |
|
82 | hash.end(contents);
|
83 |
|
84 | return hash.read().toString('hex') + '.json.gzip';
|
85 | };
|
86 |
|
87 | /**
|
88 | * Retrieve file from cache, or create a new one for future reads
|
89 | *
|
90 | * @async
|
91 | * @param {Object} params
|
92 | * @param {String} params.directory Directory to store cached files
|
93 | * @param {String} params.identifier Unique identifier to bust cache
|
94 | * @param {String} params.source Original contents of the file to be cached
|
95 | * @param {Object} params.options Options to be given to the transform fn
|
96 | * @param {Function} params.transform Function that will transform the
|
97 | * original file and whose result will be
|
98 | * cached
|
99 | *
|
100 | * @param {Function<err, result>} callback
|
101 | *
|
102 | * @example
|
103 | *
|
104 | * cache({
|
105 | * directory: '.tmp/cache',
|
106 | * identifier: 'babel-loader-cachefile',
|
107 | * source: *source code from file*,
|
108 | * options: {
|
109 | * experimental: true,
|
110 | * runtime: true
|
111 | * },
|
112 | * transform: function(source, options) {
|
113 | * var content = *do what you need with the source*
|
114 | * return content;
|
115 | * }
|
116 | * }, function(err, result) {
|
117 | *
|
118 | * });
|
119 | */
|
120 | var cache = module.exports = function(params, callback) {
|
121 | // Spread params into named variables
|
122 | // Forgive user whenever possible
|
123 | var source = params.source;
|
124 | var options = params.options || {};
|
125 | var transform = params.transform;
|
126 | var identifier = params.identifier;
|
127 | var directory = (typeof params.directory === 'string') ?
|
128 | params.directory :
|
129 | os.tmpdir();
|
130 | var file = path.join(directory, filename(source, identifier, options));
|
131 |
|
132 | // Make sure the directory exists.
|
133 | return mkdirp(directory, function(err) {
|
134 | if (err) { return callback(err); }
|
135 |
|
136 | return read(file, function(err, content) {
|
137 | var result = {};
|
138 | // No errors mean that the file was previously cached
|
139 | // we just need to return it
|
140 | if (!err) { return callback(null, content); }
|
141 |
|
142 | // Otherwise just transform the file
|
143 | // return it to the user asap and write it in cache
|
144 | try {
|
145 | result = transform(source, options);
|
146 | } catch (error) {
|
147 | return callback(error);
|
148 | }
|
149 |
|
150 | return write(file, result, function(err) {
|
151 | return callback(err, result);
|
152 | });
|
153 | });
|
154 | });
|
155 | };
|