# 混淆 esbuild 构建

@afterpack/esbuild 挂接 build.onEnd，就地混淆已写出的输出。

Source: https://www.afterpack.dev/zh/docs/frameworks/esbuild

`@afterpack/esbuild` 是一个标准的 esbuild 插件（`{ name, setup(build) }`）。它挂接 `build.onEnd`，该钩子在 esbuild 把输出写入磁盘后触发（`write !== false`，即默认值）。

它能处理两种输出形态：`outdir`（多个文件）和 `outfile`（单个文件）。已经出错的构建会被跳过。

## 安装

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

```js
// build.mjs
import { build } from "esbuild";
import { afterpackEsbuild } from "@afterpack/esbuild";

await build({
  entryPoints: ["src/main.js"],
  bundle: true,
  format: "esm",
  outfile: "dist/app.js",
  plugins: [afterpackEsbuild()],
});
```

具名导出为 `afterpackEsbuild`。

## 构建

插件在 `build.onEnd` 上运行，此时 esbuild 已经把输出写入磁盘，`outdir` 和 `outfile` 都是如此。不存在需要它跳过的单独开发命令：每一次写出文件的 `build()` 调用都会运行插件。

esbuild 没有提供流程内的钩子，因此在这一遍处理完成之前，未混淆的 bundle 会留在磁盘上；对一个 185 KB 的 bundle，这一遍处理在默认预设下大约需要四分之一秒。在这段时间窗口内能读取输出目录的任何东西都能读到它。混淆失败时，这份输出会原样保留：构建失败，所以不会有任何东西被发布，但这些文件会一直留着，直到下一次构建覆盖它们。

## 验证

[Protection Map](https://www.afterpack.dev/zh/docs/protection-map) 默认会在 esbuild 输出 [source map](https://www.afterpack.dev/zh/docs/config#sourceMap-enabled) 且构建不在 CI 中（`CI=true` 或 `CI=1`）时写出；[生产](https://www.afterpack.dev/zh/docs/config#build-mode)模式不会关闭它。除非你设置 `sourcemap`，否则 esbuild 不会输出 source map，因此 Protection Map 默认关闭。请显式开启：

```js
afterpackEsbuild({ protectionMap: { enabled: true } });
```

它会写入已被 gitignore 的 `.afterpack/protectionMap.html`。没有 `sourcemap: true` 时，插件会警告说找不到可用来渲染原始源码的打包工具 map。报告包含你的完整源码。绝不要提交或对外提供它。

## 选项

每个[配置键](https://www.afterpack.dev/zh/docs/config)都可以通过这里的选项对象传入，例如 `afterpackEsbuild({ preset: "hard", complexity: 40 })`。

## 指令

指令是从打包工具的 source map 中恢复出来的，因此如果使用指令，请以 `sourcemap: true` 构建。参见[指令](https://www.afterpack.dev/zh/docs/directives)。

## 边界情况

- **[失败即中止](https://www.afterpack.dev/zh/docs/diagnostics#fail-closed-no-exceptions)。**某个文件失败或输出为空，都会让构建被拒绝。
- **`write: false`。**磁盘上没有任何文件，因此插件什么也不做。AfterPack 需要已写出的文件。
- **TypeScript。**AfterPack 运行时，esbuild 已经剥离了类型；map 会一路链回你的 `.ts` 行。

## 下一步

- [框架](https://www.afterpack.dev/zh/docs/frameworks)：完整矩阵。
- [Vite](https://www.afterpack.dev/zh/docs/frameworks/vite)：参考集成以及完整的选项语义。
- [构建与 CI](https://www.afterpack.dev/zh/docs/builds)：种子、开发与生产环境的区别、原样提升同一份字节。
- [Protection Map](https://www.afterpack.dev/zh/docs/protection-map)：如何阅读报告。
- [指令](https://www.afterpack.dev/zh/docs/directives)：每个标记键及其作用。
- [配置](https://www.afterpack.dev/zh/docs/config)：引擎的配置面。
