eslint-plugin-implicit-dependencies
Advanced tools
Comparing version 1.0.4 to 1.1.0
{ | ||
"name": "eslint-plugin-implicit-dependencies", | ||
"version": "1.0.4", | ||
"version": "1.1.0", | ||
"description": "eslint plugin to detect implicit dependencies", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -5,3 +5,3 @@ # eslint-plugin-implicit-dependencies | ||
Detects when a module has been required that is not listed as a dependency in the project's package.json. | ||
Detects when a module has been 'require'd or 'import'ed that is not listed as a dependency in the project's package.json. | ||
@@ -8,0 +8,0 @@ This helps prevent accidentally depending on a module that is present in node_modules as a result of being installed further down your dependency tree, but is not listed as an explicit dependency of your project. |
@@ -31,48 +31,54 @@ 'use strict'; | ||
create: (context) => { | ||
// find the nearest package.json | ||
const dir = path.dirname(context.getFilename()); | ||
const jsonPath = path.join(findup.sync(dir, 'package.json'), 'package.json'); | ||
const pkg = require(jsonPath); | ||
const checkModuleName = (name, node) => { | ||
let moduleName; | ||
// ignore dynamic arguments | ||
if (!name || typeof name !== 'string') { | ||
return; | ||
} | ||
if (name[0] !== '.' && name[0] !== '/') { | ||
// if module is a node core module then skip | ||
if (builtin[name]) { | ||
return; | ||
} | ||
// parse module name from scope packages and deep requires | ||
if (name[0] === '@') { | ||
moduleName = name.split('/').slice(0, 2).join('/'); | ||
} else { | ||
moduleName = name.split('/')[0]; | ||
} | ||
// check dependencies | ||
const opts = context.options[0] || {}; | ||
if (pkg.dependencies && pkg.dependencies[moduleName]) { | ||
return; | ||
} else if (pkg.optionalDependencies && pkg.optionalDependencies[moduleName] && opts.optional) { | ||
return; | ||
} else if (pkg.peerDependencies && pkg.peerDependencies[moduleName] && opts.peer) { | ||
return; | ||
} else if (pkg.devDependencies && pkg.devDependencies[moduleName] && opts.dev) { | ||
return; | ||
} else { | ||
context.report({ | ||
node, | ||
message: `Module "${moduleName}" is not listed as a dependency in package.json` | ||
}); | ||
} | ||
} | ||
}; | ||
return { | ||
'CallExpression:exit': (node) => { | ||
if (node.callee.name === 'require') { | ||
let moduleName; | ||
const name = node.arguments[0].value; | ||
// ignore dynamic arguments | ||
if (!name || typeof name !== 'string') { | ||
return; | ||
} | ||
if (name[0] !== '.' && name[0] !== '/') { | ||
// if module is a node core module then skip | ||
if (builtin[name]) { | ||
return; | ||
} | ||
// parse module name from scope packages and deep requires | ||
if (name[0] === '@') { | ||
moduleName = name.split('/').slice(0, 2).join('/'); | ||
} else { | ||
moduleName = name.split('/')[0]; | ||
} | ||
// find the nearest package.json | ||
const dir = path.dirname(context.getFilename()); | ||
const jsonPath = path.join(findup.sync(dir, 'package.json'), 'package.json'); | ||
const pkg = require(jsonPath); | ||
// check dependencies | ||
const opts = context.options[0] || {}; | ||
if (pkg.dependencies && pkg.dependencies[moduleName]) { | ||
return; | ||
} else if (pkg.optionalDependencies && pkg.optionalDependencies[moduleName] && opts.optional) { | ||
return; | ||
} else if (pkg.peerDependencies && pkg.peerDependencies[moduleName] && opts.peer) { | ||
return; | ||
} else if (pkg.devDependencies && pkg.devDependencies[moduleName] && opts.dev) { | ||
return; | ||
} else { | ||
context.report({ | ||
node, | ||
message: `Module "${moduleName}" is not listed as a dependency in package.json` | ||
}); | ||
} | ||
} | ||
checkModuleName(name, node); | ||
} | ||
}, | ||
'ImportDeclaration:exit': (node) => { | ||
const name = node.source.value; | ||
checkModuleName(name, node); | ||
} | ||
@@ -79,0 +85,0 @@ }; |
87
5652
5