pnpm pack-app
추가된 버전: v11.0.0
실험적 기능
pnpm pack-app은 실험적 기능입니다. 이 명령의 플래그, pnpm.app 구성 스키마, 출력 레이아웃은 향후 릴리스에서 변경될 수 있습니다.
Node.js Single Executable Applications API를 내부적으로 사용하여 CommonJS 진입 파일을 하나 이상의 대상 플랫폼용 독립 실행형 실행 파일로 패키징합니다.
pnpm pack-app --entry <path> --target <triplet> [--target <triplet> ...]
각 대상은 <output-dir>/<target>/ 아래에 실행 파일을 생성합니다(기본값은 dist-app/<target>/). Windows 대상에서는 출력 파일에 .exe 접미사가 붙으며, macOS 출력은 SEA 주입이 기존 코드 서명을 무효화하기 때문에 자동으로 ad-hoc 서명됩니다(macOS 호스트에서는 codesign, Linux 호스트에서는 ldid 사용).
요구 사항#
- SEA 주입을 수행하려면 호스트에서 Node.js v25.5+를 실행해야 합니다. 실행 중인 Node.js 버전이 더 낮거나(또는 내장된 런타임 버전과 일치하지 않는 경우 — SEA blob은 마이너 릴리스 간에 호환되지 않습니다), pnpm이 일치하는 빌더를 자동으로 다운로드합니다.
- Linux에서 macOS 대상을 크로스 컴파일하려면
$PATH에ldid가 필요합니다. Windows 호스트에서는 macOS 출력을 ad-hoc 서명할 수 없으므로 macOS 대상은 macOS 또는 Linux에서 빌드하세요.
지원되는 대상#
대상은 <os>-<arch>[-<libc>] 형식을 사용합니다:
linux-x64,linux-x64-musl,linux-arm64,linux-arm64-musldarwin-x64,darwin-arm64win32-x64,win32-arm64
-musl 접미사는 linux 대상에서만 유효합니다. <os> 세그먼트는 process.platform 값과 일치하므로, 이 플래그는 pnpm의 --os 플래그 및 pnpm-workspace.yaml의 supportedArchitectures.os와 일관됩니다.
알려진 제약 사항#
Intel Mac에서 darwin-x64 바이너리가 비정상 종료됩니다#
--build-sea 주입 단계의 업스트림 Node.js 버그로 인해 Intel Mac에서 darwin-x64 출력이 시작 시 segfault가 발생합니다. LIEF의 x64용 Mach-O 수정 작업은 SEA 세그먼트가 삽입된 후 LC_DYLD_CHAINED_FIXUPS 체인 항목이 오래된 대상을 가리키는 상태로 남기며, 그러면 dyld가 체인 인코딩된 원시 값을 포인터로 역참조하여 사용자 코드가 실행되기 전에 __cxx_global_var_init에서 바이너리가 충돌합니다. 표준 node --build-sea + codesign --sign - 흐름에서 pnpm이 관여하지 않아도 재현됩니다.
Node.js 팀은 x64 macOS가 단계적으로 중단되고 있다는 이유로 이 문제를 수정하지 않기로 했습니다. 서명 관련 우회 방법은 도움이 되지 않습니다 — 손상은 서명 이전의 주입 단계에서 발생하므로 ldid를 codesign으로 바꾸거나(또는 그 반대로) 해도 차이가 없습니다. 다시 서명하면 이미 손상된 바이트에 대해 유효한 서명이 생성됩니다.
추적:
- nodejs/node#62893 — 최소한의
node --build-sea재현 사례 - nodejs/node#59553 — 동일한 근본 원인을 가진 macOS x64의 장기간 지속되는 SEA 테스트 실패
- nodejs/node#60250 — 수정하는 대신 x64 macOS에서 SEA 테스트를 건너뛰는 Node.js
Intel Mac에서 실행되는 CLI를 배포해야 한다면 @yao-pkg/pkg (Mach-O 섹션을 변경하는 대신 바이너리 끝에 덧붙이는 방식)와 같은 비 SEA 도구로 darwin-x64 아티팩트를 빌드하세요. Rosetta는 우회 수단이 아니라는 점에 유의하세요 — x64 → arm64로만 변환하며(Intel 바이너리를 실행하는 Apple Silicon Mac용) 반대 방향은 변환하지 않으므로 Intel Mac에서는 darwin-arm64 빌드를 실행할 수 없습니다.
예시#
Linux와 Windows용을 한 번에 빌드합니다:
pnpm pack-app --entry dist/index.cjs --target linux-x64 --target win32-x64
특정 Node.js 버전을 포함합니다:
pnpm pack-app --entry dist/index.cjs --target linux-x64-musl --runtime node@25.5.0
옵션#
--entry <path>#
실행 파일에 포함할 CJS 진입 파일의 경로입니다. package.json에 pnpm.app.entry가 설정되어 있지 않으면 필수입니다. 위치 인수만 단독으로 지정해도 됩니다(예: pnpm pack-app dist/index.cjs).
--target, -t <triplet>#
빌드할 대상입니다. 여러 번 지정할 수 있습니다. 허용되는 값은 지원되는 대상을 참조하세요. pnpm.app.targets가 설정되어 있지 않으면 필수입니다. CLI에서 전달하면 --target이 구성된 목록을 완전히 대체하므로 호출 시점에 빌드 범위를 좁힐 수 있습니다.
--runtime <spec>#
출력 실행 파일에 포함할 런타임을 <name>@<version> 형식으로 지정합니다(예: node@25, node@25.5.0). 현재는 node만 지원되며, <name>@ 접두사는 향후 런타임(bun, deno)을 위한 여지를 남겨 둡니다. 버전은 >= v25.5여야 합니다(--build-sea를 지원하는 최소 버전). 기본값은 실행 중인 Node.js 버전입니다.
--output-dir, -o <dir>#
빌드된 실행 파일의 출력 디렉터리입니다. 기본값은 dist-app입니다.
--output-name <name>#
출력 실행 파일의 이름입니다(확장자 제외). 기본값은 package.json의 스코프 없는 name입니다(예: @acme/my-cli의 경우 my-cli).
환경설정#
모든 플래그의 기본값은 package.json의 pnpm.app 아래에 설정할 수 있습니다. CLI 플래그가 구성보다 우선합니다:
{
"name": "my-cli",
"pnpm": {
"app": {
"entry": "dist/index.cjs",
"targets": [
"linux-x64",
"linux-arm64",
"darwin-x64",
"darwin-arm64",
"win32-x64"
],
"runtime": "node@25.5.0",
"outputDir": "dist-app",
"outputName": "my-cli"
}
}
}
이 구성이 있으면 pnpm pack-app을 인수 없이 실행할 수 있습니다. CLI에서 --target은 구성된 targets 목록을 대체하며, 빌드 범위를 좁히는 데 유용합니다(예: pnpm pack-app --target linux-x64).