JavaScript Obfuscator fits best after your JavaScript assets have already been generated. Use the online tool for quick validation, then move into the desktop project and command-line workflow when you want a repeatable release process.
Typical release sequence
- Build or bundle your application so the JavaScript files you intend to ship are ready.
- Open those generated files, or the folders that contain them, in the desktop project.
- Configure obfuscation, exclusion, and cross-file options based on what your application exposes publicly.
- Test the protected output in the same environments where you normally validate a release.
- Generate the command line from the desktop project when you want to repeat the same protection settings automatically.
Good fits for the desktop workflow
- Applications that ship many JavaScript files together and need consistent renaming across them.
- Projects that embed JavaScript inside HTML, PHP, ASP, ASPX, JSP, or similar mixed files.
- Release processes that need the same protection settings reused across many builds.
How to protect public or shared names safely
If your application depends on names that must stay stable, use the Variable Exclusion List and the cross-file options such as Replace Globals and Protect Members. This is the safest way to keep integrations and shared identifiers compatible while still increasing protection.
Automation guidance
The command-line workflow is generated from the desktop project, which means your release automation can reuse the same protection settings instead of recreating them by hand. This is the recommended path for CI jobs, scripted release steps, or any environment where consistency matters more than one-off experimentation.
Start from Use Command Line when you are ready to turn a working desktop project into a repeatable build step. For Node-based pipelines the npm CLI is the canonical path and needs no desktop install at all.
Where protection goes in a CI pipeline
Protection is a release step, not a development step. The ordering below is the one that works in every stack, and each position is load-bearing:
- Install, lint, type-check, unit test. All against unprotected source, because that is what developers debug.
- Bundle and minify. Your bundler needs readable code to trace references and tree-shake. Running protection first destroys the analysis it depends on — the failure modes are enumerated in build order.
- Protect the build output in a single pass, pointing at the output directory rather than looping over files. Cross-file options make their decisions per run, so a per-file loop produces chunks that disagree with each other.
- Archive the identifier map as a private build artifact. Without it you cannot read a production stack trace from this release — see symbolication.
- Smoke test the protected artifact, not the pre-protection one. This is the single most commonly skipped step and the one that catches almost everything.
- Generate hashes, manifests and integrity attributes last, from the files you are actually deploying.
- Deploy the protected directory.
Steps 5 and 6 are where most protected releases go wrong, and both failures have the same root cause: a later step consuming the pre-protection artifact. A useful guard is to have the pipeline delete the unprotected output directory immediately after step 3, so nothing downstream can accidentally reference it.
What each release should retain
A protected release is only supportable if you keep a small set of artifacts alongside it. Store these privately, per release, for as long as that release can appear in a stack trace:
- The identifier map — the private record of which original name became which generated name. Never publish it beside the bundle.
- The exact option set used, ideally the committed config file plus any CI overrides that were applied.
- The seed, if you used one, so the build can be reproduced byte-for-byte later — see reproducible builds.
- Hashes of every emitted file, which is what lets you later prove that a file served in production is the file you released.
- The compatibility-validation result, so a regression has a baseline to be compared against.
Anything that must not ship — source maps, identifier maps, config files with credentials — is covered in deployment hygiene.
Making a protected release reviewable
Polymorphic output is the default: the same input produces a different artifact every build. That is a real protection property, and it makes change review harder, because every rebuild looks like a total rewrite of the file.
If your process needs to answer “did anything actually change in this deploy?”, protect release candidates with a seed derived from the version tag. Every build of a given version is then byte-identical, and a diff between two builds of the same version is empty unless something genuinely changed. Different versions still look nothing alike, so you keep polymorphism between releases while gaining diffability within one.
npx jso-protector --config jso.config.json \
--seed "$RELEASE_TAG" \
--input dist --output dist-protected \
--manifest dist-protected/jso-manifest.json
Smoke tests worth running on every protected build
Keep this short enough that it runs on every release rather than being skipped. The goal is coverage of the paths where protection changes behaviour, not coverage of your application:
- Load the app cold and confirm no console error on first paint.
- Exercise one path that crosses a network boundary, so JSON field names are checked.
- Exercise one path that reads or writes storage, so persisted keys are checked.
- Navigate to a lazily loaded route, so cross-chunk naming is checked.
- Confirm no
.map file and no identifier map is reachable from the deployed origin.
Compatibility validation automates the first layer of this by checking the protected output parses and behaves like its input; the list above covers the contracts that only your application knows about.
Practical rule of thumb: obfuscate the code you actually ship, not the original authoring sources that will still be transformed later by other build steps. If a step after protection reads the pre-protection files, that step is a bug waiting to reach production.