Comparing version 0.3.1 to 0.4.0
224
index.js
var fs = require("fs"); | ||
var path = require("path"); | ||
var esprima = require("esprima"); | ||
var sets = require("simplesets"); | ||
var detective = require('detective'); | ||
var q = require('q'); | ||
var walkdir = require("walkdir"); | ||
var _ = require('lodash'); | ||
var minimatch = require('minimatch'); | ||
if (typeof String.prototype.startsWith !== "function") { | ||
String.prototype.startsWith = function (str) { | ||
return this.slice(0, str.length) === str; | ||
}; | ||
} | ||
function traverse(object, visitor) { | ||
var key, child; | ||
if (visitor.call(null, object) === false) { | ||
return; | ||
} | ||
for (key in object) { | ||
if (object.hasOwnProperty(key)) { | ||
child = object[key]; | ||
if (typeof child === "object" && child !== null) { | ||
traverse(child, visitor); | ||
} | ||
} | ||
} | ||
} | ||
function isRequire(node) { | ||
return node.callee.name === "require" || (node.callee.property && node.callee.property.name === "loadNpmTasks"); | ||
} | ||
function parse(filename) { | ||
function getModulesRequiredFromFilename(filename) { | ||
var content = fs.readFileSync(filename, "utf-8"); | ||
var lines; | ||
try { | ||
lines = content.split("\n"); | ||
if (lines[0][0] === "#") { | ||
lines.shift(); | ||
content = lines.join("\n"); | ||
return detective(content, { | ||
word: '', | ||
isRequire: function(node) { | ||
var callee = node.callee; | ||
return callee && | ||
( | ||
(node.type === 'CallExpression' && callee.type === 'Identifier' | ||
&& callee.name === 'require') | ||
|| | ||
(callee.property && callee.property.name === 'loadNpmTasks') | ||
); | ||
} | ||
return esprima.parse(content, {tolerant: true}); | ||
} catch (e) { | ||
return null; | ||
} | ||
}); | ||
} | ||
function checkFile(filename) { | ||
var used = new sets.Set(); | ||
var syntax = parse(filename); | ||
function checkDirectory(dir, ignoreDirs, deps, devDeps) { | ||
if (!syntax) { | ||
return new sets.Set(); | ||
} | ||
var deferred = q.defer(); | ||
var directoryPromises = []; | ||
var finder = walkdir(dir, { "no_recurse": true }); | ||
traverse(syntax, function (node) { | ||
var arg; | ||
if (node.type !== "CallExpression") { | ||
return; | ||
finder.on("directory", function (subdir) { | ||
if (ignoreDirs.contains(path.basename(subdir)) | ||
|| (deps.isEmpty() && devDeps.isEmpty())) { | ||
return; | ||
} | ||
if (node.arguments.length !== 1) { | ||
return; | ||
} | ||
if (!isRequire(node)) { | ||
return; | ||
} | ||
arg = node.arguments[0]; | ||
directoryPromises.push(checkDirectory(subdir, ignoreDirs, deps, devDeps)); | ||
}); | ||
if (arg.type === "Literal" && arg.value[0] !== ".") { | ||
used.add(arg.value); | ||
finder.on("file", function (filename) { | ||
if (path.extname(filename) === ".js") { | ||
var modulesRequired = getModulesRequiredFromFilename(filename); | ||
deps = deps.difference(modulesRequired); | ||
devDeps = devDeps.difference(modulesRequired); | ||
} | ||
}); | ||
return used; | ||
} | ||
finder.on("end", function () { | ||
deferred.resolve(q.allSettled(directoryPromises).then(function(directoryResults) { | ||
function hasBin(root, dependency) { | ||
try { | ||
var depPkg = require(path.join(root, "node_modules", dependency, "package.json")); | ||
return depPkg.bin !== undefined; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
_(directoryResults).each(function(result) { | ||
if (result.state === 'fulfilled') { | ||
deps = deps.intersection(result.value.dependencies); | ||
devDeps = devDeps.intersection(result.value.devDependencies); | ||
} | ||
}); | ||
function collectUnused(root, usedDependencies, definedDependencies) { | ||
var found = new sets.Set(); | ||
definedDependencies.array().forEach(function (definedDependency) { | ||
usedDependencies.array().forEach(function (usedDependency) { | ||
if (usedDependency === definedDependency || usedDependency.startsWith(definedDependency + "/") || hasBin(root, definedDependency)) { | ||
found.add(definedDependency); | ||
} | ||
}); | ||
return { | ||
dependencies: deps.valueOf(), | ||
devDependencies: devDeps.valueOf()}; | ||
})); | ||
}); | ||
return definedDependencies.difference(found).array(); | ||
return deferred.promise; | ||
} | ||
function check(options, root, files, cb) { | ||
var pkg = require(path.join(root, "/package.json")); | ||
var deps = new sets.Set(Object.keys(pkg.dependencies)); | ||
var usedDependencies = new sets.Set(); | ||
var ret = {}; | ||
function depCheck(rootDir, options, cb) { | ||
files.forEach(function (file) { | ||
usedDependencies = usedDependencies.union(checkFile(file)); | ||
}); | ||
var pkg = require(path.join(rootDir, 'package.json')); | ||
var deps = filterDependencies(pkg.dependencies); | ||
var devDeps = filterDependencies(options.withoutDev ? [] : pkg.devDependencies); | ||
var ignoreDirs = _([ | ||
'.git', | ||
'.svn', | ||
'.hg', | ||
'.idea', | ||
'node_modules' | ||
]) | ||
.concat(options.ignoreDirs) | ||
.flatten() | ||
.unique(); | ||
ret.dependencies = collectUnused(root, usedDependencies, deps); | ||
function isIgnored(dependency) { | ||
return _.any(options.ignoreMatches, function(match) { | ||
return minimatch(dependency, match); | ||
}); | ||
} | ||
if (!options.withoutDev) { | ||
ret.devDependencies = collectUnused(root, usedDependencies, new sets.Set(Object.keys(pkg.devDependencies || {}))); | ||
} else { | ||
ret.devDependencies = []; | ||
function hasBin(dependency) { | ||
try { | ||
var depPkg = require(path.join(rootDir, "node_modules", dependency, "package.json")); | ||
return _.has(depPkg, 'bin'); | ||
} catch (e) {} | ||
} | ||
cb(ret); | ||
} | ||
function filterDependencies(dependencies) { | ||
return _(dependencies) | ||
.keys() | ||
.reject(hasBin) | ||
.reject(isIgnored) | ||
} | ||
function collectSubdirectories(directories, cb) { | ||
var n = 0; | ||
var files = []; | ||
directories.forEach(function (dir) { | ||
var finder = walkdir(dir); | ||
n++; | ||
finder.on("file", function (file) { | ||
files.push(file); | ||
}); | ||
finder.on("end", function () { | ||
n--; | ||
if (n === 0) { | ||
cb(files); | ||
} | ||
}); | ||
}); | ||
return checkDirectory(rootDir, ignoreDirs, deps, devDeps) | ||
.then(cb) | ||
.done(); | ||
} | ||
module.exports = function checkDirectory(dir, options, cb) { | ||
var files = []; | ||
var directories = []; | ||
var finder = walkdir(dir, { "no_recurse": true }); | ||
finder.on("directory", function (dir) { | ||
if (path.basename(dir) === "node_modules") { | ||
return; | ||
} | ||
directories.push(dir); | ||
}); | ||
finder.on("file", function (file) { | ||
if (path.extname(file) !== ".js") { | ||
return; | ||
} | ||
files.push(file); | ||
}); | ||
finder.on("end", function () { | ||
if (directories.length === 0) { | ||
check(options, dir, files, cb); | ||
} else { | ||
collectSubdirectories(directories, function (subFiles) { | ||
check(options, dir, files.concat(subFiles), cb); | ||
}); | ||
} | ||
}); | ||
}; | ||
module.exports = depCheck; |
{ | ||
"name": "depcheck", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "Check dependencies in your node module", | ||
@@ -20,11 +20,14 @@ "main": "index", | ||
"dependencies": { | ||
"optimist": "~0.6.0", | ||
"walkdir": "0.0.7", | ||
"detective": "^3.1.0", | ||
"esprima": "~1.0.4", | ||
"simplesets": "~1.2.0" | ||
"lodash": "^2.4.1", | ||
"minimatch": "^0.3.0", | ||
"q": "^1.0.1", | ||
"walkdir": "0.0.7" | ||
}, | ||
"devDependencies": { | ||
"should": "~1.2.2", | ||
"mocha": "~1.8.1" | ||
"mocha": "~1.8.1", | ||
"optimist": "~0.6.0" | ||
} | ||
} |
@@ -35,3 +35,11 @@ # depcheck [![build status](https://secure.travis-ci.org/rumpl/depcheck.png)](http://travis-ci.org/rumpl/depcheck) | ||
var options = { | ||
"withoutDev": false // Check against devDependencies too | ||
"withoutDev": false, // Check against devDependencies too | ||
"ignoreDirs": [ // Pathnames to ignore | ||
"sandbox", | ||
"dist", | ||
"bower_components" | ||
], | ||
"ignoreMatches": [ // Ignore dependencies that match these minimatch patterns | ||
"grunt-*" | ||
] | ||
}; | ||
@@ -38,0 +46,0 @@ var root = path.resolve("some path"); |
@@ -50,2 +50,21 @@ var assert = require("should"); | ||
}); | ||
it("should should ignore ignoreDirs", function testUnused(done) { | ||
var absolutePath = path.resolve("test/fake_modules/bad"); | ||
depcheck(absolutePath, { "ignoreDirs": ['sandbox'] }, function checked(unused) { | ||
assert.equal(unused.dependencies.length, 1); | ||
done(); | ||
}); | ||
}); | ||
it("should should ignore ignoreMatches", function testUnused(done) { | ||
var absolutePath = path.resolve("test/fake_modules/bad"); | ||
depcheck(absolutePath, { "ignoreMatches": ['o*'] }, function checked(unused) { | ||
assert.equal(unused.dependencies.length, 0); | ||
done(); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
35
66
32025
6
3
151
1
+ Addeddetective@^3.1.0
+ Addedlodash@^2.4.1
+ Addedminimatch@^0.3.0
+ Addedq@^1.0.1
+ Addedamdefine@1.0.1(transitive)
+ Addeddetective@3.1.0(transitive)
+ Addedescodegen@1.1.0(transitive)
+ Addedesprima-fb@3001.1.0-dev-harmony-fb(transitive)
+ Addedestraverse@1.5.1(transitive)
+ Addedesutils@1.0.0(transitive)
+ Addedlodash@2.4.2(transitive)
+ Addedlru-cache@2.7.3(transitive)
+ Addedminimatch@0.3.0(transitive)
+ Addedq@1.5.1(transitive)
+ Addedsigmund@1.0.1(transitive)
+ Addedsource-map@0.1.43(transitive)
- Removedoptimist@~0.6.0
- Removedsimplesets@~1.2.0
- Removedminimist@0.0.10(transitive)
- Removedoptimist@0.6.1(transitive)
- Removedsimplesets@1.2.0(transitive)
- Removedwordwrap@0.0.3(transitive)