exportedDeclarationsOf function
The public declarations entryPoint exports, keyed by name: its own,
its parts', and those of every file it re-exports within its package,
with show / hide applied.
A package: export of another package is not followed: those names are
that package's, re-exported for convenience, and are not this package's
to name.
Implementation
Map<String, PublicDeclaration> exportedDeclarationsOf(String entryPoint) {
final context = _PackageContext.of(entryPoint);
final visiting = <String>{};
Map<String, PublicDeclaration> namespaceOf(String file) {
final path = p.normalize(p.absolute(file));
final handle = File(path);
if (!handle.existsSync() || !visiting.add(path)) return const {};
final source = handle.readAsStringSync();
final stripped = dartCodeWithoutComments(source);
final names = <String, PublicDeclaration>{};
void declareFrom(String declaringFile, String text) {
for (final d in topLevelDeclarations(text)) {
if (d.name.startsWith('_')) continue;
names.putIfAbsent(
d.name,
() => PublicDeclaration(d.name, d.kind, declaringFile),
);
}
}
declareFrom(path, source);
for (final part in _partDirective.allMatches(stripped)) {
final partFile = p.normalize(p.join(p.dirname(path), part.group(2)!));
if (File(partFile).existsSync()) {
declareFrom(partFile, File(partFile).readAsStringSync());
}
}
for (final export in _exportDirective.allMatches(stripped)) {
final uri = export.group(2)!;
String? target;
final selfPrefix = context == null ? null : 'package:${context.name}/';
if (selfPrefix != null && uri.startsWith(selfPrefix)) {
target = p.join(
context!.libDirectory,
uri.substring(selfPrefix.length),
);
} else if (!uri.contains(':')) {
target = p.join(p.dirname(path), uri);
}
if (target == null) continue;
var exported = namespaceOf(target);
final combinators = export.group(3)!;
for (final c in RegExp(
r'\b(show|hide)\b([^;]*?)(?=\b(?:show|hide)\b|$)',
).allMatches(combinators)) {
final listed = {for (final n in c.group(2)!.split(',')) n.trim()}
..remove('');
exported = {
for (final e in exported.entries)
if (listed.contains(e.key) == (c.group(1) == 'show'))
e.key: e.value,
};
}
for (final e in exported.entries) {
names.putIfAbsent(e.key, () => e.value);
}
}
visiting.remove(path);
return names;
}
return namespaceOf(entryPoint);
}