Skip to content
This repository was archived by the owner on Sep 28, 2020. It is now read-only.

Commit acae2be

Browse files
authored
refactor: new cache implementation (#320)
* refactor: new cache implementation add Filesystem Cache implementation instead of loader-fs-cache * chore: remove unused devDependencies
1 parent c086892 commit acae2be

File tree

5 files changed

+267
-80
lines changed

5 files changed

+267
-80
lines changed

package-lock.json

+58-50
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
"webpack": "^4.0.0 || ^5.0.0"
4242
},
4343
"dependencies": {
44+
"find-cache-dir": "^3.3.1",
4445
"fs-extra": "^9.0.0",
45-
"loader-fs-cache": "^1.0.3",
4646
"loader-utils": "^2.0.0",
4747
"object-hash": "^2.0.3",
4848
"schema-utils": "^2.6.5"
@@ -70,7 +70,6 @@
7070
"jest": "^25.2.6",
7171
"jest-junit": "^10.0.0",
7272
"lint-staged": "^10.1.1",
73-
"mkdirp": "^1.0.3",
7473
"npm-run-all": "^4.1.5",
7574
"prettier": "^2.0.2",
7675
"standard-version": "^7.1.0",

src/cache.js

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/**
2+
* Original Filesystem Cache implementation by babel-loader
3+
* Licensed under the MIT License
4+
*
5+
* @see https://github.com/babel/babel-loader/commits/master/src/fs-cache.js
6+
* @see https://github.com/babel/babel-loader/commits/master/src/cache.js
7+
*/
8+
9+
/**
10+
* Filesystem Cache
11+
*
12+
* Given a file and a transform function, cache the result into files
13+
* or retrieve the previously cached files if the given file is already known.
14+
*
15+
* @see https://github.com/babel/babel-loader/issues/34
16+
* @see https://github.com/babel/babel-loader/pull/41
17+
*/
18+
import fs from 'fs';
19+
import os from 'os';
20+
import { join } from 'path';
21+
import { promisify } from 'util';
22+
import zlib from 'zlib';
23+
import { createHash } from 'crypto';
24+
25+
import findCacheDir from 'find-cache-dir';
26+
27+
// Lazily instantiated when needed
28+
let defaultCacheDirectory = null;
29+
30+
const readFile = promisify(fs.readFile);
31+
const writeFile = promisify(fs.writeFile);
32+
const gunzip = promisify(zlib.gunzip);
33+
const gzip = promisify(zlib.gzip);
34+
35+
/**
36+
* Read the contents from the compressed file.
37+
*
38+
* @async
39+
* @params {String} filename
40+
* @params {Boolean} compress
41+
*/
42+
const read = async (filename, compress) => {
43+
const data = await readFile(filename + (compress ? '.gz' : ''));
44+
const content = compress ? await gunzip(data) : data;
45+
46+
return JSON.parse(content.toString());
47+
};
48+
49+
/**
50+
* Write contents into a compressed file.
51+
*
52+
* @async
53+
* @params {String} filename
54+
* @params {Boolean} compress
55+
* @params {String} result
56+
*/
57+
const write = async (filename, compress, result) => {
58+
const content = JSON.stringify(result);
59+
60+
const data = compress ? await gzip(content) : content;
61+
return writeFile(filename + (compress ? '.gz' : ''), data);
62+
};
63+
64+
/**
65+
* Build the filename for the cached file
66+
*
67+
* @params {String} source File source code
68+
* @params {String} identifier
69+
* @params {Object} options Options used
70+
*
71+
* @return {String}
72+
*/
73+
const filename = (source, identifier, options) => {
74+
const hash = createHash('md4');
75+
76+
const contents = JSON.stringify({ source, options, identifier });
77+
78+
hash.update(contents);
79+
80+
return `${hash.digest('hex')}.json`;
81+
};
82+
83+
/**
84+
* Handle the cache
85+
*
86+
* @params {String} directory
87+
* @params {Object} params
88+
*/
89+
const handleCache = async (directory, params) => {
90+
const {
91+
source,
92+
options = {},
93+
transform,
94+
cacheIdentifier,
95+
cacheDirectory,
96+
cacheCompression,
97+
} = params;
98+
99+
const file = join(directory, filename(source, cacheIdentifier, options));
100+
101+
try {
102+
// No errors mean that the file was previously cached
103+
// we just need to return it
104+
return await read(file, cacheCompression);
105+
// eslint-disable-next-line no-empty
106+
} catch (err) {}
107+
108+
const fallback =
109+
typeof cacheDirectory !== 'string' && directory !== os.tmpdir();
110+
111+
// Make sure the directory exists.
112+
try {
113+
fs.mkdirSync(directory, { recursive: true });
114+
} catch (err) {
115+
if (fallback) {
116+
return handleCache(os.tmpdir(), params);
117+
}
118+
119+
throw err;
120+
}
121+
122+
// Otherwise just transform the file
123+
// return it to the user asap and write it in cache
124+
const result = await transform(source, options);
125+
126+
try {
127+
await write(file, cacheCompression, result);
128+
} catch (err) {
129+
if (fallback) {
130+
// Fallback to tmpdir if node_modules folder not writable
131+
return handleCache(os.tmpdir(), params);
132+
}
133+
134+
throw err;
135+
}
136+
137+
return result;
138+
};
139+
140+
/**
141+
* Retrieve file from cache, or create a new one for future reads
142+
*
143+
* @async
144+
* @param {Object} params
145+
* @param {String} params.cacheDirectory Directory to store cached files
146+
* @param {String} params.cacheIdentifier Unique identifier to bust cache
147+
* @param {Boolean} params.cacheCompression
148+
* @param {String} params.source Original contents of the file to be cached
149+
* @param {Object} params.options Options to be given to the transform fn
150+
* @param {Function} params.transform Function that will transform the
151+
* original file and whose result will be
152+
* cached
153+
*
154+
* @example
155+
*
156+
* cache({
157+
* cacheDirectory: '.tmp/cache',
158+
* cacheIdentifier: 'babel-loader-cachefile',
159+
* cacheCompression: true,
160+
* source: *source code from file*,
161+
* options: {
162+
* experimental: true,
163+
* runtime: true
164+
* },
165+
* transform: function(source, options) {
166+
* var content = *do what you need with the source*
167+
* return content;
168+
* }
169+
* });
170+
*/
171+
172+
module.exports = async (params) => {
173+
let directory;
174+
175+
if (typeof params.cacheDirectory === 'string') {
176+
directory = params.cacheDirectory;
177+
} else {
178+
if (defaultCacheDirectory === null) {
179+
defaultCacheDirectory =
180+
findCacheDir({ name: 'eslint-loader' }) || os.tmpdir();
181+
}
182+
183+
directory = defaultCacheDirectory;
184+
}
185+
186+
return handleCache(directory, params);
187+
};

0 commit comments

Comments
 (0)