Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dependency-tree

Package Overview
Dependencies
Maintainers
1
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dependency-tree - npm Package Compare versions

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.

@@ -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);
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc