
Defeat every bundler in the chain
On this page
An API route of mine needed a module that simply does not exist where the build runs. It is a platform-native module that only appears at runtime on the deploy target. On my machine, and in the build environment, it is not there.
That should have been fine. The route only runs on the target, where the module exists. But the build kept failing, because something insisted on resolving the import before it ever ran.
Two somethings, actually.
Two bundlers, one import#
The first was the framework’s own static analysis. It walks your imports at build time to work out what to include, and when it saw the specifier it went looking for the module and could not find it.
The second was the deploy adapter’s bundler, which does its own pass to package everything for the target. It also saw the import, also tried to resolve it, also failed.
So I had two independent tools both trying to be helpful, and both breaking the build over a module that is only ever present at runtime. A build that will not complete is a feature that cannot ship, so this one invisible import was holding the entire deploy hostage no matter how correct the code was.
A runtime-only module has to survive every bundler between your source and the deployed artefact. Miss one and the build still dies.
Two coordinated fixes#
Marking the module external in the bundler config handled the tool I could configure. External means “do not try to bundle this, leave the import alone, it will be there at runtime”. I marked the whole module namespace external so nothing under it was followed.
That satisfied the bundler I had settings for. It did not satisfy the static analysis that folds string literals aggressively.
Some analysers are clever enough to constant-fold a literal import specifier and follow the string even when you would rather they did not. The way past that is to never hand them a literal to fold.
So I stopped writing the specifier as a literal. I stored it base64-encoded and reassembled the real string at runtime, with an ignore comment on the dynamic import so the analyser would leave it be. No bundler can constant-fold a string that does not exist until the code actually runs.
I also set the runtime compatibility flag the target needs, so the native module is actually available once the route executes.
Every bundler in the chain, plus the analyser#
When a runtime provides modules your build tools cannot see, you do not have one bundler to get past. You have every bundler in the chain, plus static analysis itself.
Externals handle the tools you can configure. Runtime string assembly handles the ones that fold literals whether you asked them to or not. You need both, because they fail in different ways for different reasons.
It felt like fighting my own toolchain. In a sense it was. Every one of those tools was doing exactly what it is meant to do, which is resolve imports early. I just happened to have one import that must not be resolved until it is somewhere else.


