Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
### 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)

> Get the dependency tree of a module (as a list)
> Get the dependency tree of a module

`npm install dependency-tree`

### Usage

```js
var getTreeAsList = require('dependency-tree');
var dependencyTree = require('dependency-tree');

// Returns a list of filepaths for all visited dependencies
var tree = getTreeAsList('path/to/a/file', 'path/to/all/js/files');
// Returns a dependency tree for the given file
var tree = dependencyTree('path/to/a/file', 'path/to/all/js/files');

// Returns a pre-order traversal of the tree with duplicate sub-trees pruned.
var preOrderList = dependencyTree.traversePreOrder(tree);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had no real intention behind doing a pre-order traversal. Perhaps all of this could be masked behind dependencyTree.asList(tree) that returns a postOrder list.


// Returns a post-order traversal of the tree with duplicate sub-trees pruned.
// This is useful for bundling source files, because the list gives the
// concatenation order.
var postOrderList = dependencyTree.traversePostOrder(tree);
```

Returns the entire dependency tree as a **flat** list of filepaths for a given module.
Basically, all files visited during traversal of the dependency-tree are returned.
Returns the entire dependency tree as an object containing the absolute path of the entry file (tree.root) and a mapping from each processed file to its direct dependencies (tree.nodes). For example, the following yields the direct dependencies (child, but not grand-child dependencies) of the root file:

```js
var dependencyTree = require('dependency-tree');

var tree = dependencyTree('path/to/a/file', 'path/to/all/js/files');

var rootDependencies = tree.nodes[tree.root];
```

* All core Node modules (assert, path, fs, etc) are removed from the dependency list by default
* Works for AMD, CommonJS, ES6 modules and SASS files.
Expand All @@ -31,10 +46,17 @@ used for avoiding redundant subtree generations.
tree filename root
```

Prints
Prints the pre-order and post-order traversals of the dependency tree

```
Pre-Order:
/a.js
/b.js
/c.js

Post-Order:
/b.js
/c.js
/a.js
```

12 changes: 9 additions & 3 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

'use strict';

var treeAsList = require('../');
var dependencyTree = require('../');
var filename = process.argv[2];
var root = process.argv[3];

var tree = treeAsList(filename, root);
var tree = dependencyTree(filename, root);

tree.forEach(function(node) {
console.log('Pre-Order:');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I often use the CLI's output in editor plugins like https://github.com/mrjoelkemp/sublime-dependents where the cleaner the output, the easier the integration.

If distinguishing between traversal methods was necessary, I'd opt for a cli flag traversalMethod with potential values: preOrder or postOrder to distinguish which method to call and avoid console.log('\nPost-Order:');.

dependencyTree.traversePreOrder(tree).forEach(function(node) {
console.log(node);
});

console.log('\nPost-Order:');
dependencyTree.traversePostOrder(tree).forEach(function(node) {
console.log(node);
});
91 changes: 87 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ var path = require('path');
var fs = require('fs');
var resolveDependencyPath = require('resolve-dependency-path');

var PRE_ORDER = 1;
var POST_ORDER = 2;

/**
* Recursively find all dependencies (avoiding circular) traversing the entire dependency tree
* and returns a flat list of all unique, visited nodes
Expand All @@ -30,10 +33,89 @@ module.exports = function(filename, root, visited) {

var results = traverse(filename, root, visited);
results = removeDups(results);
var tree = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the object form, I've always envisioned a structure like:

{
  'a.js': {
    'b.js': {
      'e.js': {}
    },
    'c.js': {}
  }
}

Instead of having artificial keys like root and nodes. It's also better for lookups when you have the filepath as the index since it's likely that I'll want to find subtrees as quickly as possible.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, your way is better for lookups since the nodes object is flat. The way above is what I had in mind for dependents/Dependents#55 where the dependency tree is displayed as a JSON file.

root: filename,
nodes: visited
};
return tree;
};

/**
* Executes a pre-order depth first search on the dependency tree and returns a
* list of absolute file paths. The order of files in the list will be the order
* in which the module processed files as it built the tree. The root (entry
* point) file will be first, followed by the root file's first dependency and
* the first dependency's dependencies. The list will not contain duplicates.
*
* @param {Object} tree - Tree object produced by this module.
*/
module.exports.traversePreOrder = function(tree) {
if (!tree) { throw new Error('tree not given'); }
if (!tree.root) { throw new Error('Tree object is missing root'); }
if (!tree.nodes) { throw new Error('Tree object is missing nodes'); }

return traverseTree(tree.root, tree.nodes, {}, PRE_ORDER);
};

return results;
/**
* Executes a post-order depth first search on the dependency tree and returns a
* list of absolute file paths. The order of files in the list will be the
* proper concatenation order for bundling. In other words, for any file in the
* list, all of that file's dependencies (direct or indirect) will appear at
* lower indeces in the list. The root (entry point) file will therefore appear
* last. The list will not contain duplicates.
*
* @param {Object} tree - Tree object produced by this module.
*/
module.exports.traversePostOrder = function(tree) {
if (!tree) { throw new Error('tree not given'); }
if (!tree.root) { throw new Error('Tree object is missing root'); }
if (!tree.nodes) { throw new Error('Tree object is missing nodes'); }

return traverseTree(tree.root, tree.nodes, {}, POST_ORDER);
};

/**
* Executes an ordered depth first search on the dependency tree and returns a
* list of nodes.
*
* @param {String} root - The root or current node.
* @param {Object} nodes - Child map (node -> children[])
* @param {Object} visited - Map of visited nodes (node -> true || false)
* @param {Integer} order - 1 = pre-order; 2 = post-order
*/
function traverseTree(root, nodes, visited, order) {
if ((order !== PRE_ORDER) && (order !== POST_ORDER)) {
throw new Error ('Traversal order not supported: ' + order);
}

var list = [];
if (order === PRE_ORDER) {
list.push(root);
}

// If the root has already been visited, it, and its dependencies, will
// already appear in the list.
if (visited[root]) {
return [];
}

// Mark the node as visited
visited[root] = true;

var children = nodes[root] || [];

children.forEach(function(child) {
list = list.concat(traverseTree(child, nodes, visited, order));
});

if (order === POST_ORDER) {
list.push(root);
}

return list;
}

/**
* Returns the list of dependencies for the given filename
* Protected for testing
Expand All @@ -52,7 +134,7 @@ module.exports._getDependencies = function(filename) {
}

return dependencies;
}
};

/**
* @param {String} filename
Expand All @@ -61,7 +143,7 @@ module.exports._getDependencies = function(filename) {
* @return {String[]}
*/
function traverse(filename, root, visited) {
var tree = [filename];
var tree = [];

if (visited[filename]) {
return visited[filename];
Expand Down Expand Up @@ -94,8 +176,9 @@ function traverse(filename, root, visited) {
tree = removeDups(tree);

visited[filename] = visited[filename].concat(tree);
tree.push(filename);
return tree;
};
}

/**
* Returns a list of unique items from the array
Expand Down
Loading