Skip to content

Instantly share code, notes, and snippets.

@JamieMason
Created March 2, 2022 12:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JamieMason/536f8b11d4f5ccfeade8144e1e114cea to your computer and use it in GitHub Desktop.
Save JamieMason/536f8b11d4f5ccfeade8144e1e114cea to your computer and use it in GitHub Desktop.
Find all modules which a given JavaScript module depends on, and all they modules they depend on, and all they modules they depend on etc.

Find nested dependencies or imports of a JavaScript Module

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.

Installation

npm install -g ts-node
npm install --save-dev dependency-cruiser

Usage

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

Output

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

scripts/find-nested-dependencies.ts

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);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment