siblingImportClosureOf function

Map<String, Set<String>> siblingImportClosureOf(
  1. String entryPoint,
  2. String root, {
  3. required String followPrefix,
})

The import closure of entryPoint across sibling packages: every file reached, mapped to the URIs it names that the walk did not follow.

importClosureOf stops at its own package, which is the right question for an entry point's own rules. This one answers the question a composition root poses — what does this build link? — so a package: URI whose package name starts with followPrefix is followed into the sibling package of that name under root, and only dart: URIs and other packages are leaves.

Implementation

Map<String, Set<String>> siblingImportClosureOf(
  String entryPoint,
  String root, {
  required String followPrefix,
}) {
  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 leaves = <String>{};
    for (final uri in directiveUris(handle.readAsStringSync())) {
      if (uri.startsWith('package:$followPrefix')) {
        final slash = uri.indexOf('/');
        final package = uri.substring('package:'.length, slash);
        queue.add(
          p.normalize(p.join(root, package, 'lib', uri.substring(slash + 1))),
        );
      } else if (uri.startsWith('dart:') || uri.startsWith('package:')) {
        leaves.add(uri);
      } else if (!uri.contains(':')) {
        queue.add(p.normalize(p.join(p.dirname(file), uri)));
      }
    }
    closure[file] = leaves;
  }
  return closure;
}