Skip to content

Commit

Permalink
Added depth limit
Browse files Browse the repository at this point in the history
  • Loading branch information
keijokapp committed Sep 30, 2023
1 parent c2191a1 commit cd0a06e
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 23 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
14 changes: 14 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 @@ -112,6 +113,19 @@ if (program.tsConfig) {
config.tsConfig = program.tsConfig;
}

if ('depth' in program) {
config.depth = program.depth;
}

if ('depth' in config) {
config.depth = Number(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
71 changes: 48 additions & 23 deletions lib/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,19 @@ class Tree {
* @return {Object}
*/
generateTree(files) {
const depTree = {};
const visited = {};
const modules = {};
const nonExistent = [];
const npmPaths = {};
const pathCache = {};

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

Object.assign(depTree, dependencyTree({
dependencyTree({
filename: file,
directory: this.baseDir,
requireConfig: this.config.requireConfig,
webpackConfig: this.config.webpackConfig,
tsConfig: this.config.tsConfig,
visited: visited,
visited: modules,
filter: (dependencyFilePath, traversedFilePath) => {
let dependencyFilterRes = true;
const isNpmPath = this.isNpmPath(dependencyFilePath);
Expand All @@ -145,10 +140,10 @@ class Tree {
},
detective: this.config.detectiveOptions,
nonExistent: nonExistent
}));
});
});

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

for (const npmKey in npmPaths) {
const id = this.processPath(npmKey, pathCache);
Expand All @@ -171,27 +166,57 @@ 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 (let i = 0; i < tree.length; i++) {
const dependency = tree[i];
depths[dependency] = true;
calculateDepths(Object.keys(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

0 comments on commit cd0a06e

Please sign in to comment.