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

如何生成含导出项类型的JavaScript细粒度依赖JSON报告

实现ES6+ JS文件依赖及导出项的精准JSON报告方案

要生成你需要的精准依赖报告,可以用Node.js结合Babel的AST解析工具实现,直接扫描指定目录、解析每个JS文件的导入导出逻辑,整理成目标格式的JSON。

步骤1:安装依赖

首先安装解析AST所需的核心包:

npm install @babel/parser @babel/traverse @babel/types

步骤2:编写扫描脚本(命名为dependency-scanner.js)

const fs = require('fs').promises;
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');

// 目标扫描目录(按需修改)
const TARGET_DIR = path.resolve(__dirname, './js');
// 输出报告路径
const OUTPUT_FILE = path.resolve(__dirname, './dependency-report.json');

/**
 * 获取单个JS文件的导出项及类型
 * @param {string} filePath 文件绝对路径
 * @returns {Array<{name: string, type: string}>} 导出项列表
 */
async function getExportsFromFile(filePath) {
  const code = await fs.readFile(filePath, 'utf8');
  const ast = parser.parse(code, {
    sourceType: 'module',
    plugins: ['jsx'] // 若用TS可添加'typescript'插件
  });

  const exportsList = [];

  traverse(ast, {
    // 处理直接导出的函数/变量
    ExportNamedDeclaration(path) {
      const declaration = path.node.declaration;
      // 函数导出
      if (t.isFunctionDeclaration(declaration)) {
        exportsList.push({
          name: declaration.id.name,
          type: 'function'
        });
      }
      // 变量导出(区分常量/数组/对象)
      else if (t.isVariableDeclaration(declaration)) {
        declaration.declarations.forEach(declarator => {
          let type = 'constant';
          if (t.isArrayExpression(declarator.init)) type = 'array';
          if (t.isObjectExpression(declarator.init)) type = 'object';
          exportsList.push({
            name: declarator.id.name,
            type: declaration.kind === 'const' ? type : 'variable'
          });
        });
      }
    },
    // 处理批量导出的标识符
    ExportSpecifier(path) {
      exportsList.push({
        name: path.node.exported.name,
        type: 'unknown' // 需精准类型可递归解析源文件,此处简化处理
      });
    }
  });

  return exportsList;
}

/**
 * 获取单个JS文件的导入依赖信息
 * @param {string} filePath 文件绝对路径
 * @returns {Array<{source: string, imports: Array<string>}>} 导入依赖列表
 */
async function getImportsFromFile(filePath) {
  const code = await fs.readFile(filePath, 'utf8');
  const ast = parser.parse(code, { sourceType: 'module' });

  const importsList = [];

  traverse(ast, {
    ImportDeclaration(path) {
      const sourcePath = path.node.source.value;
      // 只处理本地相对路径依赖(排除第三方包)
      if (sourcePath.startsWith('./') || sourcePath.startsWith('../')) {
        const resolvedSource = path.resolve(path.dirname(filePath), sourcePath);
        const fullSourcePath = resolvedSource.endsWith('.js') ? resolvedSource : `${resolvedSource}.js`;
        const relativeSource = path.relative(TARGET_DIR, fullSourcePath);

        // 收集当前文件导入的具体名称
        const importedNames = path.node.specifiers
          .filter(spec => t.isImportSpecifier(spec))
          .map(spec => spec.imported.name);

        importsList.push({ source: relativeSource, imports: importedNames });
      }
    }
  });

  return importsList;
}

/**
 * 扫描目录生成最终依赖报告
 */
async function generateDependencyReport() {
  const report = {};

  // 递归遍历目录下所有JS文件
  async function traverseDir(dir) {
    const entries = await fs.readdir(dir, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.resolve(dir, entry.name);
      if (entry.isDirectory()) {
        await traverseDir(fullPath);
      } else if (entry.isFile() && entry.name.endsWith('.js')) {
        const relativePath = path.relative(TARGET_DIR, fullPath);
        const imports = await getImportsFromFile(fullPath);
        if (imports.length === 0) continue;

        report[relativePath] = {};
        // 匹配每个依赖文件中当前文件实际导入的导出项
        for (const imp of imports) {
          const depFilePath = path.resolve(TARGET_DIR, imp.source);
          try {
            const depExports = await getExportsFromFile(depFilePath);
            const matchedExports = depExports.filter(exp => imp.imports.includes(exp.name));
            report[relativePath][imp.source] = { exports: matchedExports };
          } catch (err) {
            console.warn(`无法解析依赖文件: ${depFilePath}`, err.message);
            report[relativePath][imp.source] = { exports: [] };
          }
        }
      }
    }
  }

  await traverseDir(TARGET_DIR);
  await fs.writeFile(OUTPUT_FILE, JSON.stringify(report, null, 2), 'utf8');
  console.log(`依赖报告已生成:${OUTPUT_FILE}`);
}

// 执行扫描
generateDependencyReport().catch(err => console.error('生成报告失败:', err));

步骤3:运行脚本

在终端执行:

node dependency-scanner.js

关键说明

  • 脚本仅扫描本地相对路径的JS依赖,自动排除第三方包
  • 支持识别函数、常量、数组、对象等常见导出类型
  • 若项目使用JSX或TypeScript,可在AST解析配置中添加对应插件
  • 生成的JSON结构完全匹配你要求的格式,仅保留实际导入的导出项,无冗余信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:23:13