A preset is a named level of protection. One name sets three settings together: the complexity target, the output size multiplier, and the inflation budget. Setting complexity explicitly changes only the target; the other two stay as the preset defines them.
The ladder
| Preset | complexity | Max output size | String floor | Reach for it when |
|---|---|---|---|---|
minify | 0 | 1.2x | off | Size reduction only. No obfuscation; string literals stay readable. |
light | 2 | 2.0x | on | Default. String literals are encoded, structure lightly inflated. The right baseline for most projects. |
medium | 8 | 2.5x | on | Production code you want meaningfully harder to follow, at a modest size cost. |
hard | 25 | 4.0x | on | Code that matters: pricing, gating, license checks, client-side business logic. |
extreme | 80 | 7.0x | on | The highest applied complexity. Aim it at a file or region rather than a whole bundle. |
The string floor is on for any positive target.
The default is light
Run npx afterpack@latest dist/ with no --preset, or install a plugin and configure nothing, and you get light.
$ npx afterpack@latest dist/npx afterpack@latest dist/ --preset=hard # step upFlags join their value with =. A space-separated value is rejected as an unknown flag.
light is the zero-config floor. Every string literal it can rewrite goes through a runtime decoder. Some strings stay readable at every preset: directive prologues, typeof comparands, property names the runtime needs, and export names. The Protection Map lists them, and the engine reports the count and byte total on every build. Raising the preset from light to hard adds structural work: more inflation, more entanglement, more control-flow reshaping. It does not change string readability, which is already at its floor.
minify is the one rung that turns the floor off. It is minification only, with no obfuscation applied.
Size budget
A preset carries its own inflation.max rung: the multiplier column above. An unset inflation.max resolves to that rung.
The multiplier is a budget for a build, not a promise about one small file. Every output carries the engine's fixed runtime overhead — the decoders and the integer-expression interpreter — so a file of a few hundred bytes comes out several times larger than the rung suggests. Aim the multiplier at bundles, and read the ratio on a real one.
The two behave differently when the budget is reached:
- Preset budget (unset
inflation.max): the build stops adding transformations when the budget is reached and reportsDIAG_INFLATION_BUDGET_HIT. The output is valid, with fewer transformations than the target asked for. The build succeeds. - Explicit
inflation.max: hitting it is an error:DIAG_SIZE_CAP_REACHED, no output, exit1. Under-protected output is never written in its place.
Set inflation.max yourself only when you have a real size budget you would rather fail than exceed.
Setting the target directly
complexity is an unbounded non-negative number. --complexity=200 is a valid Free-tier override, well past what extreme sets.
npx afterpack@latest dist/ --preset=hard --complexity=40That is hard's size limits at target 40: the preset's multiplier and budget stay, only the target moves. Passing a preset name to --complexity is an error.
Applying a preset to one region
--preset sets the whole bundle uniformly; that is Free. Concentrating a heavier preset on one function or file is a Pro feature, through a source directive:
/* @afterpack preset=extreme */
function pricingEngine(plan, region) {
return resolve(plan, region);
}
/* @afterpack end */Directives are block comments only, read from your source by the plugin. See Directives for the grammar and the tier rules.
Next
- Configuration: every key a preset touches.
- CLI reference: the flags that select a preset.
- Directives:
skiporpreset=<name>for one span. - How AfterPack works: what a higher target actually buys against a static reader.
- Best practices: what to push harder and what to leave alone.