importClosureOf function

Map<String, Set<String>> importClosureOf(
  1. String entryPoint
)

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;
}