This repository has been archived by the owner on Sep 28, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 121
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: new cache implementation (#320)
* refactor: new cache implementation add Filesystem Cache implementation instead of loader-fs-cache * chore: remove unused devDependencies
- Loading branch information
Showing
5 changed files
with
267 additions
and
80 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/** | ||
* Original Filesystem Cache implementation by babel-loader | ||
* Licensed under the MIT License | ||
* | ||
* @see https://github.com/babel/babel-loader/commits/master/src/fs-cache.js | ||
* @see https://github.com/babel/babel-loader/commits/master/src/cache.js | ||
*/ | ||
|
||
/** | ||
* 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 | ||
*/ | ||
import fs from 'fs'; | ||
import os from 'os'; | ||
import { join } from 'path'; | ||
import { promisify } from 'util'; | ||
import zlib from 'zlib'; | ||
import { createHash } from 'crypto'; | ||
|
||
import findCacheDir from 'find-cache-dir'; | ||
|
||
// Lazily instantiated when needed | ||
let defaultCacheDirectory = null; | ||
|
||
const readFile = promisify(fs.readFile); | ||
const writeFile = promisify(fs.writeFile); | ||
const gunzip = promisify(zlib.gunzip); | ||
const gzip = promisify(zlib.gzip); | ||
|
||
/** | ||
* Read the contents from the compressed file. | ||
* | ||
* @async | ||
* @params {String} filename | ||
* @params {Boolean} compress | ||
*/ | ||
const read = async (filename, compress) => { | ||
const data = await readFile(filename + (compress ? '.gz' : '')); | ||
const content = compress ? await gunzip(data) : data; | ||
|
||
return JSON.parse(content.toString()); | ||
}; | ||
|
||
/** | ||
* Write contents into a compressed file. | ||
* | ||
* @async | ||
* @params {String} filename | ||
* @params {Boolean} compress | ||
* @params {String} result | ||
*/ | ||
const write = async (filename, compress, result) => { | ||
const content = JSON.stringify(result); | ||
|
||
const data = compress ? await gzip(content) : content; | ||
return writeFile(filename + (compress ? '.gz' : ''), data); | ||
}; | ||
|
||
/** | ||
* Build the filename for the cached file | ||
* | ||
* @params {String} source File source code | ||
* @params {String} identifier | ||
* @params {Object} options Options used | ||
* | ||
* @return {String} | ||
*/ | ||
const filename = (source, identifier, options) => { | ||
const hash = createHash('md4'); | ||
|
||
const contents = JSON.stringify({ source, options, identifier }); | ||
|
||
hash.update(contents); | ||
|
||
return `${hash.digest('hex')}.json`; | ||
}; | ||
|
||
/** | ||
* Handle the cache | ||
* | ||
* @params {String} directory | ||
* @params {Object} params | ||
*/ | ||
const handleCache = async (directory, params) => { | ||
const { | ||
source, | ||
options = {}, | ||
transform, | ||
cacheIdentifier, | ||
cacheDirectory, | ||
cacheCompression, | ||
} = params; | ||
|
||
const file = join(directory, filename(source, cacheIdentifier, options)); | ||
|
||
try { | ||
// No errors mean that the file was previously cached | ||
// we just need to return it | ||
return await read(file, cacheCompression); | ||
// eslint-disable-next-line no-empty | ||
} catch (err) {} | ||
|
||
const fallback = | ||
typeof cacheDirectory !== 'string' && directory !== os.tmpdir(); | ||
|
||
// Make sure the directory exists. | ||
try { | ||
fs.mkdirSync(directory, { recursive: true }); | ||
} catch (err) { | ||
if (fallback) { | ||
return handleCache(os.tmpdir(), params); | ||
} | ||
|
||
throw err; | ||
} | ||
|
||
// Otherwise just transform the file | ||
// return it to the user asap and write it in cache | ||
const result = await transform(source, options); | ||
|
||
try { | ||
await write(file, cacheCompression, result); | ||
} catch (err) { | ||
if (fallback) { | ||
// Fallback to tmpdir if node_modules folder not writable | ||
return handleCache(os.tmpdir(), params); | ||
} | ||
|
||
throw err; | ||
} | ||
|
||
return result; | ||
}; | ||
|
||
/** | ||
* Retrieve file from cache, or create a new one for future reads | ||
* | ||
* @async | ||
* @param {Object} params | ||
* @param {String} params.cacheDirectory Directory to store cached files | ||
* @param {String} params.cacheIdentifier Unique identifier to bust cache | ||
* @param {Boolean} params.cacheCompression | ||
* @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 | ||
* | ||
* @example | ||
* | ||
* cache({ | ||
* cacheDirectory: '.tmp/cache', | ||
* cacheIdentifier: 'babel-loader-cachefile', | ||
* cacheCompression: true, | ||
* 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; | ||
* } | ||
* }); | ||
*/ | ||
|
||
module.exports = async (params) => { | ||
let directory; | ||
|
||
if (typeof params.cacheDirectory === 'string') { | ||
directory = params.cacheDirectory; | ||
} else { | ||
if (defaultCacheDirectory === null) { | ||
defaultCacheDirectory = | ||
findCacheDir({ name: 'eslint-loader' }) || os.tmpdir(); | ||
} | ||
|
||
directory = defaultCacheDirectory; | ||
} | ||
|
||
return handleCache(directory, params); | ||
}; |
Oops, something went wrong.