Skip to content

Commit

Permalink
tests for cycle output, proper return of cycle output, history for 0.…
Browse files Browse the repository at this point in the history
…4.0, documentation
  • Loading branch information
clux committed Jul 18, 2014
1 parent dcff79b commit aa08fbc
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 12 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,10 @@
0.4.0 / 2014-07-18
==================
* do cycle analysis on the digraph given by module-deps
* cyclical dependencies now detected and highlighted in the output tree
* `-c` flag for raw cycles from Tarjan's algorithm
* bump dependencies

0.3.3 / 2014-02-09
==================
* module-deps upgraded to `1.5.0`
Expand Down
62 changes: 55 additions & 7 deletions README.md
Expand Up @@ -5,36 +5,84 @@
[![coverage status](http://img.shields.io/coveralls/clux/npm-graph.svg)](https://coveralls.io/r/clux/npm-graph)
[![unstable](http://img.shields.io/badge/stability-unstable-E5AE13.svg)](http://nodejs.org/api/documentation.html#documentation_stability_index)

Essentially `npm ls` with two modifications:

`npm ls`, but without dependencies that are not explicitly required
- only listing dependencies that are explicitly required
- highlights cyclical dependencies

## Usage
Install globally and give it a path to a _local_ package or a file:

```bash
$ npm install -g npm-graph
```

### no arguments - npm modules only

# no arguments - npm modules only
$ npm-graph irc-stream/
```
$ npm-graph node_modules/irc-stream/
irc-stream
└───irc
```

If all modules in "dependencies" are used, then this should look like `npm ls`.

### show builtins

# show builtins
$ npm-graph irc-stream/ -b
```
$ npm-graph node_modules/irc-stream/ -b
irc-stream
├──┬irc
│ ├───net
│ ├───tls
│ └───util
└───stream
```

This can give some at a glance information about how browserifiable the module is.

# show local files
$ npm-graph irc-stream/ -l
### show local files
File by file inclusion (a requirement for cycle detection):

```
$ npm-graph node_modules/irc-stream/ -l
irc-stream
└──┬irc
├───./codes
└───./colors
```

### cycle detection
Cycles are detected and shown in the tree with a `` after an offender. As an example, readable-stream (tsk tsk) closes a cyclical loop by having Duplex depend on Writable and vice versa (albeit lightly).

```
$ npm install readable-stream@1.0.27-1
$ npm-graph node_modules/readable-stream/writable.js -l
writable.js
└─┬./lib/_stream_writable.js
├─┬./_stream_duplex ↪ ./_stream_writable
│ ├─┬./_stream_readable
│ │ ├──core-util-is
│ │ ├──inherits
│ │ ├──isarray
│ │ └──string_decoder/
│ ├──core-util-is
│ └──inherits
├──core-util-is
└──inherits
```

The mutual file inclusions would normally cause a recursion overflow when generating the tree if we hadn't first found the [strongly connected components](https://npmjs.org/package/strongly-connected-components) in the inclusion digraph and manually broken the cycle. Thank you mathematics.

The cyclical components from Tarjan's algorithm are also available with `-c`:

```
$ npm-graph node_modules/readable-stream/writable.js -l -c
[ [ './node_modules/readable-stream/lib/_stream_writable.js',
'./node_modules/readable-stream/lib/_stream_duplex.js' ] ]
```

In this case, a 2-cycle.

## License
MIT-Licensed. See LICENSE file for details.
8 changes: 7 additions & 1 deletion bin.js
Expand Up @@ -6,6 +6,12 @@ var graph = require('./').analyze
, dir = path.join(process.cwd(), argv._[0] || '.')
, name, file;

var opts = {
showLocal: Boolean(argv.l),
showBuiltins: Boolean(argv.b),
showCycles: Boolean(argv.c)
};

// resolve entry point dynamically
if (path.extname(dir) === '.js') { // either we got the specific entry point
file = dir;
Expand All @@ -29,4 +35,4 @@ graph(file, name, function (err, str) {
throw err;
}
console.log(str);
}, { showLocal: Boolean(argv.l), showBuiltins: Boolean(argv.b) });
}, opts);
2 changes: 1 addition & 1 deletion lib/npm-graph.js
Expand Up @@ -45,7 +45,7 @@ exports.analyze = function (file, name, cb, opts) {
}).on('end', function () {
var cycleData = cycle.trimWhileCycles(lookup);
if (opts.showCycles) {
return console.log(inspect(cycleData.cycles, { colors: true }));
return cb(null, inspect(cycleData.cycles, { colors: true }));
}

var labeler = function (o) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -21,7 +21,7 @@
"coveralls": "$(npm bin)/jscoverage lib && NPM_GRAPH_COV=1 $(npm bin)/nodeunit --reporter=lcov test | $(npm bin)/coveralls"
},
"dependencies": {
"minimist": "0.0.5",
"minimist": "^0.2.0",
"module-deps": "^2.1.5",
"strongly-connected-components": "^1.0.1",
"topiary": "^1.1.1"
Expand Down
53 changes: 51 additions & 2 deletions test/self.graph.test.js
Expand Up @@ -2,10 +2,10 @@ var graph = require('../').analyze;
var join = require('path').join;

exports.packages = function (t) {
graph(join(__dirname, 'fake-package', 'index.js'), 'fake-package', function (err, str) {
graph(join(__dirname, 'fake-package', 'index.js'), 'fp', function (err, str) {
t.ok(!err, 'worked');
t.deepEqual(str.split('\n'), [
"fake-package",
"fp",
" └──fake2"
],
"fake-package deps"
Expand Down Expand Up @@ -51,3 +51,52 @@ exports.entryPoint = function (t) {
t.done();
}, { showLocal: true });
};

exports.cycleModule = function (t) {
var writableEntry = join(
__dirname,
'fake-package',
'node_modules',
'readable-stream',
'writable.js'
);
graph(writableEntry, 'writable.js', function (err, str) {
t.ok(!err, 'worked');
t.deepEqual(str.split('\n'), [
"writable.js",
" └─┬./lib/_stream_writable.js",
" ├─┬./_stream_duplex ↪ ./_stream_writable",
" │ ├─┬./_stream_readable",
" │ │ ├──core-util-is",
" │ │ ├──inherits",
" │ │ ├──isarray",
" │ │ └──string_decoder/",
" │ ├──core-util-is",
" │ └──inherits",
" ├──core-util-is",
" └──inherits"
], "cycle indicated");
t.done();
}, { showLocal: true });
};

exports.cycleModuleCycles = function (t) {
var writableEntry = join(
__dirname,
'fake-package',
'node_modules',
'readable-stream',
'writable.js'
);
graph(writableEntry, 'writable.js', function (err, str) {
t.ok(!err, 'worked');
var out = str.split('\n').map(function (s) {
return s.match(/\/readable\-stream\/(.*)/)[1];
});
t.deepEqual(out, [
"lib/_stream_writable.js\'\u001b[39m,",
"lib/_stream_duplex.js\'\u001b[39m ] ]"
], "cycle indicated");
t.done();
}, { showLocal: true, showCycles: true });
};

0 comments on commit aa08fbc

Please sign in to comment.