Repository navigation
feat: build hooks (pre/post + per-file transform) #252
Description
Users often need to run extra steps around a pkg build — bundling with esbuild/webpack beforehand, minifying/obfuscating the files pkg is about to embed, or smoke-testing the produced binary afterwards. Today this lives in external shell scripts, and there's no way to transform walker output without mutating the user's source tree.
Blocked on #242 — the per-file transform hook is inherently a JS function, so it needs the typed Node.js API options object to land first. Shipping the whole hooks surface together (rather than shell-only first, functions later) avoids an awkward intermediate config shape.
Proposal
Split hooks into two kinds with clearly different roles.
1. Shell hooks — preBuild, postBuild
For setup/teardown and smoke tests. Config-file friendly (strings).
{
"pkg": {
"hooks": {
"preBuild": "esbuild src/index.js --bundle --outfile=dist/bundle.js",
"postBuild": "./dist/my-app-linux --version"
}
}
}- Spawned via shell, stdio inherited, non-zero exit fails the build
postBuildruns per produced binary; path exposed asPKG_OUTPUTenv var- JS API also accepts function form (
() => Promise<void>)
2. Transformer hook — transform(filePath, contents) (JS API only)
For per-file content transformation after the walker collects files, before they're written into the VFS / SEA archive. This is the hook for minification / obfuscation.
await pkg.exec([...], {
transform: (filePath, contents) => {
if (filePath.endsWith('.js')) return minify(contents).code;
return contents;
}
});Why a dedicated transformer rather than a shell hook:
- Operates on the exact set of files pkg is embedding (walker output), not the whole source tree
- Never touches the user's source files on disk
- Per-file — users apply their own matching rules
- Runs before bytecode compilation and compression, so it composes cleanly with
--public-packagesand Brotli/GZip
Minification / Obfuscation
Deliberately not a built-in pkg feature — keeps runtime deps minimal. Instead, document recipes using the transform hook with the user's tool of choice (terser, javascript-obfuscator, swc, esbuild, etc.). Users pick the tool that fits their project; pkg stays out of the transform business.
Scope
- Lifecycle order:
preBuild→ walk →transform(per file) → bytecode/compression → write →postBuild(per binary) - Config surface: CLI flags not needed (function hooks can't live on the CLI); configure via
package.json#pkg/.pkgrc(feat: support a .pkgrc configuration file #238) for shell hooks, and via the typed Node.js API (feat: typed options object for the Node.js API #242) for thetransformfunction - Documentation: add a "Recipes" section covering minify/obfuscate via
transform, and pre-bundling viapreBuild
Part of #235
Activity
feat: build hooks (preBuild, postBuild, per-file transform) (yao-pkg#273