Skip to content

Commit 3eec111

Browse files
Cache package.json FS lookups in isPackageInProjectDeps
Co-authored-by: NullVoxPopuli <[email protected]>
1 parent 1985afa commit 3eec111

1 file changed

Lines changed: 23 additions & 13 deletions

File tree

lib/rules/template-missing-invokable.js

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
3842
function 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

Comments
 (0)