# Obfuscate a Rollup build

@afterpack/rollup reads directives from your source and runs the engine in generateBundle, before Rollup writes the output.

Source: https://www.afterpack.dev/docs/frameworks/rollup

`@afterpack/rollup` mirrors [`@afterpack/vite`](https://www.afterpack.dev/docs/frameworks/vite): Rollup is what Vite builds on, so the same approach applies. It reads directives from your source, then runs the engine in `generateBundle`, with the finished bundle in memory and before Rollup writes any of it.

## Install

```bash
npm install -D @afterpack/rollup
```

```js
// 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.

## Verify

Ask for the [Protection Map](https://www.afterpack.dev/docs/protection-map) explicitly on a [production build](https://www.afterpack.dev/docs/config#build-mode):

```js
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.

*(live Protection Map demo embed — see https://www.afterpack.dev/protection-map-demo.html)*

## Options

Every [configuration key](https://www.afterpack.dev/docs/config) can be passed here as the options object, for example `afterpackRollup({ preset: "hard", complexity: 40 })`.

## Directives

Directives are read from your source by the plugin, before minification, so a [directive](https://www.afterpack.dev/docs/directives) survives bundling.

```js
/* @afterpack preset=hard */
export function signRequest(payload) {
  // ...
}
/* @afterpack end */
```

Capture skips `node_modules`, virtual modules, and non-JS/TS files. The [grammar](https://www.afterpack.dev/docs/directives#writing-one) 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:

```js
output: [
  { dir: "dist/esm", format: "es", entryFileNames: "lib.js" },
  { dir: "dist/cjs", format: "cjs", entryFileNames: "lib.cjs" },
],
plugins: [afterpackRollup({ seed: "git" })],
```

Pin one [`seed`](https://www.afterpack.dev/docs/config#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](https://www.afterpack.dev/docs/frameworks): the full matrix.
- [Vite](https://www.afterpack.dev/docs/frameworks/vite): the same hooks, with Vite's config resolution.
- [Builds & CI](https://www.afterpack.dev/docs/builds): seeds, dev-vs-prod, promoting the same bytes.
- [Protection Map](https://www.afterpack.dev/docs/protection-map): reading the report.
- [Directives](https://www.afterpack.dev/docs/directives): every marker key, and what it does.
- [Configuration](https://www.afterpack.dev/docs/config): the engine config surface.
