Dart语言如何通过编程方式获取所有可导入的类及Dart文件
Dart 编程获取所有可导入文件及公开类实现方案
要实现这个需求不要用纯正则硬解析,Dart语法存在大量边界情况(注释中的类名、part文件、私有成员、语法嵌套),正则匹配的误判率极高,直接用官方维护的analyzer包做语法级解析是最稳妥的方案。
1. 添加依赖
在项目pubspec.yaml中添加开发依赖,版本号和你当前使用的Dart SDK版本适配即可:
dev_dependencies: analyzer: ^6.4.0 path: ^1.8.0
2. 扫描所有可直接导入的Dart文件
可直接导入的Dart文件需要排除三类:缓存/构建输出目录下的文件、part of修饰的分片文件、你不需要的自动生成代码文件,参考实现:
import 'dart:io'; import 'package:path/path.dart' as p; Future<List<File>> scanImportableDartFiles(String projectRoot) async { final root = Directory(projectRoot); final result = <File>[]; await for (final entity in root.list(recursive: true, followLinks: false)) { // 跳过隐藏目录、构建缓存目录 final pathSegs = p.split(entity.path); if (pathSegs.any((seg) => seg.startsWith('.') || seg == 'build' || seg == '.dart_tool')) { continue; } if (entity is! File || p.extension(entity.path) != '.dart') continue; // 排除part分片文件,这类文件不能被直接import final content = await entity.readAsString(); if (RegExp(r'^\s*part\s+of\s+', multiLine: true).hasMatch(content)) continue; // 按需打开注释过滤生成文件 // final fileName = p.basename(entity.path); // if (fileName.endsWith('.g.dart') || fileName.endsWith('.freezed.dart')) continue; result.add(entity); } return result; }
3. 解析文件内可供外部导入的公开类
通过analyzer做语法树解析,筛选顶层、非下划线开头的类声明即可,这类声明在文件被import后可以直接被外部访问:
import 'dart:io'; import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:path/path.dart' as p; Future<List<String>> parsePublicClasses(String dartFilePath) async { final absPath = p.normalize(p.absolute(dartFilePath)); // 分析上下文可以批量复用,不需要每个文件新建一次 final collection = AnalysisContextCollection( includedPaths: [absPath], sdkPath: p.dirname(p.dirname(Platform.resolvedExecutable)), ); final session = collection.contextFor(absPath).currentSession; final resolvedUnit = await session.getResolvedUnit(absPath); final publicClasses = <String>[]; for (final dec in resolvedUnit.unit.declarations) { // 筛选公开类:非下划线开头的顶层类声明 if (dec is ClassDeclaration && !dec.name.lexeme.startsWith('_')) { publicClasses.add(dec.name.lexeme); } // 按需扩展:如果需要识别可导入的枚举、mixin、扩展方法,加对应类型判断即可 // if (dec is EnumDeclaration && !dec.name.lexeme.startsWith('_')) { // publicClasses.add(dec.name.lexeme); // } } return publicClasses; }
4. 调用示例
Future<void> main() async { const projectPath = './'; final files = await scanImportableDartFiles(projectPath); for (final file in files) { final relative = p.relative(file.path, from: projectPath); final classes = await parsePublicClasses(file.path); print('文件路径: $relative'); print('公开可导入类: ${classes.join(', ')}\n'); } }
补充说明
- 如果需要扫描pub依赖中的可导入文件和类,只需要把pub缓存对应的依赖路径加入扫描范围即可,解析逻辑完全一致
- part分片文件不需要单独解析,它的公开成员会随主文件的导入自动对外暴露
- 所有下划线开头的顶层成员都是库私有,即使文件被导入外部也无法访问,直接过滤即可
内容的提问来源于stack exchange,提问作者aguiadouro
相关产品推荐
相关产品推荐

