103 lines
2.4 KiB
JavaScript
103 lines
2.4 KiB
JavaScript
|
'use strict';
|
||
|
const fs = require('fs');
|
||
|
const path = require('path');
|
||
|
const hasha = require('hasha');
|
||
|
const makeDir = require('make-dir');
|
||
|
const writeFileAtomic = require('write-file-atomic');
|
||
|
const packageHash = require('package-hash');
|
||
|
|
||
|
let ownHash = '';
|
||
|
function getOwnHash() {
|
||
|
ownHash = packageHash.sync(path.join(__dirname, 'package.json'));
|
||
|
return ownHash;
|
||
|
}
|
||
|
|
||
|
function wrap(opts) {
|
||
|
if (!(opts.factory || opts.transform) || (opts.factory && opts.transform)) {
|
||
|
throw new Error('Specify factory or transform but not both');
|
||
|
}
|
||
|
|
||
|
if (typeof opts.cacheDir !== 'string' && !opts.disableCache) {
|
||
|
throw new Error('cacheDir must be a string');
|
||
|
}
|
||
|
|
||
|
opts = {
|
||
|
ext: '',
|
||
|
salt: '',
|
||
|
hashData: () => [],
|
||
|
filenamePrefix: () => '',
|
||
|
onHash: () => {},
|
||
|
...opts
|
||
|
};
|
||
|
|
||
|
let transformFn = opts.transform;
|
||
|
const {factory, cacheDir, shouldTransform, disableCache, hashData, onHash, filenamePrefix, ext, salt} = opts;
|
||
|
const cacheDirCreated = opts.createCacheDir === false;
|
||
|
let created = transformFn && cacheDirCreated;
|
||
|
const encoding = opts.encoding === 'buffer' ? undefined : opts.encoding || 'utf8';
|
||
|
|
||
|
function transform(input, metadata, hash) {
|
||
|
if (!created) {
|
||
|
if (!cacheDirCreated && !disableCache) {
|
||
|
makeDir.sync(cacheDir);
|
||
|
}
|
||
|
|
||
|
if (!transformFn) {
|
||
|
transformFn = factory(cacheDir);
|
||
|
}
|
||
|
|
||
|
created = true;
|
||
|
}
|
||
|
|
||
|
return transformFn(input, metadata, hash);
|
||
|
}
|
||
|
|
||
|
return function (input, metadata) {
|
||
|
if (shouldTransform && !shouldTransform(input, metadata)) {
|
||
|
return input;
|
||
|
}
|
||
|
|
||
|
if (disableCache) {
|
||
|
return transform(input, metadata);
|
||
|
}
|
||
|
|
||
|
const data = [
|
||
|
ownHash || getOwnHash(),
|
||
|
input,
|
||
|
salt,
|
||
|
...[].concat(hashData(input, metadata))
|
||
|
];
|
||
|
const hash = hasha(data, {algorithm: 'sha256'});
|
||
|
const cachedPath = path.join(cacheDir, filenamePrefix(metadata) + hash + ext);
|
||
|
|
||
|
onHash(input, metadata, hash);
|
||
|
|
||
|
let result;
|
||
|
let retry = 0;
|
||
|
/* eslint-disable-next-line no-constant-condition */
|
||
|
while (true) {
|
||
|
try {
|
||
|
return fs.readFileSync(cachedPath, encoding);
|
||
|
} catch (readError) {
|
||
|
if (!result) {
|
||
|
result = transform(input, metadata, hash);
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
writeFileAtomic.sync(cachedPath, result, {encoding});
|
||
|
return result;
|
||
|
} catch (error) {
|
||
|
/* Likely https://github.com/npm/write-file-atomic/issues/28
|
||
|
* Make up to 3 attempts to read or write the cache. */
|
||
|
retry++;
|
||
|
if (retry > 3) {
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
module.exports = wrap;
|