importClosureOf function
The import closure of entryPoint within its own package: every file
reached, mapped to the dart: and package: URIs it directly names.
This is the one walk a boundary audit is built on: three copies of a boundary check drift exactly the way the boundaries they check do.
Traversal follows relative directives and self-referential
package:<own package>/… ones, so a barrel that imports itself by package
URI does not silently end the walk. Every other package: URI is a leaf:
what a package pulls in from outside itself is precisely the boundary in
question. A third-party package's own transitive dart:io is a dependency
decision rather than a slip, and is caught by a real compile instead.
Implementation
Map<String, Set<String>> importClosureOf(String entryPoint) {
final context = _PackageContext.of(entryPoint);
final closure = <String, Set<String>>{};
final queue = <String>[p.normalize(p.absolute(entryPoint))];
while (queue.isNotEmpty) {
final file = queue.removeLast();
if (closure.containsKey(file)) continue;
final handle = File(file);
if (!handle.existsSync()) continue;
final external = <String>{};
for (final uri in directiveUris(handle.readAsStringSync())) {
final selfPrefix = context == null ? null : 'package:${context.name}/';
if (selfPrefix != null && uri.startsWith(selfPrefix)) {
queue.add(
p.normalize(
p.join(context!.libDirectory, uri.substring(selfPrefix.length)),
),
);
} else if (uri.startsWith('dart:') || uri.startsWith('package:')) {
external.add(uri);
} else if (!uri.contains(':')) {
queue.add(p.normalize(p.join(p.dirname(file), uri)));
}
}
closure[file] = external;
}
return closure;
}