diff --git a/CHANGELOG.md b/CHANGELOG.md index e349c4a0b..8a04c80a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Performance] `ExportMap`: add caching after parsing for an ambiguous module ([#2531], thanks [@stenin-nikita]) - [Docs] [`no-useless-path-segments`]: fix paths ([#2424], thanks [@s-h-a-d-o-w]) - [Tests] [`no-cycle`]: add passing test cases ([#2438], thanks [@georeith]) +- [Refactor] [`no-extraneous-dependencies`] improve performance using cache ([#2374], thanks [@meowtec]) ## [2.26.0] - 2022-04-05 @@ -1029,6 +1030,7 @@ for info on changes for earlier releases. [#2387]: https://github.com/import-js/eslint-plugin-import/pull/2387 [#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381 [#2378]: https://github.com/import-js/eslint-plugin-import/pull/2378 +[#2374]: https://github.com/import-js/eslint-plugin-import/pull/2374 [#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 [#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 [#2332]: https://github.com/import-js/eslint-plugin-import/pull/2332 @@ -1663,6 +1665,7 @@ for info on changes for earlier releases. [@Maxim-Mazurok]: https://github.com/Maxim-Mazurok [@maxkomarychev]: https://github.com/maxkomarychev [@maxmalov]: https://github.com/maxmalov +[@meowtec]: https://github.com/meowtec [@mgwalker]: https://github.com/mgwalker [@mhmadhamster]: https://github.com/MhMadHamster [@MikeyBeLike]: https://github.com/MikeyBeLike diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index b54ee28bb..4fc1a01d4 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -1,6 +1,6 @@ import path from 'path'; import fs from 'fs'; -import readPkgUp from 'eslint-module-utils/readPkgUp'; +import pkgUp from 'eslint-module-utils/pkgUp'; import minimatch from 'minimatch'; import resolve from 'eslint-module-utils/resolve'; import moduleVisitor from 'eslint-module-utils/moduleVisitor'; @@ -18,6 +18,16 @@ function arrayOrKeys(arrayOrObject) { return Array.isArray(arrayOrObject) ? arrayOrObject : Object.keys(arrayOrObject); } +function readJSON(jsonPath, throwException) { + try { + return JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + } catch (err) { + if (throwException) { + throw err; + } + } +} + function extractDepFields(pkg) { return { dependencies: pkg.dependencies || {}, @@ -30,6 +40,15 @@ function extractDepFields(pkg) { }; } +function getPackageDepFields(packageJsonPath, throwAtRead) { + if (!depFieldCache.has(packageJsonPath)) { + const depFields = extractDepFields(readJSON(packageJsonPath, throwAtRead)); + depFieldCache.set(packageJsonPath, depFields); + } + + return depFieldCache.get(packageJsonPath); +} + function getDependencies(context, packageDir) { let paths = []; try { @@ -53,24 +72,21 @@ function getDependencies(context, packageDir) { // use rule config to find package.json paths.forEach(dir => { const packageJsonPath = path.join(dir, 'package.json'); - if (!depFieldCache.has(packageJsonPath)) { - const depFields = extractDepFields( - JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')), - ); - depFieldCache.set(packageJsonPath, depFields); - } - const _packageContent = depFieldCache.get(packageJsonPath); + const _packageContent = getPackageDepFields(packageJsonPath, true); Object.keys(packageContent).forEach(depsKey => Object.assign(packageContent[depsKey], _packageContent[depsKey]), ); }); } else { + const packageJsonPath = pkgUp({ + cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), + normalize: false, + }); + // use closest package.json Object.assign( packageContent, - extractDepFields( - readPkgUp({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), normalize: false }).pkg, - ), + getPackageDepFields(packageJsonPath, false), ); } @@ -267,4 +283,8 @@ module.exports = { reportIfMissing(context, deps, depsOptions, node, source.value); }, { commonjs: true }); }, + + 'Program:exit': () => { + depFieldCache.clear(); + }, };