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

如何通过代码获取Node.js模块中的类/模块列表?

获取Node.js模块中的类/模块列表

要实现类似IntelliSense的模块成员提取,主要有静态分析和动态加载两种方案,考虑到你是做代码生成器(大概率要避免执行模块的副作用),优先推荐静态分析方案,以下是具体实现:

方案1:静态分析模块代码(推荐,无执行风险)

通过解析模块的AST(抽象语法树)提取导出的类、函数等成员,无需执行模块代码,适合处理未知或不可信的模块。

步骤:

  1. 安装依赖:@babel/parser(解析JS/TS代码)和@babel/traverse(遍历AST)
npm install @babel/parser @babel/traverse
  1. 编写解析代码:
const fs = require('fs');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;

function extractExports(modulePath) {
  const code = fs.readFileSync(modulePath, 'utf8');
  // 解析代码生成AST,支持JSX、TS等语法,可根据模块类型调整配置
  const ast = parser.parse(code, {
    sourceType: 'module',
    plugins: ['typescript', 'jsx']
  });

  const exports = {
    namedExports: [],
    defaultExport: null
  };

  traverse(ast, {
    // 处理命名导出(export class X {} / export const Y = ...)
    ExportNamedDeclaration(path) {
      // 处理直接声明的导出
      path.node.declaration?.declarations?.forEach(decl => {
        if (decl.id?.name) {
          const isClass = path.node.declaration.type === 'ClassDeclaration';
          exports.namedExports.push({
            name: decl.id.name,
            type: isClass ? 'class' : 'variable'
          });
        }
      });
      // 处理重导出(export { X } from './other')
      path.node.specifiers?.forEach(spec => {
        exports.namedExports.push({
          name: spec.exported.name,
          type: 're-export'
        });
      });
    },
    // 处理默认导出(export default class X {} / export default ...)
    ExportDefaultDeclaration(path) {
      const node = path.node.declaration;
      if (node.type === 'ClassDeclaration' && node.id?.name) {
        exports.defaultExport = {
          name: node.id.name,
          type: 'class'
        };
      } else if (node.type === 'Identifier') {
        exports.defaultExport = {
          name: node.name,
          type: 'reference'
        };
      }
    }
  });

  return exports;
}

// 使用示例
const moduleExports = extractExports('./your-module.js');
console.log('命名导出:', moduleExports.namedExports);
console.log('默认导出:', moduleExports.defaultExport);

方案2:动态加载模块(仅适合信任的模块)

直接加载模块并遍历导出对象,这种方式会执行模块代码,可能触发副作用(比如数据库连接、文件写入),只适合处理你完全信任的模块。

async function loadModuleExports(modulePath) {
  // 用ES模块的import(需Node.js支持,或配置package.json type:module)
  const moduleExports = await import(modulePath);
  
  const exportsInfo = [];
  for (const [key, value] of Object.entries(moduleExports)) {
    // 简单判断是否为类:类的typeof是'function',且prototype不可枚举
    const isClass = typeof value === 'function' && !Object.prototype.hasOwnProperty.call(value, 'prototype');
    exportsInfo.push({
      name: key,
      type: isClass ? 'class' : typeof value
    });
  }
  return exportsInfo;
}

// 使用示例
loadModuleExports('./your-module.js').then(console.log);

方案3:解析TypeScript类型定义(如果模块有.d.ts)

如果目标模块是TypeScript编写的,或带有.d.ts类型声明文件,可以直接解析类型定义获取更准确的成员信息:

const ts = require('typescript');

function extractTsExports(dtsPath) {
  const program = ts.createProgram([dtsPath], {});
  const sourceFile = program.getSourceFile(dtsPath);
  
  const exports = [];
  ts.forEachChild(sourceFile, node => {
    if (ts.isExportDeclaration(node)) {
      node.specifiers?.forEach(spec => {
        if (ts.isExportSpecifier(spec)) {
          exports.push({
            name: spec.name.text,
            type: 'named-export'
          });
        }
      });
      if (node.declaration && ts.isClassDeclaration(node.declaration)) {
        exports.push({
          name: node.declaration.name?.text,
          type: 'class'
        });
      }
    }
  });
  return exports;
}

// 使用示例
const tsExports = extractTsExports('./your-module.d.ts');
console.log(tsExports);

注意事项

  • 静态分析需要覆盖各种语法场景(比如解构导出、匿名类默认导出等),可根据你的实际模块类型扩展AST遍历逻辑。
  • 动态加载需警惕模块的副作用,比如模块初始化时的异步操作或全局变量修改。
  • 第三方模块优先用静态分析或解析其类型声明文件,避免不必要的执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 03:30:30