Turbopack: add all keys to dynamic exports before sealing the object (#93334)
User surfaced an issue where a barrel file had `export * from` a chain
of CJS files. In this barrel-file-exports + dynamic-exports case, the
Turbopack runtime wraps `module.exports` in a `Proxy` whose `ownKeys`
trap synthesizes keys from the re-exported namespace. The constructed
proxy conflicts with `Object.seal`, which `esm()` calls to ensure the
namespace is immutable (per spec).
When you try to execute the code in the browser or for build, you get a
`TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy
target is non-extensible`
I've constructed a test case that fails today. Nothing about this code
is "wrong" -- a CJS module gets imported and re-exported, and `Page.tsx`
does an `import *` from that file. But `ownKeys` returns something
different from the defined properties, which causes the JS engine to
trigger an exception.
The solution is to remove the Object.seal() for dynamic modules.
Seal does two things:
* Prevent extension of the object
* Set each key's [[Configurable]] property to false
We want this for true ESModules, but for dynamic exports we need to allow flexibility. We do this by passing the dynamic flag to the codgen, and avoiding the seal call in those cases. We use Proxy traps to ensure exported values can't be modified or deleted externally, while still allowing the original dynamic module to change them.
While investigating, I also found that Object.keys(importNamespace) would miss re-exported dynamic keys. Similarly, 'exportName' in starImports would always return false for dynamic imports. Since we're already touching this code, I also fixed those cases.
Test cases cover the scenarios that seemed relevant to real user code, as well as the invariants we care about.