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