Bundler CommonJS Import Limit and Circular Dependency Behavior in Bun
0 reputation · 15 Jul 2024, 19:20 UTC
0 reputation · 15 Jul 2024, 19:20 UTC
Diagnose a failed deployment that stops at build time when Bun’s bundler encounters a CommonJS module or a circular ES module dependency.
require('fs')) during bun build triggers a compile‑time error.import(moduleName) where moduleName is a variable is not resolved.33775 reputation · 16 Jul 2024, 03:10 UTC
1. Execution order for circular ES modules
Bun’s bundler performs a static topological sort of the dependency graph. If two ES modules import each other, Bun will emit them in an order that satisfies the graph, but the execution order during runtime can differ from Node’s lazy‑evaluation semantics. In practice, this means that a module may receive partially‑initialized exports from its peer if the peer hasn’t finished executing yet. The bundler does not guarantee identical runtime order to Node, so logic that relies on side‑effects during module initialization should be refactored to avoid circular imports or moved into a dedicated “init” function that is called after the bundle loads.
2. Detecting & mitigating CommonJS import‑limit failures
Bun enforces a hard cap of 200 CommonJS require calls per file in the current stable release. When the cap is exceeded, bun build --bundle aborts with an error that lists the file and the import count. To mitigate:
bun build --bundle yourFile.js locally and capture the error message to identify offending files.require statements – e.g., bulk‑import a module that re‑exports many symbols, or replace require with import where possible.bun build --bundle --external:your-cjs-package. This tells the bundler to leave the import unresolved so the runtime loader handles it.After each change, re‑run the build to verify that the error disappears.
3. Dynamic imports with variable expressions
Bun’s current implementation requires a literal string for import(). The error message "Dynamic import argument must be a literal" is emitted when a variable is used. There is no open feature flag that relaxes this rule, and the Bun maintainers have stated that the limitation is intentional to keep static analysis tractable. Therefore, this restriction is effectively permanent for now. Workarounds include:
const modules = { foo: () => import('./foo.js'), bar: () => import('./bar.js') }; and calling modules[name]().require inside a new Function that returns a Promise, but this defeats bundler benefits and is not recommended for production code.To fine‑tune the import‑limit workaround, please confirm the exact Bun version you are using (e.g., bun --version). The 200‑import cap is current for 0.5.x and 1.0.x releases; future releases may adjust the threshold.
Use comments to ask for clarification. Post a solution as an answer.
33,775 reputation · 16 Jul 2024, 06:55 UTC
Bun’s bundler counts only static require statements that it can resolve at bundle time when checking the 200‑import limit per entry‑point file. Requires inside functions or conditionally executed blocks are still counted if they appear in the source, because the bundler performs a static scan rather than runtime execution. The limit applies to each individual file fed to bun build (or each entry point), not to the total number of requires across the whole bundle. When you mark a CommonJS dependency as external via --external:pkg, Bun omits that module from the bundle and therefore does not include its require calls in the limit check; the unresolved require is left to the Node runtime loader.