Avoiding Identifier Collisions Across Obfuscated Files
When several VM-obfuscated files end up in the same bundle, they can declare the same top-level identifier and crash the page at parse time. Here is why it happens and how to fix it.
The symptom
Your build runs fine, but the browser throws at parse time, before the app starts:
To see how many chunks declare the name from the error, count the files that contain a declaration of it:
-l lists each matching file once and -w matches whole words only, so vmdX is not counted. var is left out because repeating a var declaration is not an error on its own. A result above 1
means the name is declared in more than one chunk. Replace vmd with the name from your error message.
Identifier collisions only matter when the declarations share a scope. The same name repeated in separate modules or closures is not proof of a collision; the parse error above is.
Why this happens
VM presets use the mangled-shuffled identifier-names generator. It walks a small alphabet in a shuffled order, and
under VM obfuscation every renamed global gets a vm prefix (the default identifiersPrefix). The shuffled order is
cached per process, so files obfuscated in the same process, or with the same fixed seed, draw from the same
sequence - the first global in each file shares a name, the second shares another, and so on.
Each file is obfuscated independently and the generator restarts from the beginning of its sequence for every one. When
two files end up in the same scope, the names collide. Two top-level const vmd = … declarations land in the same
scope and the parser rejects the second one.
Options that introduce more top-level identifiers increase the odds that two files reach the same generated name.
vmWrapTopLevelInitializers is one of them, and every VM preset already enables it; options such as
vmDynamicOpcodes or vmBytecodeEncoding add more.
Fixes
Prefer bundling first, then obfuscating the bundle once. If you do obfuscate separately and the scripts share a global scope, pick one of the following:
Enable
randomIdentifiersPrefix(recommended)Each obfuscation run gets a random prefix prepended to every global identifier (under VM obfuscation it replaces the default
vmprefix). Names from different files no longer share a namespace, so collisions disappear without you having to coordinate prefixes by hand.Set a unique
identifiersPrefixper fileManually pass a different prefix when obfuscating each file (e.g.
identifiersPrefix: 'auth_'for one,checkout_for another). Effective, but error-prone if you have many files - prefer the random option above.Switch
identifierNamesGeneratortohexadecimalHexadecimal names use a much larger keyspace, so two files are far less likely to produce the same identifier - but uniqueness is not guaranteed. Trade-off: identifiers are longer than
mangled-shuffledoutput, so the bundle is slightly larger.Use the multi-file batch UI, or check your bundler plugin
The dashboard adds a distinct prefix per file when you use multi-file batch obfuscation, which is available on paid plans. If you use a bundler plugin, verify its behavior instead of assuming it does the same. If you're wiring up obfuscation manually with the npm API, you have to opt in to a prefix yourself.
To verify the fix, rebuild and reload the combined application and confirm the SyntaxError is gone. The name from
the old error message should no longer be declared in more than one chunk; a prefix changes every generated name, so
the grep above only tells you about that specific name, not about the new ones.
Related options
randomIdentifiersPrefix- random prefix per build (recommended)identifiersPrefix- explicit prefix stringidentifierNamesGenerator- name shape (mangled,hexadecimal, …)
