Compatibility
Published
A router is the one piece of a single-page application that touches everything, so when a protected build shows a blank screen the router is a reasonable first suspect. We reduced a router to the parts that do not need a browser -- pattern compilation, parameter extraction, query parsing, history navigation, guards and URL resolution -- and diffed it against protected copies on five configurations. Nothing changed. The interesting result is what that implies about what protection does not hide.
What a router is, from the transform's point of view
A router is a table of strings, a set of regular expressions compiled from those strings, and some object bookkeeping. None of that is identifier structure, which is why the compatibility answer is short.
The sample compiles six route patterns including required parameters, an optional trailing segment and a catch-all, matches nine paths against them, extracts and percent-decodes parameters, parses query strings with repeated keys and plus-encoded spaces, splits hash fragments, drives a history stub through push, replace, back, forward and listener removal, runs a two-stage guard chain, resolves six relative URLs against a base, and dispatches through a registry keyed by route name.
All five configurations matched the original line for line. Route matching, parameter values, query parsing including the repeated-key array, history state after five navigations, guard verdicts and URL resolution were all identical.
The results in detail
Pattern compilation survives because it is ordinary string and RegExp work. /users/:id/posts/:postId compiled and matched to {"id":"42","postId":"7"} in every build. The optional segment correctly produced null when absent and the value when present. The catch-all captured a/b/c.txt as one parameter.
Percent-decoding is unchanged, including above-ASCII characters. A path segment encoded as caf%C3%A9 decoded to the same string before and after protection. This is worth stating because the protected file itself is pure ASCII: the writer escapes above-ASCII characters in string literals, and the value produced at runtime is identical anyway.
Query parsing kept its edge cases: a repeated key still collected into an array, a bare key with no equals sign still produced an empty string, a plus sign still decoded as a space, and an empty value stayed an empty string. History navigation produced the same five-event listener log and the same final location and stack length, and removing the listener still stopped events.
Relative resolution through URL matched exactly: a bare about, a ./about, a ../up, an absolute path, a query-only reference and a hash-only reference all resolved as before.
The two ways member renaming breaks a router
This is the area where a router differs from most code, because router objects use short, generic property names that collide with names the platform already owns. We measured both failure modes.
The first is the ordinary one: renaming a property changes the serialised shape. With a pattern matching path, query and hash, the parser's output went from {"path":"/search",...} to {"_0x4":"/search",...}. That is expected and harmless inside a bundle, and a problem the moment that object is written to sessionStorage, posted to an analytics endpoint, or read by code that was protected separately.
The second is specific and easy to miss: those same names exist on the platform's own objects. The same pattern that renamed the router's hash field also rewrote the read of hash on a real URL object, so relative resolution produced /a/b/aboutundefined. Nothing threw; a URL simply gained the text undefined on the end. A pattern matching push, replace and length was worse and failed outright with TypeError: pattern._0x1 is not a function, because those are Array and String methods.
Router property names are exactly the ones most likely to collide: path, hash, search, href, location, push, replace, length, state. If you enable member renaming in an application with a router, anchor MemberRegexp to names you are certain are yours, and re-test navigation specifically.
What protection does not do to your routes
The compatibility answer being clean has a direct consequence that is worth being straight about: your route table is still in the bundle, in readable form.
Route patterns are string literals. The string table can move them into an encoded array and decode them at use, which means they are not visible to a plain text search of the file, but they are visible to anyone who runs the decoder or watches the values at runtime. Anyone determined to enumerate your routes can, and a protected bundle does not change that.
The practical implication is about authorisation rather than obfuscation. A route guard in the browser decides what to render; it cannot decide what a user is allowed to fetch. If an administrative route exists and its guard is client-side only, the data behind it is protected by the server or it is not protected at all. Our guard chain measured identically after protection, which is precisely the point: it does exactly what it did before, including being bypassable by someone editing the running program.
Lazily loaded chunk names are the same story, and the interaction between protection and code splitting has its own measurement in does obfuscation break code splitting.
What to do with this
Run your existing routing tests against the protected bundle. If they cover navigation, parameter extraction and guard behaviour, that is sufficient; we could not find a configuration where any of those moved.
If you use member renaming, treat the router as the highest-risk area in the application for pattern collisions, and check navigation by hand after enabling it. The failure mode we measured was silent for URL resolution and a hard TypeError for array methods, so a smoke test that only checks the home route will not find it.
Do not rely on the router for authorisation. Client-side guards are a user experience mechanism; the server decides what data leaves it. That is true of unprotected applications too, and protection changes nothing about it either way.
For the URL and query-string layer specifically, the companion measurement is in does obfuscation break URL and query strings.
Frequently asked questions
Does obfuscation break a single-page app router?
No. We measured route pattern compilation, parameter extraction including percent-decoding, query parsing with repeated keys, history push, replace, back and forward, listener removal, guard chains and relative URL resolution against protected copies on five configurations covering both targets, two identifier-renaming presets and the string table. Every line was identical.
Are my route paths hidden after obfuscation?
No. Route patterns are string literals, and while the string table moves them into an encoded array so they do not appear in a plain text search, they are recoverable by anyone who runs the decoder or observes the values at runtime. Protection raises the effort of reading your routes; it does not hide them.
Does history.pushState still work in protected code?
Yes. History navigation is ordinary object and method work from the transform's point of view. Our history stub produced the same event log across push, replace, back and forward, the same final location and stack length, and listener removal still stopped events after protection.
Why did navigation break after I enabled member renaming?
Because router property names collide with names the platform owns. We measured a pattern matching path, query and hash silently corrupting a URL read into the text undefined, and a pattern matching push, replace and length failing outright with a TypeError because those are Array and String methods. Anchor MemberRegexp to names that are certainly yours.
Can I rely on route guards for security in a protected bundle?
No, and that is unrelated to protection. A guard running in the browser decides what to render, not what a user may fetch. Our guard chain behaved identically after protection, which includes remaining bypassable by anyone editing the running program. Authorisation belongs on the server.
Does protection affect lazily loaded route chunks?
Route names used as registry keys are strings and were unaffected in our sample. The separate question of how protection interacts with the chunk files themselves, including their names and load order, is covered in our code splitting measurement.
Related reading