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 in the inclusion digraph and manually broken the cycle.

( •_•)

( •_•)>⌐■-■

(⌐■_■)

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