@afterpack/rollup mirrors @afterpack/vite: Rollup is what Vite builds on, so the same approach applies. It reads @afterpack directives from your source, then runs the engine in generateBundle, with the finished bundle in memory and before Rollup writes any of it.
Install
$ npm install -D @afterpack/rollup// rollup.config.mjs
import { afterpackRollup } from "@afterpack/rollup";
export default {
input: "src/index.js",
output: { dir: "dist", format: "es", sourcemap: true },
plugins: [afterpackRollup()],
};The named export is afterpackRollup.
Build
The plugin runs in generateBundle, with order: "post", so it is the last hook to touch the bundle. It obfuscates every JavaScript chunk the bundle holds and hands them back, so Rollup writes the obfuscated chunks and never the originals. A failed obfuscation leaves the output directory as it was.
Each chunk's name follows its obfuscated bytes, and precompressed .gz and .br copies in the bundle are regenerated from the obfuscated bytes.
Verify
The Protection Map is written by default when output.sourcemap is set and the build is not in CI (CI=true or CI=1); a production build does not turn it off. Ask for it explicitly when you want to inspect one:
afterpackRollup({ protectionMap: { enabled: true } });It lands in the gitignored .afterpack/protectionMap.html. With output.sourcemap on it renders your original source; without one, there is nothing to render from and the pass says so. The report contains your full source. Never commit it or serve it.
entitlements.js, 54 lines, built at the light preset with a directive around the signature check. Click any token to see what was applied to it.Options
Every configuration key can be passed here as the options object, for example afterpackRollup({ preset: "hard", complexity: 40 }).
Directives
Directives are a Pro feature. On a Free build, a directive that raises protection stops the build with DIAG_PRO_DIRECTIVE_REQUIRES_KEY.
Directives are read from your source by the plugin, before minification, so a directive survives bundling.
/* @afterpack preset=hard */
export function signRequest(payload) {
// ...
}
/* @afterpack end */Capture skips node_modules, virtual modules, and non-JS/TS files. The grammar is the same everywhere.
Edge cases
Multiple outputs
A config with two outputs runs generateBundle once per output, and each run only sees its own bundle. Nothing is obfuscated twice, whatever directories the outputs write to:
output: [
{ dir: "dist/esm", format: "es", entryFileNames: "lib.js" },
{ dir: "dist/cjs", format: "cjs", entryFileNames: "lib.cjs" },
],
plugins: [afterpackRollup({ seed: "git" })],Pin one seed across outputs that ship together.
Library builds
AfterPack runs on the bundle Rollup built, so every exported name your consumers import survives as written; only internals are renamed and restructured.
Next
- Frameworks: the full matrix.
- Vite: the same hooks, with Vite's config resolution.
- Builds & CI: seeds, dev-vs-prod, promoting the same bytes.
- Protection Map: reading the report.
- Directives: every marker key, and what it does.
- Configuration: the engine config surface.