カテゴリー
 【nodejsシェルアプリ開発】vercel/pkgでCLI版スネークゲームを作ってみる  
※ 当ページには【広告/PR】を含む場合があります。
2022/12/23
 2024/08/21 

※本記事で取り上げていた
Node.jsアプリのバイナリ化での移行先として
詳しくは以下の記事を参考にしてみてください。
さて、以前の記事で、
実際のところ、nodejsをバイナリ化してシェルコマンドアプリとしてパッケージするユーティリティは「nexe」だけではなく、以下のnpm trandの比較に見るように、いくつか選択肢があります。
これらのパッケージャには一朝一夕あり、「nexe」はメンテナンス頻度が多く、開発者の活動が活発であることが非常に好ましくありますが、不要なライブラリまでまとめて固めてくれるため、ビルド後のバイナリのサイズはやや大きくなってしまいます。
そこで今回は、非常にきめ細いビルドオプションを指定できる
pkgプロジェクトの準備
では早速、簡単なpkgプログラムを作成していきましょう。
なお以下が手元のnodejs環境です。
            $ node --version
v18.12.1
$ npm --version
9.1.2
$ yarn --version
1.22.19
        適当なプロジェクトフォルダを作って、以下のようにほぼ空の
package.json            {
  "name": "snake_pkg",
  "version": "0.0.1",
  "description": "To execise to use pkg with Snake Game."
}
        次に前回Snake-game用に解説した
index.jsbuildなお
pkgbinpackage.jsonでは
pkg            $ yarn add pkg -D
        ローカルにインストールしたので、
pkgpackage.jsonscriptspkg            {
  "name": "snake_pkg",
  "version": "0.0.1",
  "description": "To execise to use pkg with Snake Game.",
  "bin": "build/index.js",
  "scripts": {
    "pkg": "pkg"
  },
  "devDependencies": {
    "pkg": "^5.8.0"
  }
}
        一度、
pkg            $ yarn pkg -h
  pkg [options] <input>
  Options:
    -h, --help           output usage information
    -v, --version        output pkg version
    -t, --targets        comma-separated list of targets (see examples)
    -c, --config         package.json or any json file with top-level config
    --options            bake v8 options into executable to run with them on
    -o, --output         output file name or template for several files
    --out-path           path to save output one or more executables
    -d, --debug          show more information during packaging process [off]
    -b, --build          don't download prebuilt base binaries, build them
    --public             speed up and disclose the sources of top-level project
    --public-packages    force specified packages to be considered public
    --no-bytecode        skip bytecode generation and include source files as plain js
    --no-native-build    skip native addons build
    --no-dict            comma-separated list of packages names to ignore dictionaries. Use --no-dict * to disable all dictionaries
    -C, --compress       [default=None] compression algorithm = Brotli or GZip
  Examples:
  – Makes executables for Linux, macOS and Windows
    $ pkg index.js
  – Takes package.json from cwd and follows 'bin' entry
    $ pkg .
  – Makes executable for particular target machine
    $ pkg -t node14-win-arm64 index.js
  – Makes executables for target machines of your choice
    $ pkg -t node12-linux,node14-linux,node14-win index.js
  – Bakes '--expose-gc' and '--max-heap-size=34' into executable
    $ pkg --options "expose-gc,max-heap-size=34" index.js
  – Consider packageA and packageB to be public
    $ pkg --public-packages "packageA,packageB" index.js
  – Consider all packages to be public
    $ pkg --public-packages "*" index.js
  – Bakes '--expose-gc' into executable
    $ pkg --options expose-gc index.js
  – reduce size of the data packed inside the executable with GZip
    $ pkg --compress GZip index.js
        公式にも出ているヘルプが表示されました。
pkgとはいえ、コマンドオプションにビルド設定をすべて指定していくのは結構しんどいので、
package.jsonpkg            {
  "name": "snake_pkg",
  "version": "0.0.1",
  "description": "To execise to use pkg with Snake Game.",
  "bin": "build/index.js",
  "scripts": {
    "pkg": "pkg"
  },
  "pkg": {
    "scripts": "build/**/*.js",
    "assets": [
        "assets/**/*",
        "static/**/*"
    ],
    "targets": ["latest-linux-x64"],
    "outputPath": "dist"
  }
  "devDependencies": {
    "pkg": "^5.8.0"
  }
}
        このプロジェクトでは
index.jspkgscripts他に
assetstargetsoutputPathビルドターゲット
公式にも言及されていますが、
pkg            nodeRange:
    (node8)
    node10
    node12
    node14
    node16
    latest
platform:
    alpine
    linux
    linuxstatic
    win
    macos
    (freebsd)
arch:
    x86
    x64
    arm64
        なお、(*)は非サポートですが、おそらく動くだろうというオプションです。
これら3つの要素の組み合わせによる
pkg            [nodeRange]-[platform]-[arch]
        になっています。
pkgのビルド
では先程までで、プロジェクトフォルダのファイル構造(node_modulesは無視)は以下のようになっていると思います。
            $ tree -L 2 -I node_modules
.
├── build
│   └── index.js
├── package.json
└── yarn.lock
        後はもうバイナリビルドするだけで、
package.json            $ yarn pkg .
        すると、
distpackage.json            $ tree -L 2 -I node_modules
.
├── build
│   └── index.js
├── dist
│   └── snake_pkg #👈バイナリファイル
├── package.json
└── yarn.lock
        pkgでビルドしたバイナリファイルの実行
最後に、指定したターゲットの環境で動くかをチェックしてみます。
先程の設定では、

と期待通りに動いています。
nexeバイナリとの比較
最後にnexeバイナリとのビルド後の比較も行いましょう。
nexeでのバイナリビルドに関しては
比較としてx64アーキテクチャのLinux(Debian11)をターゲットとします。
まずはnexeバイナリの方ですが、
            $ du -sSk snake_nexe
72392 snake_nexe
        ということで、約
72.4MBしかも現時点でNexeのビルドターゲットが
linux-x64-14.15.3対して、pkgですが、
            $ du -sSk snake_pkg
45220 snake_pkg
        と約
45.2MBしかも、nexeの良いところは現在の環境のnodejsバージョンに併せてターゲットのライブラリを自動設定してくれていて、ここでは
v18.5.0-linux-x64すでにnode18に対応していて、しかもビルド後のバイナリサイズは小さい、というのがpkgの大きなアドバンテージと言えそうです。