How to find out which dependencies should be passed as transitive dependencies

I've read this proposal: swift-evolution/proposals/0409-access-level-on-imports.md at main · swiftlang/swift-evolution · GitHub

I'm really interested in

When using this access level information at the module level, if a dependency is never imported publicly and other requirements are met, it becomes possible to hide the dependency from clients. The clients can then be built without loading the transitive dependency. This can speed up build times and avoid the need to distribute modules that are implementation details.

So I want to bring this ability to Bazel's rules_swift.

Let's say A depends on B, and B depends on C.

Now I know C.swiftmodule should not be passed to A, if B internal imports C, and B is a resilient module.

But from build system's aspective, how could Bazel find out whether to consider C.swiftmodule as A's compilation inputs?

Is there any build products from module B, that can tell: "OK, module C is only my implementation details, do not use C.swiftmodule as my client."

One way could tell this is to parse B.swiftinterface, and find out all public import, then considering other dependencies of B as non-public import, and do not propagate them to Module A.

But I don't think this is a solid solution, because swiftinterface is not always necessary,and this solution would break if B is a non-resilient module.

Another way I can think about is use swiftc's emit-dependencies option before actually compile module A. But emit-dependencies is not always a non-trivial operation in non-trivial projects.

I think Xcodebuild really do well. Xcodebuild entirely skip build module A if C's public interface changed and B internal imports C, and B is a resilient module. So anyone know what is Xcodebuild's magic?