如何获取TypeScript编译器在项目中用到的所有node_modules文件?
获取TypeScript项目依赖的node_modules文件
你可以通过TypeScript Compiler API的Program对象分析项目的依赖关系,精准提取出项目源文件直接或间接依赖的node_modules中的文件,具体实现如下:
核心思路
- 利用
ts.createProgram生成项目编译程序,获取所有源文件的依赖解析能力 - 遍历项目自身的源文件,解析每个文件的导入/require语句,找到对应的模块路径
- 递归追踪依赖链,筛选出路径包含
node_modules的文件并去重收集
修改后的完整代码
import * as fs from "fs" import * as ts from "typescript" // 递归收集所有依赖的node_modules文件 function collectNodeModulesDeps( program: ts.Program, filePath: string, collected: Set<string>, processedFiles: Set<string> ): void { if (processedFiles.has(filePath)) return; processedFiles.add(filePath); const sourceFile = program.getSourceFile(filePath); if (!sourceFile) return; // 处理ES模块导入/导出 ts.forEachChild(sourceFile, node => { let moduleSpecifier: string | undefined; if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) { const specifier = node.moduleSpecifier; if (specifier && ts.isStringLiteral(specifier)) { moduleSpecifier = specifier.text; } } // 处理CommonJS require调用 else if (ts.isCallExpression(node) && node.expression.getText() === 'require') { const arg = node.arguments[0]; if (arg && ts.isStringLiteral(arg)) { moduleSpecifier = arg.text; } } if (moduleSpecifier) { const resolvedModule = program.getResolvedModule(filePath, moduleSpecifier); if (resolvedModule && resolvedModule.resolvedFileName) { const resolvedPath = resolvedModule.resolvedFileName; // 筛选node_modules中的文件 if (resolvedPath.includes('node_modules')) { collected.add(resolvedPath); } // 递归追踪该模块的依赖,确保收集到间接依赖 collectNodeModulesDeps(program, resolvedPath, collected, processedFiles); } } }); } function compile(fileNames: string[], options: ts.CompilerOptions): void { const createdFiles: { [key: string]: string } = {} const host = ts.createCompilerHost(options); host.writeFile = (fileName: string, contents: string) => { console.log(">>", fileName) createdFiles[fileName] = contents } const program = ts.createProgram(fileNames, options, host); // 初始化收集容器 const nodeModulesDeps = new Set<string>(); const processedFiles = new Set<string>(); const projectFiles = new Set(fileNames); // 遍历所有项目源文件,启动依赖收集 projectFiles.forEach(file => { collectNodeModulesDeps(program, file, nodeModulesDeps, processedFiles); }); // 输出收集到的node_modules依赖文件 console.log('\n=== 依赖的node_modules文件 ==='); nodeModulesDeps.forEach(file => console.log(file)); // 执行编译输出 program.emit(); } const parseConfigHost = { fileExists: fs.existsSync, readDirectory: ts.sys.readDirectory, readFile: (file: string) => fs.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: true, } const configResult = ts.parseJsonConfigFileContent( ts.readConfigFile('./tsconfig.json', ts.sys.readFile).config, parseConfigHost, "." ) compile(configResult.fileNames, configResult.options)
关键说明
- 模块解析兼容性:同时支持ES模块(import/export)和CommonJS(require)的依赖解析,确保覆盖项目中所有模块引用方式
- 递归收集:通过递归追踪每个依赖模块的自身依赖,能完整收集到直接和间接用到的
node_modules文件 - 精准筛选:仅收集项目实际引用的模块文件,不会包含
node_modules中未被使用的冗余文件 - 可定制化:如果不需要类型定义文件(
.d.ts),可以在筛选条件中添加&& !resolvedPath.endsWith('.d.ts')
内容的提问来源于stack exchange,提问作者Corno
相关产品推荐
相关产品推荐

