ng-esbuild-obfuscator
v1.0.0
Published
面向 Angular 18+ esbuild 构建链的代码混淆插件
Maintainers
Readme
ng-esbuild-obfuscator
面向 Angular 18 及以上 esbuild 构建链的业务代码混淆插件。它根据 esbuild metafile 判断每个输出 chunk 是否包含项目业务源码,只处理命中的 JavaScript chunk,并保留静态导入和懒加载 import() 的模块路径。
特性
- Angular 18+及后续采用 esbuild application builder 的版本可用。
- 只识别
src下的业务 TypeScript/JavaScript,默认排除src/assets。 - 业务代码和第三方依赖被打进同一 chunk 时处理整个 chunk,避免业务代码漏混淆。
- 使用 JavaScript 语法分析器保护静态导入、再导出和动态导入路径。
- 默认采用轻量混淆配置,避免控制流平坦化、死代码注入带来的明显体积和性能开销。
- 提供摘要日志、debug 判定日志、严格失败策略和 source map 防泄漏检查。
- 同时提供开箱即用的默认插件和可配置工厂函数,包含 TypeScript 类型声明。
环境要求
- Node.js
>= 18.19.0;实际 Node.js 版本还必须满足所用 Angular CLI 的要求。 - Angular 18 或更高版本。
@angular-builders/custom-esbuild必须与 Angular 主版本一致。
| Angular | 自定义 builder |
| --- | --- |
| 18 | @angular-builders/custom-esbuild@^18 |
| 19 | @angular-builders/custom-esbuild@^19 |
| 20 | @angular-builders/custom-esbuild@^20 |
| 21 | @angular-builders/custom-esbuild@^21 |
| 22 | @angular-builders/custom-esbuild@^22 |
本包不依赖 Angular API,因此不需要跟随 Angular 主版本发布不同构建产物。兼容边界由 esbuild 插件 API 和对应版本的自定义 builder 提供。
esbuild >= 0.21 < 1 是 peer dependency,通常会由 Angular 构建工具链提供或被 npm 自动解析,无需在常规 Angular 项目中单独配置。
安装
以 Angular 18 项目为例:
npm install --save-dev ng-esbuild-obfuscator @angular-builders/custom-esbuild@^18其他 Angular 版本只需替换自定义 builder 的主版本。
推荐接入方式
在 Angular 项目中新建 tools/esbuild-plugins.mjs:
import { createObfuscatorPlugin } from 'ng-esbuild-obfuscator';
export default createObfuscatorPlugin({
sourceRoots: ['src'],
excludeRoots: ['src/assets'],
logLevel: process.env.OBFUSCATOR_LOG_LEVEL ?? 'info',
obfuscatorOptions: {
seed: 20260722,
},
});修改 angular.json。保留原有 build 配置,只替换 builder,并新增一个独立的 obfuscate configuration:
{
"projects": {
"your-app": {
"architect": {
"build": {
"builder": "@angular-builders/custom-esbuild:application",
"options": {
"outputPath": "dist/your-app",
"index": "src/index.html",
"browser": "src/main.ts",
"tsConfig": "tsconfig.app.json"
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all"
},
"obfuscate": {
"optimization": true,
"sourceMap": false,
"outputHashing": "all",
"plugins": ["tools/esbuild-plugins.mjs"]
}
}
}
}
}
}
}在 package.json 中增加脚本:
{
"scripts": {
"build:obfuscate": "ng build --configuration obfuscate"
}
}执行:
npm run build:obfuscate不要把插件放进常用的开发构建。混淆会增加构建时间,也会降低浏览器调试体验。
零配置接入
默认配置可直接使用包内插件文件:
{
"plugins": ["node_modules/ng-esbuild-obfuscator/dist/default-plugin.js"]
}@angular-builders/custom-esbuild 把 plugins 项当作相对工作区的文件路径加载,因此不能直接写成 "ng-esbuild-obfuscator/plugin"。需要自定义选项时,使用前述 tools/esbuild-plugins.mjs 包装文件。
配置项
| 配置 | 默认值 | 说明 |
| --- | --- | --- |
| enabled | true | 是否注册混淆逻辑。 |
| workspaceRoot | absWorkingDir / process.cwd() | 工作区根目录,通常无需手动设置。 |
| sourceRoots | ['src'] | 用于识别业务代码的源码目录。 |
| excludeRoots | ['src/assets'] | 从业务源码中排除的目录;传 [] 取消默认值。 |
| outputExtensions | ['.js', '.mjs', '.cjs'] | 允许处理的输出扩展名。 |
| obfuscatorOptions | 见下文 | 传给 javascript-obfuscator 的覆盖配置。 |
| logLevel | 'info' | silent、error、warn、info 或 debug。 |
| failOnError | true | 单个 chunk 混淆失败时是否让整个构建失败。 |
| sourceMapPolicy | 'error' | 检测到 source map 时采用 error、warn 或 ignore。 |
| logger | console | 工厂方式下可注入日志实现。 |
默认混淆参数:
{
compact: true,
identifierNamesGenerator: 'hexadecimal',
renameGlobals: false,
renameProperties: false,
controlFlowFlattening: false,
deadCodeInjection: false,
stringArray: true,
stringArrayRotate: true,
stringArrayThreshold: 1,
stringArrayEncoding: ['base64']
}obfuscatorOptions 会覆盖这些默认值。插件会把用户提供的 reservedStrings 与检测到的模块引用合并,防止懒加载 chunk 路径被改写。
不建议轻易开启 renameProperties、renameGlobals、controlFlowFlattening、deadCodeInjection 或 selfDefending。Angular、依赖库、接口字段、反射访问和 CSP 环境都可能受到影响;上线前必须做完整业务回归和性能评估。
日志
info 级别每轮构建只输出一行摘要:
[ng-esbuild-obfuscator] 完成:处理 12 个 chunk,跳过 3 个;1.82 MiB -> 2.06 MiB(+245.4 KiB);耗时 1843 ms。定位“某个 chunk 为什么没有处理”时临时启用 debug:
OBFUSCATOR_LOG_LEVEL=debug npm run build:obfuscatedebug 日志只记录 chunk 路径、业务输入数量和模块引用保护数量,不打印完整源码、环境变量或整份 metafile。
API
import obfuscatorPlugin, {
createObfuscatorPlugin,
type ObfuscatorPluginOptions,
} from 'ng-esbuild-obfuscator';- 默认导出:使用默认配置的 esbuild
Plugin。 createObfuscatorPlugin(options):创建可配置插件。ObfuscatorPluginOptions:完整 TypeScript 配置类型。
构建行为
- esbuild 完成打包并提供内存输出和
metafile。 - 插件逐个检查
.js、.mjs、.cjs输出。 - 只要 chunk 的输入列表含有
sourceRoots下且不在excludeRoots下的业务脚本,就处理整个 chunk。 - 插件解析 chunk 中的模块引用,并把路径加入
reservedStrings。 - 混淆结果替换内存中的输出内容,再由 Angular builder 写入
dist。
“整个 chunk”是 esbuild 打包后的最小可靠处理单位。一个 chunk 同时包含业务代码和第三方包时,无法在最终 JavaScript 中无损拆开模块,因此第三方代码也会随该 chunk 一起处理。
常见问题
报错无法访问 outputFiles 或 metafile
确认 build builder 是 @angular-builders/custom-esbuild:application,而不是 Angular 原生 @angular-devkit/build-angular:application 或 @angular/build:application;同时确认 plugins 放在该 build target 的 options/configuration 中。
报错检测到 source map
在混淆 configuration 中设置 "sourceMap": false。不建议把 sourceMapPolicy 改成 warn 或 ignore,因为 .map 文件可能直接包含原始源码。
懒加载页面无法打开
先用 logLevel: 'debug' 确认相关 chunk 已处理。若应用还在运行时拼接非字面量资源路径,把这些固定路径的完整正则加入 obfuscatorOptions.reservedStrings。ES 模块的静态和字面量动态导入由插件自动保护。
构建明显变慢或产物变大
这是字符串数组混淆的正常代价。优先降低 stringArrayThreshold,不要先开启控制流平坦化或死代码注入。可通过固定 seed 获得可复现产物,方便比较体积。
SSR 项目是否可用
插件会依据每次 esbuild 构建的 metafile 处理命中业务源码的 JavaScript,包括 browser/server 构建。SSR 上线前需要验证 Node.js 启动、路由渲染和动态导入;若只希望处理浏览器产物,应将插件仅配置到单独的浏览器 build target。
本地开发
跨项目联调、打包验证和发布流程见 docs/local-development.md。可配置包装文件见 examples/esbuild-plugins.mjs。
