dependency-tree
Advanced tools
Comparing version 5.3.0 to 5.4.0
181
index.js
@@ -7,2 +7,28 @@ var precinct = require('precinct'); | ||
function Config(options) { | ||
this.filename = options.filename; | ||
this.directory = options.directory || options.root; | ||
this.visited = options.visited || {}; | ||
this.isListForm = options.isListForm; | ||
this.requireConfig = options.config || options.requireConfig; | ||
this.webpackConfig = options.webpackConfig; | ||
this.filter = options.filter; | ||
if (!this.filename) { throw new Error('filename not given'); } | ||
if (!this.directory) { throw new Error('directory not given'); } | ||
if (this.filter && typeof this.filter !== 'function') { throw new Error('filter must be a function'); } | ||
debug('given filename: ' + this.filename); | ||
this.filename = path.resolve(process.cwd(), this.filename); | ||
debug('resolved filename: ' + this.filename); | ||
debug('visited: ', this.visited); | ||
} | ||
Config.prototype.clone = function() { | ||
return new Config(this); | ||
}; | ||
/** | ||
@@ -14,52 +40,28 @@ * Recursively find all dependencies (avoiding circular) traversing the entire dependency tree | ||
* @param {String} options.filename - The path of the module whose tree to traverse | ||
* @param {String} options.root - The directory containing all JS files | ||
* @param {String} options.directory - The directory containing all JS files | ||
* @param {String} [options.requireConfig] - The path to a requirejs config | ||
* @param {String} [options.webpackConfig] - The path to a webpack config | ||
* @param {Object} [options.visited] - Cache of visited, absolutely pathed files that should not be reprocessed. | ||
* Format is a filename -> tree as list lookup table | ||
* @param {Boolean} [options.isListForm=false] | ||
* @return {Object} | ||
*/ | ||
module.exports = function(options) { | ||
var filename = options.filename; | ||
var root = options.root; | ||
var visited = options.visited; | ||
var isListForm = options.isListForm; | ||
var requireConfig = options.config; | ||
var webpackConfig = options.webpackConfig; | ||
var config = new Config(options); | ||
if (!filename) { throw new Error('filename not given'); } | ||
if (!root) { throw new Error('root directory not given'); } | ||
debug('given filename: ' + filename); | ||
filename = path.resolve(process.cwd(), filename); | ||
debug('resolved filename: ' + filename); | ||
visited = visited || {}; | ||
debug('visited: ', visited); | ||
if (!fs.existsSync(filename)) { | ||
debug('file ' + filename + ' does not exist'); | ||
return isListForm ? [] : {}; | ||
if (!fs.existsSync(config.filename)) { | ||
debug('file ' + config.filename + ' does not exist'); | ||
return config.isListForm ? [] : {}; | ||
} | ||
if (visited[filename]) { | ||
debug('already visited: ' + filename); | ||
return visited[filename]; | ||
if (config.visited[config.filename]) { | ||
debug('already visited: ' + config.filename); | ||
return config.visited[config.filename]; | ||
} | ||
var tree; | ||
var results = traverse({ | ||
filename: filename, | ||
root: root, | ||
visited: visited, | ||
config: requireConfig, | ||
webpackConfig: webpackConfig, | ||
isListForm: isListForm | ||
}); | ||
var results = traverse(config); | ||
debug('traversal complete', results); | ||
if (isListForm) { | ||
var tree; | ||
if (config.isListForm) { | ||
debug('list form of results requested'); | ||
@@ -74,3 +76,3 @@ | ||
tree = {}; | ||
tree[filename] = results; | ||
tree[config.filename] = results; | ||
} | ||
@@ -95,12 +97,5 @@ | ||
module.exports.toList = function(options) { | ||
options.isListForm = true; | ||
// Can't pass args since visited is optional and positions will be off | ||
return module.exports({ | ||
filename: options.filename, | ||
root: options.root, | ||
visited: options.visited, | ||
config: options.config, | ||
webpackConfig: options.webpackConfig, | ||
isListForm: true | ||
}); | ||
return module.exports(options); | ||
}; | ||
@@ -117,46 +112,29 @@ | ||
module.exports._getDependencies = function(filename) { | ||
var dependencies; | ||
try { | ||
dependencies = precinct.paperwork(filename, { | ||
return precinct.paperwork(filename, { | ||
includeCore: false | ||
}); | ||
} catch (e) { | ||
dependencies = []; | ||
debug('error getting dependencies: ' + e.message); | ||
debug(e.stack); | ||
return []; | ||
} | ||
return dependencies; | ||
}; | ||
/** | ||
* @param {Object} options | ||
* @param {String} options.filename | ||
* @param {String} options.root | ||
* @param {Object} options.visited | ||
* @param {String} options.config | ||
* @param {String} options.webpackConfig | ||
* @param {Boolean} [options.isListForm=false] - Whether or not to collect the tree in a list form | ||
* @param {Boolean} [options.config] - Path to a requirejs config for AMD apps | ||
* @param {Config} config | ||
* @return {Object|String[]} | ||
*/ | ||
function traverse(options) { | ||
var filename = options.filename; | ||
var root = options.root; | ||
var visited = options.visited; | ||
var isListForm = options.isListForm; | ||
var config = options.config; | ||
var webpackConfig = options.webpackConfig; | ||
function traverse(config) { | ||
var subTree = config.isListForm ? [] : {}; | ||
isListForm = !!isListForm; | ||
debug('traversing ' + config.filename); | ||
var subTree = isListForm ? [] : {}; | ||
debug('traversing ' + filename); | ||
if (visited[filename]) { | ||
debug('already visited'); | ||
return visited[filename]; | ||
if (config.visited[config.filename]) { | ||
debug('already visited ' + config.filename); | ||
return config.visited[config.filename]; | ||
} | ||
var dependencies = module.exports._getDependencies(filename); | ||
var dependencies = module.exports._getDependencies(config.filename); | ||
@@ -166,14 +144,11 @@ debug('extracted ' + dependencies.length + ' dependencies: ', dependencies); | ||
if (dependencies.length) { | ||
dependencies = dependencies | ||
.map(function(dep) { | ||
var options = { | ||
dependencies = dependencies.map(function(dep) { | ||
var result = cabinet({ | ||
partial: dep, | ||
filename: filename, | ||
directory: root, | ||
config: config, | ||
webpackConfig: webpackConfig | ||
}; | ||
filename: config.filename, | ||
directory: config.directory, | ||
config: config.requireConfig, | ||
webpackConfig: config.webpackConfig | ||
}); | ||
var result = cabinet(options); | ||
debug('cabinet result ' + result); | ||
@@ -196,29 +171,27 @@ | ||
// so that any dependent dependencies exit | ||
visited[filename] = isListForm ? [] : {}; | ||
config.visited[config.filename] = config.isListForm ? [] : {}; | ||
if (config.filter) { | ||
dependencies = dependencies.filter(config.filter); | ||
} | ||
dependencies.forEach(function(d) { | ||
var options = { | ||
filename: d, | ||
root: root, | ||
visited: visited, | ||
config: config, | ||
webpackConfig: webpackConfig | ||
}; | ||
var localConfig = config.clone(); | ||
localConfig.filename = d; | ||
if (isListForm) { | ||
options.isListForm = isListForm; | ||
subTree = subTree.concat(traverse(options)); | ||
if (localConfig.isListForm) { | ||
subTree = subTree.concat(traverse(localConfig)); | ||
} else { | ||
subTree[d] = traverse(options); | ||
subTree[d] = traverse(localConfig); | ||
} | ||
}); | ||
if (isListForm) { | ||
if (config.isListForm) { | ||
// Prevents redundancy about each memoized step | ||
subTree = removeDups(subTree); | ||
subTree.push(filename); | ||
visited[filename] = visited[filename].concat(subTree); | ||
subTree.push(config.filename); | ||
config.visited[config.filename] = config.visited[config.filename].concat(subTree); | ||
} else { | ||
visited[filename] = subTree; | ||
config.visited[config.filename] = subTree; | ||
} | ||
@@ -225,0 +198,0 @@ |
{ | ||
"name": "dependency-tree", | ||
"version": "5.3.0", | ||
"version": "5.4.0", | ||
"description": "Get the dependency tree of a module (as a list)", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -15,5 +15,6 @@ ### dependency-tree [![npm](http://img.shields.io/npm/v/dependency-tree.svg)](https://npmjs.org/package/dependency-tree) [![npm](http://img.shields.io/npm/dm/dependency-tree.svg)](https://npmjs.org/package/dependency-tree) | ||
filename: 'path/to/a/file', | ||
root: 'path/to/all/files', | ||
config: 'path/to/requirejs/config', // optional | ||
webpackConfig: 'path/to/webpack/config' // optional | ||
directory: 'path/to/all/files', | ||
requireConfig: 'path/to/requirejs/config', // optional | ||
webpackConfig: 'path/to/webpack/config', // optional | ||
filter: path => path.indexOf('node_modules') === -1 // optional | ||
}); | ||
@@ -25,3 +26,3 @@ | ||
filename: 'path/to/a/file', | ||
root: 'path/to/all/files' | ||
directory: 'path/to/all/files' | ||
}); | ||
@@ -61,5 +62,8 @@ ``` | ||
* `config`: path to a requirejs config for AMD modules (allows for the result of aliased module paths) | ||
* `requireConfig`: path to a requirejs config for AMD modules (allows for the result of aliased module paths) | ||
* `webpackConfig`: path to a webpack config for aliased modules | ||
* `visited`: object used for avoiding redundant subtree generations via memoization. | ||
* `filter`: a function used to determine if a module (and its subtree) should be included in the dependency tree | ||
- The function should accept an absolute filepath and return a boolean | ||
- If the filter returns true, the module is included in the resulting tree | ||
@@ -74,2 +78,2 @@ **Shell version** (assuming `npm install -g dependency-tree`): | ||
* You can alternatively print out the list form one element per line using the `--list-form` option. | ||
* You can alternatively print out the list form one element per line using the `--list-form` option. |
150
test/test.js
@@ -100,7 +100,6 @@ import dependencyTree from '../'; | ||
const root = __dirname + '/extended'; | ||
const filename = root + '/a.js'; | ||
const directory = __dirname + '/extended'; | ||
const filename = directory + '/a.js'; | ||
const tree = dependencyTree({filename, root}); | ||
const tree = dependencyTree({filename, directory}); | ||
assert(tree[filename] instanceof Object); | ||
@@ -112,4 +111,4 @@ | ||
const bTree = subTree[root + '/b.js']; | ||
const cTree = subTree[root + '/c.js']; | ||
const bTree = subTree[directory + '/b.js']; | ||
const cTree = subTree[directory + '/c.js']; | ||
// d and e | ||
@@ -128,6 +127,6 @@ assert.equal(Object.keys(bTree).length, 2); | ||
const root = __dirname + '/onlyRealDeps'; | ||
const filename = root + '/a.js'; | ||
const directory = __dirname + '/onlyRealDeps'; | ||
const filename = directory + '/a.js'; | ||
const tree = dependencyTree({filename, root}); | ||
const tree = dependencyTree({filename, directory}); | ||
const subTree = tree[filename]; | ||
@@ -146,8 +145,8 @@ | ||
const root = __dirname + '/cyclic'; | ||
const filename = root + '/a.js'; | ||
const directory = __dirname + '/cyclic'; | ||
const filename = directory + '/a.js'; | ||
const spy = sinon.spy(dependencyTree, '_getDependencies'); | ||
const tree = dependencyTree({filename, root}); | ||
const tree = dependencyTree({filename, directory}); | ||
@@ -161,6 +160,6 @@ assert(spy.callCount === 2); | ||
it('excludes Nodejs core modules by default', function() { | ||
const root = __dirname + '/example/commonjs'; | ||
const filename = root + '/b.js'; | ||
const directory = __dirname + '/example/commonjs'; | ||
const filename = directory + '/b.js'; | ||
const tree = dependencyTree({filename, root}); | ||
const tree = dependencyTree({filename, directory}); | ||
assert(Object.keys(tree[filename]).length === 0); | ||
@@ -171,6 +170,6 @@ assert(Object.keys(tree)[0].indexOf('b.js') !== -1); | ||
it('traverses installed 3rd party node modules', function() { | ||
const root = __dirname + '/example/onlyRealDeps'; | ||
const filename = root + '/a.js'; | ||
const directory = __dirname + '/example/onlyRealDeps'; | ||
const filename = directory + '/a.js'; | ||
const tree = dependencyTree({filename, root}); | ||
const tree = dependencyTree({filename, directory}); | ||
const subTree = tree[filename]; | ||
@@ -182,6 +181,6 @@ | ||
it('returns a list of absolutely pathed files', function() { | ||
const root = __dirname + '/example/commonjs'; | ||
const filename = root + '/b.js'; | ||
const directory = __dirname + '/example/commonjs'; | ||
const filename = directory + '/b.js'; | ||
const tree = dependencyTree({filename, root}); | ||
const tree = dependencyTree({filename, directory}); | ||
@@ -206,3 +205,3 @@ for (let node in tree.nodes) { | ||
filename: 'root/a.js', | ||
root: 'root' | ||
directory: 'root' | ||
}); | ||
@@ -214,5 +213,12 @@ | ||
describe('throws', function() { | ||
beforeEach(function() { | ||
this._directory = __dirname + '/example/commonjs'; | ||
}); | ||
it('throws if the filename is missing', function() { | ||
assert.throws(function() { | ||
dependencyTree({filename: undefined, root}); | ||
dependencyTree({ | ||
filename: undefined, | ||
directory: this._directory | ||
}); | ||
}); | ||
@@ -226,8 +232,40 @@ }); | ||
}); | ||
it('throws if a supplied filter is not a function', function() { | ||
assert.throws(function() { | ||
const directory = __dirname + '/example/onlyRealDeps'; | ||
const filename = directory + '/a.js'; | ||
const tree = dependencyTree({ | ||
filename, | ||
directory, | ||
filter: 'foobar' | ||
}); | ||
}); | ||
}); | ||
it('does not throw on the legacy `root` option', function() { | ||
assert.doesNotThrow(function() { | ||
const directory = __dirname + '/example/onlyRealDeps'; | ||
const filename = directory + '/a.js'; | ||
const tree = dependencyTree({ | ||
filename, | ||
root: directory | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('on file error', function() { | ||
beforeEach(function() { | ||
this._directory = __dirname + '/example/commonjs'; | ||
}); | ||
it('does not throw', function() { | ||
assert.doesNotThrow(function() { | ||
dependencyTree({filename: 'foo', root}); | ||
assert.doesNotThrow(() => { | ||
dependencyTree({ | ||
filename: 'foo', | ||
directory: this._directory | ||
}); | ||
}); | ||
@@ -237,3 +275,3 @@ }); | ||
it('returns no dependencies', function() { | ||
const tree = dependencyTree({filename: 'foo', root}); | ||
const tree = dependencyTree({filename: 'foo', directory: this._directory}); | ||
assert(!tree.length); | ||
@@ -254,3 +292,3 @@ }); | ||
const filename = __dirname + '/example/amd/a.js'; | ||
const root = __dirname + '/example/amd'; | ||
const directory = __dirname + '/example/amd'; | ||
const cache = {}; | ||
@@ -265,3 +303,3 @@ | ||
filename, | ||
root, | ||
directory, | ||
visited: cache | ||
@@ -276,3 +314,3 @@ }); | ||
const filename = __dirname + '/example/amd/a.js'; | ||
const root = __dirname + '/example/amd'; | ||
const directory = __dirname + '/example/amd'; | ||
@@ -286,3 +324,3 @@ const cache = { | ||
filename, | ||
root, | ||
directory, | ||
visited: cache | ||
@@ -316,3 +354,3 @@ }); | ||
filename, | ||
root: this._directory | ||
directory: this._directory | ||
}); | ||
@@ -327,3 +365,3 @@ | ||
filename, | ||
root: this._directory | ||
directory: this._directory | ||
}); | ||
@@ -355,8 +393,8 @@ | ||
it('returns a post-order list form of the dependency tree', function() { | ||
const root = __dirname + '/example/' + format; | ||
const filename = root + '/a' + ext; | ||
const directory = __dirname + '/example/' + format; | ||
const filename = directory + '/a' + ext; | ||
const list = dependencyTree.toList({ | ||
filename, | ||
root | ||
directory | ||
}); | ||
@@ -374,8 +412,8 @@ | ||
const root = __dirname + '/imaginary'; | ||
const filename = root + '/notafile.js'; | ||
const directory = __dirname + '/imaginary'; | ||
const filename = directory + '/notafile.js'; | ||
const list = dependencyTree.toList({ | ||
filename, | ||
root | ||
directory | ||
}); | ||
@@ -388,12 +426,12 @@ | ||
it('orders the visited files by last visited', function() { | ||
const root = __dirname + '/example/amd'; | ||
const filename = root + '/a.js'; | ||
const directory = __dirname + '/example/amd'; | ||
const filename = directory + '/a.js'; | ||
const list = dependencyTree.toList({ | ||
filename, | ||
root | ||
directory | ||
}); | ||
assert(list.length === 3); | ||
assert(list[0] === root + '/c.js'); | ||
assert(list[1] === root + '/b.js'); | ||
assert(list[0] === directory + '/c.js'); | ||
assert(list[1] === directory + '/b.js'); | ||
assert(list[list.length - 1] === filename); | ||
@@ -447,3 +485,3 @@ }); | ||
filename: `${__dirname}/example/webpack/${name}.js`, | ||
root: this._root, | ||
directory: this._root, | ||
webpackConfig: this._webpackConfig | ||
@@ -499,3 +537,3 @@ }); | ||
filename: 'root/a.js', | ||
root: 'root', | ||
directory: 'root', | ||
config: 'root/require.config.js' | ||
@@ -512,3 +550,3 @@ }); | ||
filename: 'root/b.js', | ||
root: 'root', | ||
directory: 'root', | ||
config: 'root/require.config.js' | ||
@@ -522,2 +560,22 @@ }); | ||
}); | ||
describe('when a filter function is supplied', function() { | ||
it('uses the filter to determine if a file should be included in the results', function() { | ||
const directory = __dirname + '/example/onlyRealDeps'; | ||
const filename = directory + '/a.js'; | ||
const tree = dependencyTree({ | ||
filename, | ||
directory, | ||
// Skip all 3rd party deps | ||
filter: (path) => path.indexOf('node_modules') === -1 | ||
}); | ||
const subTree = tree[filename]; | ||
assert.ok(Object.keys(tree).length); | ||
const has3rdPartyDep = Object.keys(subTree).some(dep => dep === require.resolve('debug')); | ||
assert.ok(!has3rdPartyDep); | ||
}); | ||
}); | ||
}); |
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
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
26457
683
76