Skip to content

Commit cd0a06e

Browse files
committed
Added depth limit
1 parent c2191a1 commit cd0a06e

File tree

4 files changed

+64
-23
lines changed

4 files changed

+64
-23
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ Property | Type | Default | Description
222222
`requireConfig` | String | null | RequireJS config for resolving aliased modules
223223
`webpackConfig` | String | null | Webpack config for resolving aliased modules
224224
`tsConfig` | String\|Object | null | TypeScript config for resolving aliased modules - Either a path to a tsconfig file or an object containing the config
225+
`depth` | Number | null | Maximum dependency depth from source files to display
225226
`layout` | String | dot | Layout to use in the graph
226227
`rankdir` | String | LR | Sets the [direction](https://graphviz.gitlab.io/_pages/doc/info/attrs.html#d:rankdir) of the graph layout
227228
`fontName` | String | Arial | Font name to use in the graph

bin/cli.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ program
3232
.option('--require-config <file>', 'path to RequireJS config')
3333
.option('--webpack-config <file>', 'path to webpack config')
3434
.option('--ts-config <file>', 'path to typescript config')
35+
.option('--depth <integer>', 'maximum depth from source files to draw')
3536
.option('--include-npm', 'include shallow NPM modules', false)
3637
.option('--no-color', 'disable color in output and image', false)
3738
.option('--no-spinner', 'disable progress spinner', false)
@@ -112,6 +113,19 @@ if (program.tsConfig) {
112113
config.tsConfig = program.tsConfig;
113114
}
114115

116+
if ('depth' in program) {
117+
config.depth = program.depth;
118+
}
119+
120+
if ('depth' in config) {
121+
config.depth = Number(config.depth);
122+
123+
if (!Number.isInteger(config.depth) || config.depth < 0) {
124+
console.log('%s %s', chalk.red('✖'), 'Invalid depth');
125+
process.exit(1);
126+
}
127+
}
128+
115129
if (program.includeNpm) {
116130
config.includeNpm = program.includeNpm;
117131
}

lib/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const defaultConfig = {
1414
requireConfig: null,
1515
webpackConfig: null,
1616
tsConfig: null,
17+
depth: null,
1718
rankdir: 'LR',
1819
layout: 'dot',
1920
fontName: 'Arial',

lib/tree.js

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,19 @@ class Tree {
107107
* @return {Object}
108108
*/
109109
generateTree(files) {
110-
const depTree = {};
111-
const visited = {};
110+
const modules = {};
112111
const nonExistent = [];
113112
const npmPaths = {};
114113
const pathCache = {};
115114

116115
files.forEach((file) => {
117-
if (visited[file]) {
118-
return;
119-
}
120-
121-
Object.assign(depTree, dependencyTree({
116+
dependencyTree({
122117
filename: file,
123118
directory: this.baseDir,
124119
requireConfig: this.config.requireConfig,
125120
webpackConfig: this.config.webpackConfig,
126121
tsConfig: this.config.tsConfig,
127-
visited: visited,
122+
visited: modules,
128123
filter: (dependencyFilePath, traversedFilePath) => {
129124
let dependencyFilterRes = true;
130125
const isNpmPath = this.isNpmPath(dependencyFilePath);
@@ -145,10 +140,10 @@ class Tree {
145140
},
146141
detective: this.config.detectiveOptions,
147142
nonExistent: nonExistent
148-
}));
143+
});
149144
});
150145

151-
let tree = this.convertTree(depTree, {}, pathCache, npmPaths);
146+
let tree = this.convertTree(modules, files, this.config.depth);
152147

153148
for (const npmKey in npmPaths) {
154149
const id = this.processPath(npmKey, pathCache);
@@ -171,27 +166,57 @@ class Tree {
171166
/**
172167
* Convert deep tree produced by dependency-tree to a
173168
* shallow (one level deep) tree used by madge.
174-
* @param {Object} depTree
169+
* @param {Object} modules
175170
* @param {Object} tree
176-
* @param {Object} pathCache
171+
* @param {number?} depthLimit
177172
* @return {Object}
178173
*/
179-
convertTree(depTree, tree, pathCache) {
180-
for (const key in depTree) {
181-
const id = this.processPath(key, pathCache);
182-
183-
if (!tree[id]) {
184-
tree[id] = [];
185-
186-
for (const dep in depTree[key]) {
187-
tree[id].push(this.processPath(dep, pathCache));
174+
convertTree(modules, tree, depthLimit) {
175+
const self = this;
176+
const depths = {};
177+
const deepDependencies = {};
178+
179+
function calculateDepths(tree, depth) {
180+
if (depth <= depthLimit) {
181+
for (let i = 0; i < tree.length; i++) {
182+
const dependency = tree[i];
183+
depths[dependency] = true;
184+
calculateDepths(Object.keys(modules[dependency]), depth + 1);
188185
}
186+
}
187+
}
188+
189+
function getDeepDependencies(dependency) {
190+
if (deepDependencies[dependency] === null) {
191+
return [];
192+
}
189193

190-
this.convertTree(depTree[key], tree, pathCache);
194+
if (!(dependency in deepDependencies)) {
195+
deepDependencies[dependency] = null;
196+
deepDependencies[dependency] = [...new Set(Object.keys(modules[dependency]).flatMap(
197+
(dependency) => dependency in depths ? [dependency] : getDeepDependencies(dependency)
198+
))];
191199
}
200+
201+
return deepDependencies[dependency];
202+
}
203+
204+
const pathCache = {};
205+
const result = {};
206+
207+
if (!Number.isInteger(depthLimit)) {
208+
Object.entries(modules).forEach(([module, dependencies]) => {
209+
result[self.processPath(module, pathCache)] = Object.keys(dependencies).map((dependency) => self.processPath(dependency, pathCache));
210+
});
211+
} else {
212+
calculateDepths(tree, 0);
213+
214+
Object.keys(depths).forEach((module) => {
215+
result[self.processPath(module, pathCache)] = getDeepDependencies(module).map((dependency) => self.processPath(dependency, pathCache));
216+
});
192217
}
193218

194-
return tree;
219+
return result;
195220
}
196221

197222
/**

0 commit comments

Comments
 (0)