| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- import yargs from "yargs/yargs";
- import { hideBin } from "yargs/helpers";
- type Format = "tree" | "csv";
- const formats: ReadonlyArray<Format> = ["tree", "csv"];
- const args = yargs(hideBin(process.argv))
- .option("package", {
- type: "string",
- describe: "The full or relative path to the package.json file",
- require: true
- })
- .option("packageLock", {
- alias: "package-lock",
- type: "string",
- describe: "The full or relative path to the package-lock.json file",
- require: true
- })
- .option("format", {
- choices: formats,
- demandOption: true,
- default: "tree"
- })
- .help()
- .argv;
- interface Dictionary<T> {
- [index: string]: T
- }
- interface Package {
- dependencies: Dictionary<string>;
- devDependencies?: Dictionary<string>;
- }
- type PackageLockDependencies = Dictionary<{
- version: string;
- requires?: Dictionary<string>;
- dependencies?: PackageLockDependencies;
- }>
- interface PackageLock {
- dependencies: PackageLockDependencies;
- }
- type Tree<T> = Leaf<T> | Node<T>;
- type Node<T> = {
- type: "node";
- value: T;
- children: Tree<T>[];
- }
- type Leaf<T> = {
- type: "leaf";
- value: T;
- }
- type PackageLockTree = Tree<{ name: string, version: string }>;
- const constructDepTree = (packageName: string, context: PackageLockDependencies, stack: string[] = []): PackageLockTree => {
- const dependency = context[packageName];
- const value = { name: packageName, version: dependency.version };
- if (!dependency.requires) {
- return { type: "leaf", value };
- }
- return {
- type: "node",
- value,
- children: Object.keys(dependency.requires).filter(k => stack.every(s => s !== k))
- .map(requiredDepName => constructDepTree(requiredDepName, { ...context, ...dependency.dependencies }, [...stack, packageName]))
- };
- }
- const displayDepTree = (tree: PackageLockTree, level = 1): void => {
- console.log(`${" ".repeat(level - 1)}${tree.value.name}@${tree.value.version}`);
- if (tree.type === "node") {
- tree.children.forEach(t => displayDepTree(t, level + 1));
- }
- }
- const displayDepTreeAsCsv = (tree: PackageLockTree, depType: "dependency" | "devDependency", stack: string[] = []): void => {
- console.log(`${tree.value.name},${tree.value.version},${stack[0] ?? ""},${stack[stack.length - 1] ?? ""},${depType}`);
- if (tree.type === "node") {
- tree.children.forEach(t => displayDepTreeAsCsv(t, depType, [...stack, `${tree.value.name}@${tree.value.version}`]))
- }
- }
- Promise.all<Package, PackageLock>([
- import(args.package),
- import(args.packageLock)
- ])
- .then(([json, jsonLock]) => {
- const dependencyTree = Object.keys(json.dependencies)
- .map(depName => constructDepTree(depName, jsonLock.dependencies));
- if (args.format === "tree") {
- console.log("--------------------- dependencies --------------------");
- dependencyTree.forEach(t => displayDepTree(t));
- }
- if (args.format === "csv") {
- console.log("name,version,root,parent,type");
- dependencyTree.forEach(t => displayDepTreeAsCsv(t, "dependency"));
- }
- const devDependencyTree = Object.keys(json.devDependencies ?? {})
- .map(devDepName => constructDepTree(devDepName, jsonLock.dependencies));
- if (args.format === "tree") {
- console.log("-------------------- devDependencies --------------------");
- devDependencyTree.forEach(t => displayDepTree(t));
- }
- if (args.format === "csv") {
- devDependencyTree.forEach(t => displayDepTreeAsCsv(t, "devDependency"));
- }
- })
- .catch(reson => {
- console.error(reson);
- });
|