diff --git a/CHANGELOG.md b/CHANGELOG.md index 01ab3f0819..aa1761b351 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) +- [Perf] Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1066,6 +1067,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 [#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 [#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 @@ -1737,6 +1739,7 @@ for info on changes for earlier releases. [@kylemh]: https://github.com/kylemh [@laysent]: https://github.com/laysent [@le0nik]: https://github.com/le0nik +[@leipert]: https://github.com/leipert [@lemonmade]: https://github.com/lemonmade [@lencioni]: https://github.com/lencioni [@leonardodino]: https://github.com/leonardodino diff --git a/package.json b/package.json index 3733e2d76f..74b7dbdb67 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.7.4", + "fast-deep-equal": "^3.1.3", "has": "^1.0.3", "is-core-module": "^2.11.0", "is-glob": "^4.0.3", diff --git a/src/ExportMap.js b/src/ExportMap.js index 7b8c883143..4bd581473b 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -13,6 +13,7 @@ import resolve from 'eslint-module-utils/resolve'; import isIgnored, { hasValidExtension } from 'eslint-module-utils/ignore'; import { hashObject } from 'eslint-module-utils/hash'; +import deepEqual from 'fast-deep-equal'; import * as unambiguous from 'eslint-module-utils/unambiguous'; import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader'; @@ -305,7 +306,7 @@ ExportMap.get = function (source, context) { ExportMap.for = function (context) { const { path } = context; - const cacheKey = hashObject(context).digest('hex'); + const cacheKey = context.cacheKey || hashObject(context).digest('hex'); let exportMap = exportCache.get(cacheKey); // return cached ignore @@ -559,7 +560,7 @@ ExportMap.parse = function (path, content, context) { if (tsConfigInfo.tsConfigPath !== undefined) { // Projects not using TypeScript won't have `typescript` installed. if (!ts) { ts = require('typescript'); } // eslint-disable-line import/no-extraneous-dependencies - + const configFile = ts.readConfigFile(tsConfigInfo.tsConfigPath, ts.sys.readFile); return ts.parseJsonConfigFileContent( configFile.config, @@ -781,12 +782,29 @@ export function recursivePatternCapture(pattern, callback) { } } +let parserOptionsHash = ''; +let prevParserOptions = null; +let settingsHash = ''; +let prevSettings = null; /** * don't hold full context object in memory, just grab what we need. + * also calculate a cacheKey, where parts of the cacheKey hash are memoized */ function childContext(path, context) { const { settings, parserOptions, parserPath } = context; + + if (!deepEqual(settings, prevSettings)) { + settingsHash = hashObject({ settings }).digest('hex'); + prevSettings = settings; + } + + if (!deepEqual(parserOptions, prevParserOptions)) { + parserOptionsHash = hashObject({ parserOptions }).digest('hex'); + prevParserOptions = parserOptions; + } + return { + cacheKey: String(parserPath) + parserOptionsHash + settingsHash + String(path), settings, parserOptions, parserPath,