# Obfuscate a webpack build

@afterpack/webpack taps compilation.hooks.processAssets and obfuscates your assets before webpack writes them.

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

`@afterpack/webpack` is a webpack 5 plugin: a class with `apply(compiler)`. It reads [`@afterpack` directives](https://www.afterpack.dev/docs/directives) from your source before minification, then runs the engine on `compilation.hooks.processAssets`, at the last stage before webpack writes.

Each asset's bytes are read out of the compilation and the obfuscated replacement is handed back to it, so webpack never writes your unobfuscated bundle. A failed obfuscation leaves the output directory as it was.

## Install

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

```js
// webpack.config.js
import { AfterpackWebpackPlugin } from "@afterpack/webpack";

export default {
  mode: "production",
  output: { path: "dist" },
  plugins: [new AfterpackWebpackPlugin()],
};
```

The export is the class `AfterpackWebpackPlugin`.

## Build

The plugin taps `compilation.hooks.processAssets` at the last stage before webpack writes. Only the `.js`, `.mjs` and `.cjs` assets a chunk of the compilation claims are rewritten; `.hot-update.` chunks are skipped. An asset the compiler carries but did not build, such as a manifest another plugin emitted or a copied file, is left alone.

An integrity plugin that taps `afterProcessAssets` still runs after this pipeline, so its hashes are computed over the obfuscated bytes.

`processAssets` runs on every compilation, including rebuilds in watch mode, and every rebuild pays the engine's cost. Set [`build.autorun: false`](https://www.afterpack.dev/docs/config#build-autorun) (or `AFTERPACK_build_autorun=false`) for the dev loop and keep it on for production builds.

## Verify

A production webpack build sets the [production build mode](https://www.afterpack.dev/docs/config#build-mode), so the [Protection Map](https://www.afterpack.dev/docs/protection-map) is off by default. Ask for it explicitly:

```js
new AfterpackWebpackPlugin({ protectionMap: { enabled: true } });
```

The [map's default](https://www.afterpack.dev/docs/config#protectionMap-enabled) couples to bundler-sourcemap presence, so set `devtool: "source-map"` if you want it to render your original source. Without one, the plugin warns that it found no bundler source map. The file is always written to the gitignored `.afterpack/protectionMap.html`, never into `dist/`. It 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 `new AfterpackWebpackPlugin({ preset: "hard", complexity: 40 })`. The names mirror [`@afterpack/vite`](https://www.afterpack.dev/docs/frameworks/vite#options) and [`@afterpack/next`](https://www.afterpack.dev/docs/frameworks/nextjs#options) exactly, so a config is portable between packages.

## Directives

Directives are read from your source by the plugin.

```js
/* @afterpack preset=hard */
function verifyLicense(token) {
  // ...
}
/* @afterpack end */
```

Directives are block comments only: a `// @afterpack …` line comment is not scanned and surfaces as a [diagnostic](https://www.afterpack.dev/docs/diagnostics). The full grammar is on the [Directives](https://www.afterpack.dev/docs/directives#writing-one) page.

Capture skips `node_modules` and non-JS/TS modules. Set [`directives.enabled: false`](https://www.afterpack.dev/docs/config#directives-enabled) to skip the scan entirely; hand-authored [`regions`](https://www.afterpack.dev/docs/config#regions) still work either way.

## Edge cases

- **Vendored code.** webpack co-bundles `node_modules` into your chunks, so there is no output-filename pattern that separates them. AfterPack works on the emitted files as a whole; [`paths.exclude`](https://www.afterpack.dev/docs/config#paths-exclude) is the file-level carve-out when you need one.
- **Fail-closed, with no override.** A failed file or an empty result fails the build rather than shipping unobfuscated code.

## Next

- [Frameworks](https://www.afterpack.dev/docs/frameworks): the full matrix.
- [Vite](https://www.afterpack.dev/docs/frameworks/vite): the reference integration and the full option semantics.
- [Configuration](https://www.afterpack.dev/docs/config): every option above, and which surface can set it.
- [Directives](https://www.afterpack.dev/docs/directives): the marker grammar in full.
- [Presets](https://www.afterpack.dev/docs/presets): the ladder `preset` resolves against.
- [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.
