From e7eef7fcb8ab716d21dca5933dbd9f27860fb194 Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Sat, 11 May 2019 08:17:13 +0100 Subject: [PATCH] perf: add cached version of `isDir` (#218) * perf: add cached version of `isDir` * refactor: DRY up cache functions * refactor: use Map for cache over Object * refactor: rename variables for clarity --- src/index.js | 56 +++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/index.js b/src/index.js index 47437d1..b5b6c61 100644 --- a/src/index.js +++ b/src/index.js @@ -9,33 +9,29 @@ const ES6_BROWSER_EMPTY = resolve( __dirname, '../src/empty.js' ); // which deploy both ESM .mjs and CommonJS .js files as ESM. const DEFAULT_EXTS = [ '.mjs', '.js', '.json', '.node' ]; -let readFileCache = {}; const readFileAsync = file => new Promise((fulfil, reject) => fs.readFile(file, (err, contents) => err ? reject(err) : fulfil(contents))); const statAsync = file => new Promise((fulfil, reject) => fs.stat(file, (err, contents) => err ? reject(err) : fulfil(contents))); -function cachedReadFile (file, cb) { - if (file in readFileCache === false) { - readFileCache[file] = readFileAsync(file).catch(err => { - delete readFileCache[file]; - throw err; - }); - } - readFileCache[file].then(contents => cb(null, contents), cb); -} - -let isFileCache = {}; -function cachedIsFile (file, cb) { - if (file in isFileCache === false) { - isFileCache[file] = statAsync(file) - .then( - stat => stat.isFile(), - err => { - if (err.code === 'ENOENT') return false; - delete isFileCache[file]; - throw err; - }); - } - isFileCache[file].then(contents => cb(null, contents), cb); -} +const cache = fn => { + const cache = new Map(); + const wrapped = (param, done) => { + if (cache.has(param) === false) { + cache.set(param, fn(param).catch(err => { + cache.delete(param); + throw err; + })); + } + return cache.get(param).then(result => done(null, result), done); + }; + wrapped.clear = () => cache.clear(); + return wrapped; +}; +const ignoreENOENT = err => { + if (err.code === 'ENOENT') return false; + throw err; +}; +const readFileCached = cache(readFileAsync); +const isDirCached = cache(file => statAsync(file).then(stat => stat.isDirectory(), ignoreENOENT)); +const isFileCached = cache(file => statAsync(file).then(stat => stat.isFile(), ignoreENOENT)); function getMainFields (options) { let mainFields; @@ -99,8 +95,9 @@ export default function nodeResolve ( options = {} ) { }, generateBundle () { - isFileCache = {}; - readFileCache = {}; + readFileCached.clear(); + isFileCached.clear(); + isDirCached.clear(); }, resolveId ( importee, importer ) { @@ -180,8 +177,9 @@ export default function nodeResolve ( options = {} ) { } return pkg; }, - readFile: cachedReadFile, - isFile: cachedIsFile, + readFile: readFileCached, + isFile: isFileCached, + isDirectory: isDirCached, extensions: extensions };