From 58534baa80eefd3f2b976e8261fa3020b9bac433 Mon Sep 17 00:00:00 2001 From: David Greenspan Date: Fri, 5 Feb 2016 03:40:49 -0800 Subject: [PATCH] ImportScanner: cache file stats and package.jsons This helps performance quite a bit, taking what could otherwise be (say) 10 seconds down to a couple seconds in ImportScanner for an app that imports a thousand files. --- tools/isobuild/import-scanner.js | 68 +++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/tools/isobuild/import-scanner.js b/tools/isobuild/import-scanner.js index 1d2dac8bdb..a259c30295 100644 --- a/tools/isobuild/import-scanner.js +++ b/tools/isobuild/import-scanner.js @@ -65,6 +65,9 @@ export default class ImportScanner { this.watchSet = watchSet; this.absPathToOutputIndex = {}; this.outputFiles = []; + + this._statCache = new Map; + this._pkgJsonCache = new Map; } addInputFiles(files) { @@ -386,30 +389,40 @@ export default class ImportScanner { } _joinAndStat(...joinArgs) { - const path = pathNormalize(pathJoin(...joinArgs)); - const exactStat = statOrNull(path); - const exactResult = exactStat && { path, stat: exactStat }; - if (exactResult && exactStat.isFile()) { - return exactResult; + const joined = pathJoin(...joinArgs); + if (this._statCache.has(joined)) { + return this._statCache.get(joined); } - for (let ext in extensions) { - if (has(extensions, ext)) { - const pathWithExt = path + ext; - const stat = statOrNull(pathWithExt); - if (stat) { - return { path: pathWithExt, stat }; + const path = pathNormalize(joined); + const exactStat = statOrNull(path); + const exactResult = exactStat && { path, stat: exactStat }; + let result = null; + if (exactResult && exactStat.isFile()) { + result = exactResult; + } + + if (!result) { + for (let ext in extensions) { + if (has(extensions, ext)) { + const pathWithExt = path + ext; + const stat = statOrNull(pathWithExt); + if (stat) { + result = { path: pathWithExt, stat }; + break; + } } } } - if (exactResult && exactStat.isDirectory()) { + if (!result && exactResult && exactStat.isDirectory()) { // After trying all available file extensions, fall back to the // original result if it was a directory. - return exactResult; + result = exactResult; } - return null; + this._statCache.set(joined, result); + return result; } _resolveAbsolute(file, id) { @@ -465,15 +478,25 @@ export default class ImportScanner { return resolved; } + _readPkgJson(path) { + if (this._pkgJsonCache.has(path)) { + return this._pkgJsonCache.get(path); + } + + let result = null; + try { + result = JSON.parse(this._readFile(path).data); + } catch (e) { + // leave result null + } + + this._pkgJsonCache.set(path, result); + return result; + } + _resolvePkgJsonMain(dirPath, seenDirPaths) { const pkgJsonPath = pathJoin(dirPath, "package.json"); - - let pkg; - try { - pkg = JSON.parse(this._readFile(pkgJsonPath).data); - } catch (e) { - return null; - } + const pkg = this._readPkgJson(pkgJsonPath); if (pkg && isString(pkg.main)) { // The "main" field of package.json does not have to begin with ./ @@ -529,7 +552,8 @@ export default class ImportScanner { } each(["_readFile", "_findImportedModuleIdentifiers", - "_getInstallPath", "_tryToResolveImportedPath"], funcName => { + "_getInstallPath", "_tryToResolveImportedPath", + "_resolvePkgJsonMain"], funcName => { ImportScanner.prototype[funcName] = Profile( `ImportScanner#${funcName}`, ImportScanner.prototype[funcName]); });