Documentation

Variable Exclusion List

Reference guides for release workflows, command-line usage, cross-file protections, and the desktop app.

Inside the Docs

Practical guides for real release work.

How-to guides Start with release sequencing and command-line usage, then move into feature-specific references.
Advanced protection Browse cross-file controls like Replace Globals and Protect Members when a build spans multiple scripts.

Variable Exclusion List

  • VariableExclusion
  • Free

Variable Exclusion List defines which identifiers must never be renamed during name mangling. It is the escape hatch you reach for when something outside your file refers to a name by its exact text, and a rename would break that contract silently.

How to define exclusions

The setting is a multi-line list of regular expressions. Each line is one matching rule.

^myname$
^myvalue$

Source code:

function () {
    var myname;
    var myvalue;
    var hello;
}

Output with the exclusions above:

function () {
    var myname;
    var myvalue;
    var a;
}

Configuration

In jso.config.json, the publicNames array is the ergonomic form — a list of regular expressions for public names to preserve, mapped to VariableExclusion:

{
  "publicNames": ["^MyWidget$", "^ga$", "^dataLayer$"]
}

The raw engine option takes the same patterns as one newline-separated string:

{
  "options": {
    "VariableExclusion": "^MyWidget$\n^ga$\n^dataLayer$"
  }
}

What actually needs excluding

Most identifiers do not need to be here. Local variables, parameters, and local functions can always be renamed, because nothing outside the file can see them. Exclusions are for names that cross a boundary the obfuscator cannot see:

  • Globals other scripts call. A widget entry point (window.MyWidget), an analytics queue (dataLayer, ga), a callback name a third-party script looks up by string.
  • Names referenced from HTML. An inline onclick="submitForm()" attribute is a string in your markup, not code the obfuscator rewrote — the function it names must keep that name.
  • Names built at runtime. Anything reached through window[someString], eval, or a dynamic import() path. The obfuscator cannot follow a computed reference, so it cannot know the name is load-bearing.
  • Framework and platform contracts. Lifecycle method names a framework invokes by convention, service-worker event names, and anything a native bridge resolves by string.

The failure mode when you miss one is worth internalising: the build succeeds, the file parses, and the feature is broken at runtime in a way that only shows up on the path that uses it. That is why verifying protected output means running your tests, not just checking that the output loads.

Not the same as reservedStrings

Two different escape hatches for two different transform families — mixing them up is a common early mistake:

VariableExclusion / publicNamesreservedStrings
ProtectsIdentifiers (variable and function names)String literals
AgainstReplaceNames, RenameGlobals, DeepObfuscateMoveStrings, EncodeStrings
Use whenSomething calls a name from outside the fileSomething matches a literal byte-for-byte

For a whole region of a file rather than individual names, use inline directives, which mark the region in the source itself. For property and method names specifically, see Protect Members.

Anchor your patterns

These are regular expressions, not exact names, and an unanchored pattern matches far more than people expect. id matches id, but also idx, valid, hidden, and candidate — so a single sloppy line can leave most of your identifiers un-renamed while the build still reports success and the output still looks obfuscated at a glance.

  • Anchor both ends for an exact name: ^dataLayer$, not dataLayer.
  • Anchor the start for a deliberate prefix convention: ^api_ keeps every identifier you have prefixed for export.
  • Matching is case-sensitive. ^myWidget$ does not match MyWidget.
  • Audit the result. Diff the identifier count in protected output before and after adding a pattern. If adding one exclusion left dozens of names readable, the pattern is too broad.
Keep the list short: every excluded name is a name an analyst gets for free, and a long exclusion list is usually a sign the module's public surface is too wide. Where you can, narrow the surface instead — expose one entry point object, exclude that one name, and let everything behind it be renamed.

Frequently asked questions

When do I need an exclusion list at all?

When something outside your file refers to an identifier by its exact text, so a rename would break a contract silently. The usual cases are a global your other scripts call, a name referenced from markup, an entry point a framework looks up, and anything reached through a string rather than through a reference. If nothing outside the file can name it, you do not need to exclude it, because renaming a purely local name is always safe.

What format do the entries take?

Each line is one regular expression, and the list is a multi-line set of them. In the configuration file the ergonomic form is an array of patterns for public names to preserve, which maps to the same engine option. Both accept the same expressions, so choose whichever fits how you manage configuration and do not maintain the two in parallel.

Why should I anchor my patterns?

Because an unanchored expression matches far more than you intended, and the damage is invisible. A bare fragment matches every identifier that contains it, so a short pattern can exempt a large part of your file from renaming and quietly reduce the protection you thought you had applied. Anchoring each pattern at both ends confines it to the exact name you meant, which is why the examples are written that way.

How is this different from reserved strings?

They cover different things and are easy to confuse. This list preserves identifiers from being renamed. Reserved strings preserve string literals from the string transforms. If the thing you are protecting is a name in your code, you want this list; if it is a piece of text the outside world matches on, such as an event name or a key another system sends you, you want reserved strings. Some names need both, because they appear in code and in data.

What actually needs excluding in a typical project?

Less than people expect, and the exercise of writing the list is usually worth more than the list itself. The genuine cases are entry points, framework-facing names, globals shared with unprotected scripts, and anything looked up dynamically by name. If your list is long, that usually indicates the file exposes more of itself than it needs to, and narrowing the public surface is a better fix than a longer list.

Does excluding a name weaken the rest of the protection?

Only for that name, and the effect is proportionate rather than dramatic. An excluded identifier keeps whatever meaning its original name carried, so a reader learns something from it. Everything else in the file is still renamed. The reason to keep the list short is that each entry gives back a small amount of information, and a long list gives back enough to orient a reader quickly.

Try this in the online obfuscator

Paste your own code and see this option applied, or compare plans for larger projects and the desktop app.

Try It Free See Pricing