You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从package.json提取依赖构建项目逻辑树?定制类npm ls工具

实现自定义的npm ls风格依赖树工具(带过滤功能)

核心背景与需求回顾

我们拥有多个不同项目,分析每个项目的依赖往往难度大、耗时久。开发者或团队接手新项目时,需花费大量时间调研依赖构成。
需求:实现一个类似npm ls命令的解决方案,该工具可从package.json获取依赖并构建项目逻辑树,且需具备过滤功能。

实现思路

我刚好在团队里解决过类似的多项目依赖分析问题,核心逻辑拆解为三步:

  1. 读取目标项目的package.json,提取生产/开发依赖集合
  2. 构建结构化的依赖树形数据,方便后续处理
  3. 提供灵活的过滤规则(按名称模糊匹配、依赖类型筛选等)

完整代码实现(Node.js)

用Node.js实现最适配npm生态,代码简洁易扩展,步骤如下:

1. 初始化工具项目

mkdir dep-tree-analyzer && cd dep-tree-analyzer
npm init -y

2. 编写核心工具代码(index.js)

const fs = require('fs').promises;
const path = require('path');
const minimist = require('minimist'); // 解析命令行参数,需先安装:npm i minimist

// 读取目标项目的package.json并提取依赖
async function getProjectDependencies(projectPath) {
  try {
    const pkgFilePath = path.resolve(projectPath, 'package.json');
    const pkgContent = await fs.readFile(pkgFilePath, 'utf8');
    const pkg = JSON.parse(pkgContent);
    
    return {
      prod: pkg.dependencies || {},
      dev: pkg.devDependencies || {}
    };
  } catch (err) {
    console.error(`读取package.json失败: ${err.message}`);
    process.exit(1);
  }
}

// 构建基础依赖树结构
function buildDependencyTree(deps, type) {
  return Object.entries(deps).map(([name, version]) => ({
    name,
    version,
    type,
    // 如需深度遍历嵌套依赖,可在此添加递归逻辑读取子依赖的package.json
    // children: await getNestedDependencies(name, version)
  }));
}

// 实现过滤逻辑
function filterDependencyTree(tree, filterOpts) {
  let filteredTree = [...tree];
  
  // 按依赖类型过滤(prod/dev二选一)
  if (filterOpts.type) {
    filteredTree = filteredTree.filter(item => item.type === filterOpts.type);
  }
  
  // 按名称模糊匹配过滤(不区分大小写)
  if (filterOpts.name) {
    const matchRegex = new RegExp(filterOpts.name, 'i');
    filteredTree = filteredTree.filter(item => matchRegex.test(item.name));
  }
  
  return filteredTree;
}

// 格式化输出为类似npm ls的树形字符串
function formatTreeToPrintable(tree, indentLevel = 0) {
  let output = '';
  const indentStr = '  '.repeat(indentLevel);
  
  tree.forEach((item, index) => {
    const branchSymbol = index === tree.length - 1 ? '└─' : '├─';
    output += `${indentStr}${branchSymbol} ${item.name}@${item.version} (${item.type})\n`;
    
    // 递归格式化嵌套子依赖(如果有)
    if (item.children?.length) {
      output += formatTreeToPrintable(item.children, indentLevel + 1);
    }
  });
  
  return output;
}

// 主执行函数
async function main() {
  const args = minimist(process.argv.slice(2));
  const targetPath = args.path || process.cwd(); // 默认当前目录
  const filterOptions = {
    type: args.type, // 可选值:prod/dev
    name: args.filter // 模糊匹配关键词
  };
  
  // 获取依赖集合
  const { prod, dev } = await getProjectDependencies(targetPath);
  
  // 构建完整依赖树
  let fullTree = [];
  fullTree.push(...buildDependencyTree(prod, 'prod'));
  fullTree.push(...buildDependencyTree(dev, 'dev'));
  
  // 应用过滤规则
  const filteredTree = filterDependencyTree(fullTree, filterOptions);
  
  // 输出结果
  console.log(`\n项目依赖树(路径:${targetPath}):\n`);
  console.log(formatTreeToPrintable(filteredTree));
}

// 启动工具
main();

3. 安装依赖

npm i minimist

使用说明

运行工具时可传入以下参数:

  • --path <项目路径>:指定要分析的项目目录(默认当前目录)
  • --type <prod/dev>:仅显示生产依赖或开发依赖
  • --filter <关键词>:模糊匹配依赖名称(不区分大小写)

示例命令

# 分析当前项目的所有依赖
node index.js

# 只查看生产依赖
node index.js --type prod

# 过滤名称包含"react"的依赖
node index.js --filter react

# 分析指定项目的开发依赖,且筛选包含"webpack"的项
node index.js --path ../my-vue-project --type dev --filter webpack

扩展优化建议

  • 深度遍历嵌套依赖:添加递归逻辑读取每个依赖的package.json,构建完整的嵌套树(完全复刻npm ls的输出)
  • 导出结构化文件:支持将依赖树导出为JSON/CSV格式,方便后续批量分析
  • 全局命令化:将工具配置为全局命令,直接在任何项目目录运行dep-tree

内容的提问来源于stack exchange,提问作者Slava Vasylenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:19:09