Skip to content

Commit 5de0c65

Browse files
author
Shubham Kanodia
committed
Bump minimum supported node version to 12
1 parent 068be4b commit 5de0c65

File tree

2 files changed

+160
-62
lines changed

2 files changed

+160
-62
lines changed

src/cache.js

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,62 @@
77
* @see https://github.com/babel/babel-loader/issues/34
88
* @see https://github.com/babel/babel-loader/pull/41
99
*/
10+
const fs = require("fs");
1011
const os = require("os");
12+
const path = require("path");
13+
const zlib = require("zlib");
1114
const crypto = require("crypto");
1215
const findCacheDir = require("find-cache-dir");
13-
const { open } = require("lmdb");
16+
const { promisify } = require("util");
1417

1518
const transform = require("./transform");
1619
// Lazily instantiated when needed
1720
let defaultCacheDirectory = null;
18-
let cacheDB = null;
21+
22+
const readFile = promisify(fs.readFile);
23+
const writeFile = promisify(fs.writeFile);
24+
const gunzip = promisify(zlib.gunzip);
25+
const gzip = promisify(zlib.gzip);
26+
const makeDir = require("make-dir");
1927

2028
/**
21-
* Initialize cache
29+
* Read the contents from the compressed file.
30+
*
31+
* @async
32+
* @params {String} filename
33+
* @params {Boolean} compress
2234
*/
35+
const read = async function (filename, compress) {
36+
const data = await readFile(filename + (compress ? ".gz" : ""));
37+
const content = compress ? await gunzip(data) : data;
2338

24-
async function initCacheDB(cacheDir, cacheCompression) {
25-
if (cacheDB) return cacheDB;
26-
const fallback = cacheDir !== os.tmpdir();
39+
return JSON.parse(content.toString());
40+
};
2741

28-
try {
29-
cacheDB = open({
30-
path: cacheDir,
31-
compression: cacheCompression,
32-
sharedStructuresKey: Symbol.for(`structures`),
33-
});
34-
} catch (err) {
35-
if (fallback) {
36-
cacheDB = initCacheDB(os.tmpdir(), cacheCompression);
37-
}
42+
/**
43+
* Write contents into a compressed file.
44+
*
45+
* @async
46+
* @params {String} filename
47+
* @params {Boolean} compress
48+
* @params {String} result
49+
*/
50+
const write = async function (filename, compress, result) {
51+
const content = JSON.stringify(result);
3852

39-
throw err;
40-
}
41-
}
53+
const data = compress ? await gzip(content) : content;
54+
return await writeFile(filename + (compress ? ".gz" : ""), data);
55+
};
4256

4357
/**
44-
* Build the cache key for the cached file
58+
* Build the filename for the cached file
4559
*
4660
* @params {String} source File source code
4761
* @params {Object} options Options used
4862
*
4963
* @return {String}
5064
*/
51-
const fileCacheKey = function (source, identifier, options) {
65+
const filename = function (source, identifier, options) {
5266
// md4 hashing is not supported starting with node v17.0.0
5367
const majorNodeVersion = parseInt(process.versions.node.split(".")[0], 10);
5468
let hashType = "md4";
@@ -62,7 +76,7 @@ const fileCacheKey = function (source, identifier, options) {
6276

6377
hash.update(contents);
6478

65-
return hash.digest("hex");
79+
return hash.digest("hex") + ".json";
6680
};
6781

6882
/**
@@ -71,21 +85,51 @@ const fileCacheKey = function (source, identifier, options) {
7185
* @params {String} directory
7286
* @params {Object} params
7387
*/
74-
const handleCache = async function (params) {
75-
const { source, options = {}, cacheIdentifier } = params;
88+
const handleCache = async function (directory, params) {
89+
const {
90+
source,
91+
options = {},
92+
cacheIdentifier,
93+
cacheDirectory,
94+
cacheCompression,
95+
} = params;
7696

77-
const cacheKey = fileCacheKey(source, cacheIdentifier, options);
97+
const file = path.join(directory, filename(source, cacheIdentifier, options));
98+
99+
try {
100+
// No errors mean that the file was previously cached
101+
// we just need to return it
102+
return await read(file, cacheCompression);
103+
} catch (err) {}
78104

79-
// Fetch cached result if it exists
80-
const cached = await cacheDB.get(cacheKey);
81-
if (typeof cached !== "undefined") {
82-
return cached;
105+
const fallback =
106+
typeof cacheDirectory !== "string" && directory !== os.tmpdir();
107+
108+
// Make sure the directory exists.
109+
try {
110+
await makeDir(directory);
111+
} catch (err) {
112+
if (fallback) {
113+
return handleCache(os.tmpdir(), params);
114+
}
115+
116+
throw err;
83117
}
84118

85-
// Otherwise, just transform the cacheKey
119+
// Otherwise just transform the file
86120
// return it to the user asap and write it in cache
87121
const result = await transform(source, options);
88-
cacheDB.put(cacheKey, result);
122+
123+
try {
124+
await write(file, cacheCompression, result);
125+
} catch (err) {
126+
if (fallback) {
127+
// Fallback to tmpdir if node_modules folder not writable
128+
return handleCache(os.tmpdir(), params);
129+
}
130+
131+
throw err;
132+
}
89133

90134
return result;
91135
};
@@ -129,7 +173,5 @@ module.exports = async function (params) {
129173
directory = defaultCacheDirectory;
130174
}
131175

132-
await initCacheDB(directory, params.cacheCompression);
133-
134-
return await handleCache(params);
176+
return await handleCache(directory, params);
135177
};

test/cache.test.js

Lines changed: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import test from "ava";
2+
import fs from "fs";
23
import path from "path";
34
import rimraf from "rimraf";
45
import webpack from "webpack";
@@ -11,7 +12,6 @@ const defaultCacheDir = path.join(
1112
const cacheDir = path.join(__dirname, "output/cache/cachefiles");
1213
const outputDir = path.join(__dirname, "output/cache");
1314
const babelLoader = path.join(__dirname, "../lib");
14-
const { open } = require("lmdb");
1515

1616
const globalConfig = {
1717
mode: "development",
@@ -48,7 +48,7 @@ test.beforeEach.cb(t => rimraf(defaultCacheDir, t.end));
4848
test.afterEach.cb(t => rimraf(t.context.directory, t.end));
4949
test.afterEach.cb(t => rimraf(t.context.cacheDirectory, t.end));
5050

51-
test.cb("should build a cache database in the cache directory", t => {
51+
test.cb("should output files to cache directory", t => {
5252
const config = Object.assign({}, globalConfig, {
5353
output: {
5454
path: t.context.directory,
@@ -73,14 +73,16 @@ test.cb("should build a cache database in the cache directory", t => {
7373
t.deepEqual(stats.compilation.errors, []);
7474
t.deepEqual(stats.compilation.warnings, []);
7575

76-
const cacheDB = open(t.context.cacheDirectory, { readOnly: true });
77-
t.true(cacheDB.getStats().entryCount > 0);
78-
t.end();
76+
fs.readdir(t.context.cacheDirectory, (err, files) => {
77+
t.is(err, null);
78+
t.true(files.length > 0);
79+
t.end();
80+
});
7981
});
8082
});
8183

82-
test.serial.cb.only(
83-
"should add entries to cache db at standard cache dir by default",
84+
test.serial.cb(
85+
"should output json.gz files to standard cache dir by default",
8486
t => {
8587
const config = Object.assign({}, globalConfig, {
8688
output: {
@@ -106,15 +108,56 @@ test.serial.cb.only(
106108
t.deepEqual(stats.compilation.errors, []);
107109
t.deepEqual(stats.compilation.warnings, []);
108110

109-
const cacheDB = open(defaultCacheDir, { readOnly: true });
110-
t.true(cacheDB.getStats().entryCount > 0);
111-
t.end();
111+
fs.readdir(defaultCacheDir, (err, files) => {
112+
files = files.filter(file => /\b[0-9a-f]{5,40}\.json\.gz\b/.test(file));
113+
114+
t.is(err, null);
115+
t.true(files.length > 0);
116+
t.end();
117+
});
112118
});
113119
},
114120
);
115121

116122
test.serial.cb(
117-
"should add entries to cache db at standard cache dir if set to true in query",
123+
"should output non-compressed files to standard cache dir when cacheCompression is set to false",
124+
t => {
125+
const config = Object.assign({}, globalConfig, {
126+
output: {
127+
path: t.context.directory,
128+
},
129+
module: {
130+
rules: [
131+
{
132+
test: /\.jsx?/,
133+
loader: babelLoader,
134+
exclude: /node_modules/,
135+
options: {
136+
cacheDirectory: true,
137+
cacheCompression: false,
138+
presets: ["@babel/preset-env"],
139+
},
140+
},
141+
],
142+
},
143+
});
144+
145+
webpack(config, err => {
146+
t.is(err, null);
147+
148+
fs.readdir(defaultCacheDir, (err, files) => {
149+
files = files.filter(file => /\b[0-9a-f]{5,40}\b/.test(file));
150+
151+
t.is(err, null);
152+
t.true(files.length > 0);
153+
t.end();
154+
});
155+
});
156+
},
157+
);
158+
159+
test.serial.cb(
160+
"should output files to standard cache dir if set to true in query",
118161
t => {
119162
const config = Object.assign({}, globalConfig, {
120163
output: {
@@ -136,9 +179,14 @@ test.serial.cb(
136179
t.deepEqual(stats.compilation.errors, []);
137180
t.deepEqual(stats.compilation.warnings, []);
138181

139-
const cacheDB = open(t.context.cacheDirectory, { readOnly: true });
140-
t.true(cacheDB.getStats().entryCount > 0);
141-
t.end();
182+
fs.readdir(defaultCacheDir, (err, files) => {
183+
files = files.filter(file => /\b[0-9a-f]{5,40}\.json\.gz\b/.test(file));
184+
185+
t.is(err, null);
186+
187+
t.true(files.length > 0);
188+
t.end();
189+
});
142190
});
143191
},
144192
);
@@ -172,14 +220,16 @@ test.cb("should read from cache directory if cached file exists", t => {
172220

173221
webpack(config, err => {
174222
t.is(err, null);
175-
const cacheDB = open(t.context.cacheDirectory, { readOnly: true });
176-
t.true(cacheDB.getStats().entryCount > 0);
177-
t.end();
223+
fs.readdir(t.context.cacheDirectory, (err, files) => {
224+
t.is(err, null);
225+
t.true(files.length > 0);
226+
t.end();
227+
});
178228
});
179229
});
180230
});
181231

182-
test.cb("should have one cache entry per module", t => {
232+
test.cb("should have one file per module", t => {
183233
const config = Object.assign({}, globalConfig, {
184234
output: {
185235
path: t.context.directory,
@@ -204,13 +254,15 @@ test.cb("should have one cache entry per module", t => {
204254
t.deepEqual(stats.compilation.errors, []);
205255
t.deepEqual(stats.compilation.warnings, []);
206256

207-
const cacheDB = open(t.context.cacheDirectory, { readOnly: true });
208-
t.true(cacheDB.getStats().entryCount === 3);
209-
t.end();
257+
fs.readdir(t.context.cacheDirectory, (err, files) => {
258+
t.is(err, null);
259+
t.true(files.length === 3);
260+
t.end();
261+
});
210262
});
211263
});
212264

213-
test.cb("should add a new cache entry if the identifier changes", t => {
265+
test.cb("should generate a new file if the identifier changes", t => {
214266
const configs = [
215267
Object.assign({}, globalConfig, {
216268
output: {
@@ -261,9 +313,11 @@ test.cb("should add a new cache entry if the identifier changes", t => {
261313
counter -= 1;
262314

263315
if (!counter) {
264-
const cacheDB = open(t.context.cacheDirectory, { readOnly: true });
265-
t.true(cacheDB.getStats().entryCount === 6);
266-
t.end();
316+
fs.readdir(t.context.cacheDirectory, (err, files) => {
317+
t.is(err, null);
318+
t.true(files.length === 6);
319+
t.end();
320+
});
267321
}
268322
});
269323
});
@@ -320,8 +374,10 @@ test.cb("should allow to specify the .babelrc file", t => {
320374
t.deepEqual(multiStats.stats[1].compilation.errors, []);
321375
t.deepEqual(multiStats.stats[1].compilation.warnings, []);
322376

323-
const cacheDB = open(t.context.cacheDirectory, { readOnly: true });
324-
t.true(cacheDB.getStats().entryCount === 1);
325-
t.end();
377+
fs.readdir(t.context.cacheDirectory, (err, files) => {
378+
t.is(err, null);
379+
t.true(files.length === 2);
380+
t.end();
381+
});
326382
});
327383
});

0 commit comments

Comments
 (0)