Skip to content

Commit daf76c5

Browse files
robertsLandoclaude
andauthored
feat: enhanced SEA mode with walker integration and VFS (#229)
* feat: enhanced SEA mode with walker integration and VFS (#204) Evolves the --sea flag from a simple single-file wrapper into a full packaging pipeline that reuses the walker for dependency discovery, maps files as SEA assets, and provides a runtime VFS bootstrap using @platformatic/vfs for transparent fs/require/import support. - Add seaMode to walker: skips bytecode compilation and ESM-to-CJS transform, but still discovers all dependencies via stepDetect - Add sea-assets.ts: generates SEA asset map and manifest JSON from walker output (directories, stats, symlinks, native addons) - Add sea-bootstrap.js: runtime bootstrap with lazy SEAProvider, native addon extraction, and process.pkg compatibility - Add seaEnhanced() to sea.ts: walker → refiner → asset gen → blob → bake pipeline with Node 25.5+ --build-sea detection - Route --sea to enhanced mode when input has package.json and target Node >= 22; falls back to simple mode otherwise - Add 4 test suites: multi-file project, asset access, ESM (skipped until node:vfs lands), and VFS fs operations Closes #204 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: move @platformatic/vfs to optionalDependencies for Node 20 CI compat @platformatic/vfs requires Node >= 22 but CI runs on Node 20. Since the package is only needed at build time (esbuild bundles it into the sea-bootstrap) and the bundle step is skipped on Node < 22, making it optional prevents yarn install failures on older Node versions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: drop Node.js 20 support, require Node.js >= 22 Node.js 20 reached EOL in April 2026. This simplifies the project by removing the Node 20 CI matrix, test scripts, and engine check. It also allows @platformatic/vfs to be a regular dependency (requires Node 22+) and removes the conditional build script for sea-bootstrap. - Update engines to >=22.0.0 in package.json - Remove node20 from CI matrix (ci.yml, test.yml) - Update update-dep.yml to use Node 22 - Move @platformatic/vfs from optionalDependencies to dependencies - Simplify build:sea-bootstrap script (no Node version check) - Update README examples and SEA requirements - Update @types/node to ^22.0.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore test-00-sea Node version guard to < 20 Simple SEA mode was introduced in Node 20, not Node 22. The guard should reflect when the Node.js feature was added, not the project minimum. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore assertSeaNodeVersion to check Node >= 20 The shared assertSeaNodeVersion() is used by both simple and enhanced SEA modes. Simple SEA was introduced in Node 20 — the check should reflect that. Enhanced mode's Node 22 requirement is enforced separately by the routing logic in index.ts. Also fixes README --sea description to explain both modes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: enhance SEA mode tests for pkg compatibility and error handling * fix: improve error handling and conditionally remove Mach-O signature on macOS * refactor: extract shared runtime code into bootstrap-shared.js Move dlopen patching, child_process patching, and process.pkg setup into a shared module used by both the traditional and SEA bootstraps. This eliminates duplication and ensures both modes handle native addons, subprocess spawning, and process.pkg identically. - Replace copyFolderRecursiveSync with fs.cpSync (Node >= 22) - Add REQUIRE_SHARED parameter to packer wrapper for traditional mode - SEA bootstrap consumes shared module via esbuild bundling - Remove dead code (ARGV0, homedir import, copyFolderRecursiveSync) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Copilot review feedback on SEA pipeline - Handle non-numeric nodeRange (e.g. "latest") in enhanced SEA gating - Bump assertSeaNodeVersion to require Node >= 22 matching engines - Guard stepStrip in walker to only strip JS/ESM files in SEA mode, preventing binary corruption of .node addons - Replace blanket eslint-disable in sea-bootstrap.js with targeted no-unused-vars override in eslint config - Wire symlinks through to SEA manifest and bootstrap provider - Document --build-sea Node 25 gating assumption Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: throw when JSON config targets Node < 22 in SEA mode Instead of silently falling back to simple SEA mode when the input is a package.json/config but targets are below Node 22, throw a clear error. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: normalize paths for Windows compatibility in SEA bootstrap * docs: add technical architecture documentation Detailed comparison of traditional pkg mode vs enhanced SEA mode covering build pipelines, binary formats, runtime bootstraps, VFS provider architecture, shared code, performance, and code protection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: normalize all SEA provider paths to POSIX for Windows compatibility The SEA manifest uses POSIX keys but VFS passes platform-native paths on Windows. Normalize all paths to POSIX in the SEAProvider before manifest lookups, SEA asset lookups, and MemoryProvider storage. Remove the now- redundant symlink normalization block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: mention --no-bytecode flag in architecture comparison Explains that traditional mode with --no-bytecode produces a similar code protection profile to enhanced SEA (plaintext source), while still retaining compression and custom VFS format advantages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Copilot review feedback for SEA enhanced mode - Replace fs.cpSync with recursive copy using patched fs primitives so native addon extraction works through VFS in SEA mode - Use toPosixKey instead of snapshotify for symlink manifest keys to match the key format used by directories/stats/assets - Make assertSeaOutput log explicit skip on unsupported platforms instead of silently passing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add DEBUG_PKG diagnostics to SEA mode, deduplicate diagnostic code Move VFS tree dump and fs call tracing from prelude/diagnostic.js into bootstrap-shared.js as installDiagnostic(). Both bootstraps now share the same diagnostic implementation. Security: diagnostics are only available when built with --debug / -d. In SEA mode this is controlled via manifest.debug flag set at build time; without it, installDiagnostic is never called. - Delete prelude/diagnostic.js (replaced by shared installDiagnostic) - Packer injects small DICT-dump snippet + shared call when --debug - SEA bootstrap gates on manifest.debug before calling installDiagnostic - Fix assertSeaNodeVersion: restore check to Node >= 20 (simple SEA) - Fix withSeaTmpDir: restore tmpDir cleanup (was commented out) - Update architecture docs with diagnostic usage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address remaining Copilot review feedback - Bump assertSeaNodeVersion minimum from Node 20 to 22 (consistent with engines) - Extract generateSeaBlob() helper with --build-sea fallback for Node 25.x - Accept separate defaultEntrypoint in setupProcessPkg for process.pkg compat - Update ARCHITECTURE.md Simple SEA min Node from 20 to 22 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add worker thread support and fix SEA walker warnings Worker thread support: - Extract VFS setup into shared sea-vfs-setup.js (used by both main and worker threads, no duplication) - Bundle sea-worker-entry.js separately via esbuild — workers get the same @platformatic/vfs module hooks as the main thread - Monkey-patch Worker constructor to intercept /snapshot/ paths and inject the bundled VFS bootstrap via eval mode - Add test-90-sea-worker-threads test Walker fix: - Skip stepDetect (Babel parser) for non-JS files in SEA mode — only run on .js/.cjs/.mjs files, matching the existing stepStrip guard. Eliminates thousands of spurious warnings on .json, .d.ts, .md, .node files in real-world projects. Build: - Extract build:sea-bootstrap into scripts/build-sea-bootstrap.js for two-step bundling (worker entry → string, then main bootstrap) TODO: Remove node_modules/@platformatic/vfs patches once platformatic/vfs#9 is merged and released. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: update architecture documentation to include worker thread support details * fix: mount VFS at POSIX /snapshot on all platforms for Windows compat The VFS mount point must always be POSIX '/snapshot' because @platformatic/vfs internally uses '/' as the path separator. The Windows prototype patches convert C:\snapshot\... and V:\snapshot\... to /snapshot/... before they reach the VFS. This was broken during the sea-vfs-setup.js extraction where SNAPSHOT_PREFIX was incorrectly set to 'C:\snapshot' on Windows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use Module._compile for worker threads instead of raw eval Worker threads in eval mode get a synthetic require() that doesn't honour module.filename for relative path resolution. This caused require('./lib/helper.js') to fail inside workers. Fix by creating a proper CJS module context via Module._compile(), which sets up the correct resolution base path from the worker's snapshot filename. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use postject JS API instead of npx to inject SEA blobs Replace `npx postject` shell invocations with the postject JS API (`inject()` function) imported as a direct dependency. This fixes two CI issues: 1. "Text file busy" — concurrent npx invocations race on the shared npx cache when building multiple platform targets in parallel 2. "Argument is not a constructor" — npx downloading an incompatible postject version at runtime Using the JS API is also faster (no npm download/extraction overhead) and more reliable (deterministic version from package.json). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: avoid eager file loading and unnecessary temp files in SEA walker - Narrow SEA mode read condition to JS/ESM files only, preventing binary assets (.node, images) from being loaded into memory - Add bodyModified flag to FileRecord to track intentional modifications (patches, package.json rewrites) vs mere reads - Use bodyModified in generateSeaAssets instead of body != null so unmodified files reference source paths directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pass defaultEntrypoint in SEA bootstrap for API consistency Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add snapshotify function to convert file paths to snapshot paths * docs: fix version comments and Node 22 requirement notes - Clarify --build-sea is available from Node 25.5+, not 25.0 - Add JSDoc note explaining why SEA requires Node 22+ despite node:sea being stable from Node 20 - Clarify node:sea row in ARCHITECTURE.md ecosystem table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: reuse replaceSlashes from common.ts instead of duplicate toPosixKey Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: handle VFS-incompatible fs calls in SEA bootstrap Two fixes for the SEA runtime bootstrap: 1. Replace fs.copyFileSync with readFileSync+writeFileSync in the dlopen patch's cpRecursive helper. copyFileSync is not routed through the VFS in overlay mode, causing ENOENT when extracting native addon packages from the snapshot. 2. Wrap fs.realpathSync in try-catch in the diagnostic dumpLevel function. realpathSync can fail on VFS paths, crashing the DEBUG_PKG tree dump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add SKILL.md for packaging performance benchmarks comparison * perf: optimize SEA provider startup and add DEBUG_PKG_PERF instrumentation Optimize the SEAProvider hot paths for large projects (~20% faster startup): - Override internalModuleStat() with O(1) manifest lookup, bypassing the MemoryProvider tree walk (~30K calls saved for zwave-js-ui) - Override statSync() to return lightweight stat objects from the manifest - Cache readFileSync() results in a flat Map, bypassing MemoryProvider write+read roundtrip. Returns Buffer copies to prevent cache corruption. - Override existsSync() with pure manifest lookup Add DEBUG_PKG_PERF=1 runtime instrumentation (works on any SEA binary, no --debug build required). Reports phase timings (manifest parse, directory tree init, module loading) and provider counters (files loaded, stat/exists/readdir calls, sea.getRawAsset cumulative time). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: split project instructions into modular .claude/rules files CLAUDE.md referenced .github/copilot-instructions.md via a markdown link, but Claude Code doesn't follow links — it only auto-loads CLAUDE.md and .claude/rules/*.md. Created 5 focused rule files with path-scoping so Claude Code actually sees the project instructions. Copilot instructions file is unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: enhance SEA asset generation with single archive blob and offsets for zero-copy access * docs: update README and ARCHITECTURE.md for single archive blob and refactored VFS - README: fix stale node14 references, add dedicated SEA Mode section with simple vs enhanced variants, link to ARCHITECTURE.md - ARCHITECTURE: rewrite SEA binary format to document single __pkg_archive__ blob with offsets map instead of per-file assets, document sea-vfs-setup.js as shared VFS core, rewrite worker thread section (workers now reuse same @platformatic/vfs via sea-vfs-setup.js), fix all line counts, update VFS provider diagram and performance/code-protection tables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: enable model invocation for performances-compare skill * refactor: replace @platformatic/vfs with @roberts_lando/vfs@0.3.0 Swap the VFS polyfill dependency from @platformatic/vfs to @roberts_lando/vfs@0.3.0 across all source, prelude, scripts, and documentation files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: update Node.js version requirements and testing commands in documentation * feat: track body modifications in stepStrip for enhanced record handling * chore: update @roberts_lando/vfs to version 0.3.1 in package.json, package-lock.json, and yarn.lock * chore: update @roberts_lando/vfs to version 0.3.2 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Copilot review — patchDlopen regression, SEA blob fallback, CRLF normalization - Restore unconditional native addon copy in patchDlopen with per-file SHA-256 checksums, matching original vercel/pkg behavior (PRs vercel#1492, vercel#1611). The existsSync guard on destFolder was a regression — OS cleanup can delete files inside the cache while leaving the directory intact. - Narrow generateSeaBlob fallback to only catch unsupported --build-sea flag errors (exit code 9 / "bad option"), rethrow real failures. - Normalize CRLF in assertSeaOutput and test-00-sea for Windows compat. Uncomment previously disabled Windows SEA assertion. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: use assertSeaOutput helper in test-00-sea Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Copilot review — dlopen VFS, docs, prettierignore - bootstrap-shared.js: standalone addon branch now uses writeFileSync with the already-read moduleContent (reuses hash too), so extraction works through VFS in SEA mode instead of calling copyFileSync. - bootstrap-shared.js: extract cpRecursive helper out of patchDlopen for readability. - docs/ARCHITECTURE.md: drop stale diagnostic.js reference and clarify that installDiagnostic ships in the bundle but is only invoked when the binary is built with --debug. - .prettierignore: ignore generated prelude/sea-bootstrap.bundle.js so post-build lint stays clean. * feat(sea): support ESM entrypoints with top-level await Split the SEA bootstrap into a shared core plus two thin wrappers so ESM entrypoints work on any supported Node.js target: - `sea-bootstrap-core.js` — VFS mount, shared patches, worker interception, diagnostics. Exports `{ manifest, entrypoint }` with no entry execution. - `sea-bootstrap.js` (CJS) — `Module.runMain()` for CJS entries; dynamic `import(pathToFileURL(entry))` fallback for ESM entries on Node < 25.7. - `sea-bootstrap-esm.js` (ESM) — static `import core` + top-level `await import(entry)` for Node >= 25.7 with `mainFormat: "module"`. `lib/sea.ts` picks the right bundle and sea-config based on entry format and the smallest target Node major. When the ESM-on-old-Node fallback is used, a build-time `log.warn` explains the limitations (one microtask delay, no sync require of ESM-with-TLA deps) and points at Node 25.7+ as the fix. No runtime warning is emitted. Also refresh the symlink + read-once-Buffer improvements to `cpRecursive` in bootstrap-shared.js (prevents infinite recursion and unwanted content escape on addon packages with symlinks; halves I/O on the hash-mismatch path). Add tests: - `test-91-sea-esm-entry` — ESM entry with relative `.mjs` imports - `test-92-sea-tla` — ESM entry using top-level await Update `docs/ARCHITECTURE.md` with the dual-wrapper layout, the bootstrap-selection table, the TLA section, and the 3-step esbuild flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(readme): document ESM entry + top-level await in SEA mode Clarify the enhanced SEA bullet list: ESM entries work on any supported Node target (native on Node >= 25.7 via mainFormat:"module", dynamic import() fallback on Node 22-25.6 with a build-time warning). Also note that DEBUG_PKG diagnostics are available but only when built with --debug. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(sea): harden SEA pipeline — CJS bootstrap for ESM+TLA, blob generator selection, cross-plat - Always use CJS bootstrap: Node 25.5+ embedder dynamic-import callback only resolves builtins (nodejs/node#62726), so native ESM SEA main cannot import the user entrypoint. ESM entries are dispatched through a vm.Script compiled with USE_MAIN_CONTEXT_DEFAULT_LOADER, which routes dynamic import() to the default ESM loader and supports top-level await. Drop the now-unused sea-bootstrap-esm.js and its bundle step. - Pick SEA blob generator binary by host/target major: use process.execPath when host major matches minTargetMajor (works for cross-platform builds), else the downloaded target binary (works cross-major when host can exec it). Restores cross-platform same-major SEA builds. - Split host/target node version checks: assertHostSeaNodeVersion covers the pkg host, resolveMinTargetMajor validates targets separately. - Reject useSnapshot:true in enhanced SEA mode — incompatible with the VFS bootstrap (snapshot build has no __pkg_archive__ asset). - Trust in-memory body in sea-assets when present: walker invariant guarantees body equals shippable bytes in seaMode (ESM→CJS and type:module rewrites are gated on !seaMode). Removes bodyModified flag and a redundant disk re-read, and makes the build race-safe against mid-build source edits. - stepStrip leaves record.body untouched on no-op so Buffer identity is preserved for the sea-assets reuse path. - perf.finalize() moved into each bootstrap dispatcher's finally block so module-loading timings reflect real entrypoint completion (including async / top-level-await apps) and still print on thrown entrypoints. Error paths set process.exitCode instead of process.exit() so the finally runs. - _resolveSymlink now throws ELOOP instead of returning the last hop, matching fs semantics for broken symlink cycles. - insideSnapshot refactored to a prefix table; behavior unchanged. - Share public / no-dict / publicPackages WalkerParams between the SEA and traditional pipelines in lib/index.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(sea): self-review fixes — symlink fallback, archive bounds, short-write loop - bootstrap-shared.js: cpRecursive() falls back to recursive copy when symlinkSync throws EPERM/EACCES on Windows (no admin / dev mode), so native addon extraction no longer aborts silently. - sea-vfs-setup.js: validate manifest offsets before Buffer.subarray() — corrupt or truncated manifests now throw instead of returning silently truncated bytes. Also hoist MAX_SYMLINK_DEPTH to a named const and stop reaching the module-scope provider from perf.finalize(). - sea-assets.ts: writeAll() helper loops on FileHandle.write bytesWritten for both buffer and stream paths, keeping manifest offsets byte-exact even under filesystem backpressure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(sea): align README + ARCHITECTURE with single-bootstrap dispatch ESM/TLA now runs through one CJS wrapper using vm.Script + USE_MAIN_CONTEXT_DEFAULT_LOADER on every Node >= 22 target. Native ESM SEA main (mainFormat:"module") is not used — nodejs/node#62726 blocks embedder dynamic-import of the user entry. Also documents useSnapshot incompatibility, --experimental-sea-config-only blob generation, and the host/target blob generator picker. File reference + ecosystem tables refreshed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * perf(sea): read SEA blob once and share buffer across targets bake() previously reloaded the prep blob from disk per target inside Promise.all, causing N redundant reads and N peak buffer copies on multi-target builds. Read once before the loop and pass the shared Buffer into bake(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(sea): address copilot review — mixed-major guard, emitWarning leak, test cleanup - lib/sea.ts: add assertSingleTargetMajor() and call from both sea() and seaEnhanced(). SEA prep blobs are Node-major specific, so mixing majors in one run (e.g. -t node22-linux-x64,node24-linux-x64) silently produced broken executables. Reject up front instead. - prelude/sea-bootstrap.js: restore the original process.emitWarning as soon as the SEA loader warning is suppressed, so user code does not observe a permanently wrapped emitWarning. - test/utils.js: filesAfter() gains { tolerateWindowsEbusy } option that only swallows EBUSY on win32 during cleanup. - test/test-85..92: drop copy-pasted try/catch noop around filesAfter and use the new option. test-91/92 also switch from inlined spawn+CRLF logic to the shared assertSeaOutput helper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 95f4f63 commit daf76c5

68 files changed

Lines changed: 12962 additions & 1260 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/development.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
description: Build, lint, formatting, and TypeScript rules
3+
---
4+
5+
# Development
6+
7+
## Build
8+
9+
```bash
10+
npm run build # Required before testing — compiles lib/ to lib-es5/
11+
npm run start # Watch mode with auto-rebuild
12+
```
13+
14+
## Lint & Format
15+
16+
```bash
17+
npm run lint # Check both ESLint + Prettier
18+
npm run fix # Auto-fix all issues
19+
npm run lint:code # ESLint only
20+
npm run lint:style # Prettier only
21+
```
22+
23+
- Always run `npm run lint` before committing. Fix all issues — never push dirty code.
24+
- Console statements are disallowed in production code but allowed in test files.
25+
26+
## TypeScript
27+
28+
- Strict mode enabled. Target: ES2022. Module system: CommonJS.
29+
- Edit `lib/*.ts` only — never edit `lib-es5/*.js` directly.
30+
- Requires Node.js >= 22.0.0.
31+
32+
## Dependencies
33+
34+
- Keep runtime dependencies minimal — they affect all packaged apps.
35+
- Use exact or caret ranges.
36+
- `pkg-fetch` provides pre-compiled Node.js binaries.

.claude/rules/git-and-pr.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
description: Git workflow, commit conventions, and PR rules
3+
---
4+
5+
# Git & PR Workflow
6+
7+
## CRITICAL: PR Target
8+
9+
- ALWAYS create PRs against `yao-pkg/pkg` (this fork).
10+
- NEVER target `vercel/pkg` — it is archived and read-only.
11+
12+
## Commits
13+
14+
- Use conventional commits: `feat:`, `fix:`, `refactor:`, `test:`, `chore:`, `docs:`
15+
- Default branch: `main`. Tag format: `v${version}`.
16+
- Branch protection requires passing CI checks.
17+
18+
## Before Committing
19+
20+
1. Clean test artifacts from test directories (`*.exe`, `*-linux`, `*-macos`, `*-win.exe`).
21+
2. Run `npm run lint` and fix all issues.
22+
3. Show `git status --short` and get user approval before committing.
23+
24+
## Release
25+
26+
Uses `release-it` with conventional commits (`npm run release`). This runs linting, generates changelog, creates a git tag, pushes to GitHub, and publishes to npm as `@yao-pkg/pkg`.

.claude/rules/platform-and-vfs.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
description: VFS, native addons, cross-compilation, ESM, and platform-specific notes
3+
paths:
4+
- 'prelude/**'
5+
- 'lib/**'
6+
---
7+
8+
# Platform & Virtual Filesystem
9+
10+
## VFS Path Handling
11+
12+
- Packaged apps use `/snapshot/` prefix (or `C:\snapshot\` on Windows).
13+
- Use `__dirname`/`__filename` for snapshot files, `process.cwd()` for runtime fs.
14+
- Path handling differs between packaged and non-packaged execution.
15+
16+
## Native Addons
17+
18+
- Extracted to `$HOME/.cache/pkg/` at runtime. Must match target Node.js version.
19+
- `linuxstatic` target cannot load native bindings.
20+
- Add `.node` files to `assets` if not detected automatically.
21+
22+
## Cross-Compilation
23+
24+
- Bytecode generation requires the target architecture binary.
25+
- Use `--no-bytecode` for cross-arch builds.
26+
- Linux: QEMU for emulation. macOS: Rosetta 2 for arm64 building x64.
27+
28+
## ESM
29+
30+
- Requires `--options experimental-require-module` on Node.js < 22.12.0.
31+
- Check existing dictionary files for package-specific ESM handling.
32+
33+
## Platform Notes
34+
35+
- **Linux**: `linuxstatic` target for max portability.
36+
- **macOS**: arm64 requires code signing (`codesign` or `ldid`).
37+
- **Windows**: `.exe` extension required; native modules must match target arch.

.claude/rules/project.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
description: Project overview, structure, and key files for yao-pkg/pkg
3+
---
4+
5+
# Project Overview
6+
7+
`pkg` packages Node.js projects into standalone executables for Linux, macOS, and Windows. It supports Node.js 22 and newer, virtual filesystem bundling, V8 bytecode compilation, native addons, and compression (Brotli, GZip).
8+
9+
This is `yao-pkg/pkg` — a maintained fork of the archived `vercel/pkg`.
10+
11+
## Repository Structure
12+
13+
- `lib/` — TypeScript source (compiled to `lib-es5/` via `npm run build`)
14+
- `prelude/` — Bootstrap code injected into packaged executables
15+
- `dictionary/` — Package-specific configs for known npm packages
16+
- `test/` — Numbered test directories (`test-XX-name/`)
17+
- `.github/workflows/` — CI/CD (GitHub Actions)
18+
19+
## Key Entry Points
20+
21+
- `lib/index.js` — API entry point
22+
- `lib/bin.js` — CLI entry point
23+
- `prelude/bootstrap.js` — Injected into every packaged executable
24+
- `dictionary/*.js` — Special handling for specific npm packages

.claude/rules/testing.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
description: Test commands, organization, and patterns
3+
paths:
4+
- 'test/**'
5+
---
6+
7+
# Testing
8+
9+
## Commands
10+
11+
```bash
12+
npm run build # Always build first
13+
npm run test:22 # Test with Node.js 22
14+
npm run test:host # Test with host Node.js version
15+
node test/test.js node22 no-npm test-50-* # Run specific test pattern
16+
```
17+
18+
## Organization
19+
20+
- Tests live in `test/test-XX-descriptive-name/` directories (XX = execution order).
21+
- Each test has a `main.js` entry point using utilities from `test/utils.js`.
22+
- `test-79-npm/` — npm package integration tests (only-npm).
23+
- `test-42-fetch-all/` — verifies patches exist for all Node.js versions.
24+
25+
## Writing Tests
26+
27+
1. Create `test/test-XX-descriptive-name/` with a `main.js`
28+
2. Use `utils.pkg.sync()` to invoke pkg
29+
3. Verify outputs, clean up with `utils.filesAfter()`
30+
31+
Test artifacts (`*.exe`, `*-linux`, `*-macos`, `*-win.exe`) must be cleaned from test directories before committing.

0 commit comments

Comments
 (0)