Find all deep/nested/recursive/descendant dependencies/imports/requires of a JavaScript Module.
Related to sverweij/dependency-cruiser#564, find all modules which a given JavaScript module depends on, and all the modules they depend on, and all the modules they depend on etc.
npm install -g ts-node
npm install --save-dev dependency-cruiser
Fork this script if you need more, but currently the arguments you want to pass are inside the script here:
findNestedDependencies({
focus: "path/to/entry.js",
filesOrDirectories: ["path/to/"],
});
Run it from the Command Line:
$ ts-node --skip-project scripts/find-nested-dependencies.ts
Fork this script if you need more, but currently this version just outputs an indented list like this:
path/to/entry.js
path/to/some/location/wherever/direct-dependent-1.js
path/to/wherever/nested-dependent-1.js
path/to/direct-dependent-2.js
path/to/wherever/in/project/nested-dependent-2.js
import { spawn } from "child_process";
import path from "path";
import type { ICruiseResult, IModule } from "dependency-cruiser";
findNestedDependencies({
focus: "path/to/entry.js",
filesOrDirectories: ["path/to/"],
});
const root = path.resolve(__dirname, "..");
interface Options {
/** Same as `--focus` in `depcruise --help` */
focus: string;
/** Same as `[files-or-directories]` in `depcruise --help` */
filesOrDirectories: string[];
}
async function findNestedDependencies({ focus, filesOrDirectories }: Options): Promise<void> {
const graph = await getGraph(filesOrDirectories);
const entry = findBySource(focus);
const visited = new Set<string>();
let depth = 0;
let tree: string[] = [];
visit(entry);
console.log(tree.join("\n"));
async function getGraph(scopes: string[]): Promise<ICruiseResult> {
let json = "";
const child = spawn("depcruise", `--output-type json ${scopes.join(" ")}`.split(" "), {
cwd: root,
});
for await (const chunk of child.stdout) {
json += chunk;
}
return JSON.parse(json);
}
function visit(oModule: IModule | undefined) {
if (oModule && !hasBeenVisited(oModule) && isLocal(oModule) && !isUknown(oModule)) {
visited.add(oModule.source);
const indent = "\t".repeat(depth);
tree.push(`${indent}${oModule.source}`);
if (Array.isArray(oModule.dependencies)) {
depth++;
oModule.dependencies.forEach((dep) => {
visit(findBySource(dep.resolved));
});
depth--;
}
}
}
function hasBeenVisited(oModule: IModule): boolean {
return visited.has(oModule.source);
}
function isLocal(oModule: IModule): boolean {
return oModule.source.includes("packages/") && !oModule.source.includes("node_modules/");
}
function isUknown(oModule: IModule): boolean {
return Array.isArray(oModule.dependencyTypes) && oModule.dependencyTypes.includes("unknown");
}
function findBySource(source: string): IModule | undefined {
return graph.modules.find((mod) => mod.source === source);
}
}