@@ -35,22 +35,32 @@ function findNearestPackageJson(startDir) {
3535 return null ;
3636}
3737
38+ // Cache: `${pkg}\0${fileDir}` -> boolean. Survives the lifetime of the process
39+ // (one ESLint run), avoiding repeated FS reads for the same file directory.
40+ const packageInDepsCache = new Map ( ) ;
41+
3842function isPackageInProjectDeps ( moduleName , fileDir ) {
43+ const pkg = rootPackageName ( moduleName ) ;
44+ const cacheKey = `${ pkg } ::${ fileDir } ` ;
45+ if ( packageInDepsCache . has ( cacheKey ) ) {
46+ return packageInDepsCache . get ( cacheKey ) ;
47+ }
48+ let result = false ;
3949 try {
4050 const pkgPath = findNearestPackageJson ( fileDir ) ;
41- if ( ! pkgPath ) {
42- return false ;
51+ if ( pkgPath ) {
52+ const packageJson = JSON . parse ( fs . readFileSync ( pkgPath , 'utf8' ) ) ;
53+ result = Boolean (
54+ ( packageJson . dependencies && pkg in packageJson . dependencies ) ||
55+ ( packageJson . devDependencies && pkg in packageJson . devDependencies ) ||
56+ ( packageJson . peerDependencies && pkg in packageJson . peerDependencies )
57+ ) ;
4358 }
44- const packageJson = JSON . parse ( fs . readFileSync ( pkgPath , 'utf8' ) ) ;
45- const pkg = rootPackageName ( moduleName ) ;
46- return Boolean (
47- ( packageJson . dependencies && pkg in packageJson . dependencies ) ||
48- ( packageJson . devDependencies && pkg in packageJson . devDependencies ) ||
49- ( packageJson . peerDependencies && pkg in packageJson . peerDependencies )
50- ) ;
5159 } catch {
52- return false ;
60+ result = false ;
5361 }
62+ packageInDepsCache . set ( cacheKey , result ) ;
63+ return result ;
5464}
5565
5666/** @type {import('eslint').Rule.RuleModule } */
@@ -89,6 +99,9 @@ module.exports = {
8999
90100 create : ( context ) => {
91101 const sourceCode = context . sourceCode ;
102+ const fileDir = path . dirname (
103+ path . resolve ( context . getPhysicalFilename ?. ( ) ?? context . getFilename ( ) )
104+ ) ;
92105
93106 // takes a node with a `.path` property
94107 function checkInvokable ( node ) {
@@ -97,9 +110,6 @@ module.exports = {
97110 const matched = context . options [ 0 ] ?. invokables ?. [ node . path . head . name ] ;
98111 if ( matched ) {
99112 const [ name , moduleName ] = matched ;
100- const fileDir = path . dirname (
101- path . resolve ( context . getPhysicalFilename ?. ( ) ?? context . getFilename ( ) )
102- ) ;
103113 const canAutoFix =
104114 isBuiltinPackage ( moduleName ) ||
105115 isPackageInProjectDeps ( moduleName , fileDir ) ;
0 commit comments