Skip to content

Commit

Permalink
Added depth limit
Browse files Browse the repository at this point in the history
  • Loading branch information
keijokapp committed May 29, 2024
1 parent 5c9b1c3 commit dc4f0b0
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ Property | Type | Default | Description
`requireConfig` | String | null | RequireJS config for resolving aliased modules
`webpackConfig` | String | null | Webpack config for resolving aliased modules
`tsConfig` | String\|Object | null | TypeScript config for resolving aliased modules - Either a path to a tsconfig file or an object containing the config
`depth` | Number | null | Maximum dependency depth from source files to display
`layout` | String | dot | Layout to use in the graph
`rankdir` | String | LR | Sets the [direction](https://graphviz.gitlab.io/_pages/doc/info/attrs.html#d:rankdir) of the graph layout
`fontName` | String | Arial | Font name to use in the graph
Expand Down
12 changes: 12 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ program
.option('--require-config <file>', 'path to RequireJS config')
.option('--webpack-config <file>', 'path to webpack config')
.option('--ts-config <file>', 'path to typescript config')
.option('--depth <integer>', 'maximum depth from source files to draw')
.option('--include-npm', 'include shallow NPM modules', false)
.option('--no-color', 'disable color in output and image', false)
.option('--no-spinner', 'disable progress spinner', false)
Expand Down Expand Up @@ -113,6 +114,17 @@ if (program.tsConfig) {
config.tsConfig = program.tsConfig;
}

if (program.depth) {
config.depth = Number(program.depth);
}

if (config.depth) {
if (!Number.isInteger(config.depth) || config.depth < 0) {
console.log('%s %s', chalk.red('✖'), 'Invalid depth');
process.exit(1);
}
}

if (program.includeNpm) {
config.includeNpm = program.includeNpm;
}
Expand Down
1 change: 1 addition & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const defaultConfig = {
requireConfig: null,
webpackConfig: null,
tsConfig: null,
depth: null,
rankdir: 'LR',
layout: 'dot',
fontName: 'Arial',
Expand Down
61 changes: 43 additions & 18 deletions lib/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ class Tree {
const pathCache = {};

files.forEach((file) => {
if (visited[file]) {
return;
}

Object.assign(depTree, dependencyTree({
filename: file,
directory: this.baseDir,
Expand Down Expand Up @@ -148,7 +144,7 @@ class Tree {
}));
});

let tree = this.convertTree(depTree, {}, pathCache, npmPaths);
let tree = this.convertTree(visited, depTree, this.config.depth);

for (const npmKey in npmPaths) {
const id = this.processPath(npmKey, pathCache);
Expand All @@ -171,27 +167,56 @@ class Tree {
/**
* Convert deep tree produced by dependency-tree to a
* shallow (one level deep) tree used by madge.
* @param {Object} depTree
* @param {Object} modules
* @param {Object} tree
* @param {Object} pathCache
* @param {number} [depthLimit]
* @return {Object}
*/
convertTree(depTree, tree, pathCache) {
for (const key in depTree) {
const id = this.processPath(key, pathCache);

if (!tree[id]) {
tree[id] = [];

for (const dep in depTree[key]) {
tree[id].push(this.processPath(dep, pathCache));
convertTree(modules, tree, depthLimit) {
const self = this;
const depths = {};
const deepDependencies = {};

function calculateDepths(tree, depth) {
if (depth <= depthLimit) {
for (const dependency in tree) {
depths[dependency] = true;
calculateDepths(modules[dependency], depth + 1);
}
}
}

function getDeepDependencies(dependency) {
if (deepDependencies[dependency] === null) {
return [];
}

this.convertTree(depTree[key], tree, pathCache);
if (!(dependency in deepDependencies)) {
deepDependencies[dependency] = null;
deepDependencies[dependency] = [...new Set(Object.keys(modules[dependency]).flatMap(
(dependency) => dependency in depths ? [dependency] : getDeepDependencies(dependency)
))];
}

return deepDependencies[dependency];
}

const pathCache = {};
const result = {};

if (!Number.isInteger(depthLimit)) {
Object.entries(modules).forEach(([module, dependencies]) => {
result[self.processPath(module, pathCache)] = Object.keys(dependencies).map((dependency) => self.processPath(dependency, pathCache));
});
} else {
calculateDepths(tree, 0);

Object.keys(depths).forEach((module) => {
result[self.processPath(module, pathCache)] = getDeepDependencies(module).map((dependency) => self.processPath(dependency, pathCache));
});
}

return tree;
return result;
}

/**
Expand Down
48 changes: 48 additions & 0 deletions test/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,52 @@ describe('API', () => {
.catch(done);
});
});

describe('output depth limit', () => {
const baseDir = path.join(__dirname, 'deep-dependencies');
const entryFile = path.join(baseDir, 'a.js');

it('limits the output depth to 0', async () => {
const res = await madge(entryFile, {depth: 0});

res.obj().should.eql({
'a.js': []
});
});

it('limits the output depth to 1', async () => {
const res = await madge(entryFile, {depth: 1});

res.obj().should.eql({
'a.js': ['b.js', 'c.js'],
'b.js': [],
'c.js': ['b.js', 'c.js']
});
});

it('limits the output depth to 2', async () => {
const res = await madge(entryFile, {depth: 2});

res.obj().should.eql({
'a.js': ['b.js', 'c.js'],
'b.js': [],
'c.js': ['d.js', 'e.js'],
'd.js': [],
'e.js': ['b.js', 'c.js']
});
});

it('limits the output depth to 3', async () => {
const res = await madge(entryFile, {depth: 3});

res.obj().should.eql({
'a.js': ['b.js', 'c.js'],
'b.js': [],
'c.js': ['d.js', 'e.js'],
'd.js': [],
'e.js': ['f.js'],
'f.js': ['b.js', 'c.js']
});
});
});
});
2 changes: 2 additions & 0 deletions test/deep-dependencies/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import 'b.js';
import 'c.js';
Empty file added test/deep-dependencies/b.js
Empty file.
2 changes: 2 additions & 0 deletions test/deep-dependencies/c.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import 'd.js';
import 'e.js';
Empty file added test/deep-dependencies/d.js
Empty file.
1 change: 1 addition & 0 deletions test/deep-dependencies/e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'f.js';
2 changes: 2 additions & 0 deletions test/deep-dependencies/f.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import 'c.js';
import 'g.js';
1 change: 1 addition & 0 deletions test/deep-dependencies/g.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'b.js';

0 comments on commit dc4f0b0

Please sign in to comment.