Node.js開発者であれば、 npmとYarnについてはよくご存知でしょう。どちらか一方を使用するという強いこだわりがあるかもしれません。何年もの間、開発者は Node.js パッケージ マネージャー、特に npm を使用する際のディスク ストレージとビルド時間の肥大化に悩まされてきました。
その後、パッケージ ストレージを異なる方法で処理し、ユーザーのスペースを節約し、ビルド時間を短縮するパッケージ マネージャーであるpnpmが登場しました。pnpm ではその違いを次のように説明しています。
「パッケージをインストールすると、それをマシン上のグローバル ストアに保存し、コピーするのではなく、そこからハード リンクを作成します。モジュールの各バージョンに対して、ディスク上に保存されるコピーは 1 つだけです。たとえば、npm または yarn を使用する場合、lodash を使用するパッケージが 100 個あると、ディスク上に lodash のコピーが 100 個あることになります。pnpm を使用すると、ギガバイト単位のディスク領域を節約できます。」
pnpm が人気を集め、ますます多くの開発者がパッケージ マネージャーとして選択していることは驚くことではありません。採用率の上昇に伴い、Heroku でアプリを実行している多くの開発者 (私のように) が pnpm のサポートを望んでいました。
幸いなことに、pnpm は Node.js とともに配布されるCorepack経由で利用できます。そのため、2024 年 5 月現在、 pnpm は Heroku で利用できるようになります。
この記事では、Heroku で pnpm を使い始めるために必要なことについて説明します。また、pnpm を使用することで得られるストレージとビルド時間のメリットについても紹介します。
pnpm は、Node.js パッケージ マネージャーの長年の課題である冗長なストレージと依存関係処理の非効率性を解決するために作成されました。npm と Yarn は依存関係を各プロジェクトのnode_modules
にコピーします。対照的に、pnpm はすべてのプロジェクトのすべてのパッケージを単一のグローバル ストアに保持し、これらのパッケージをコピーするのではなく、ハード リンクを作成します。これは何を意味するのでしょうか。
lodash
使用する Node.js プロジェクトがあると仮定します。当然、プロジェクトにはnode_modules
フォルダーと、ファイルが入ったlodash
というサブフォルダーがあります。正確には、 [email protected]
は 639 個のファイルがあり、 fp
という別のサブフォルダーにはさらに 415 個のファイルがあります。
lodash
だけで 1,000 を超えるファイルがあります。
私は Node.js プロジェクトを 6 つ作成しました。2 つは pnpm、2 つは npm、2 つは Yarn です。それぞれlodash
を使用しています。lodash lodash
関係フォルダー内のファイルの 1 つに関する情報を見てみましょう。
~/six-projects$ ls -i npm-foo/node_modules/lodash/lodash.js 14754214 -rw-rw-r-- 544098 npm-foo/node_modules/lodash/lodash.js ~/six-projects$ ls -i npm-bar/node_modules/lodash/lodash.js 14757384 -rw-rw-r-- 544098 npm-bar/node_modules/lodash/lodash.js ~/six-projects$ ls -i yarn-foo/node_modules/lodash/lodash.js 14760047 -rw-r--r-- 544098 yarn-foo/node_modules/lodash/lodash.js ~/six-projects$ ls -i yarn-bar/node_modules/lodash/lodash.js 14762739 -rw-r--r-- 544098 yarn-bar/node_modules/lodash/lodash.js ~/six-projects$ ls -i pnpm-foo/node_modules/lodash/lodash.js 15922696 -rw-rw-r-- 544098 pnpm-foo/node_modules/lodash/lodash.js ~/six-projects$ ls -i pnpm-bar/node_modules/lodash/lodash.js 15922696 -rw-rw-r-- 544098 pnpm-bar/node_modules/lodash/lodash.js
lodash.js
ファイルのサイズは 0.5 メガバイト強です。ソフト リンクが表示されていないため、一見すると、各プロジェクトにこのファイルの独自のコピーがあるように見えます。しかし、実際にはそうではありません。
lodash.js
ファイルのinode を表示するために、 -i
フラグ付きのls
使用しました。pnpm pnpm-foo
プロジェクトとpnpm-bar
プロジェクトでは、両方のファイルに同じ inode ( 15922696
) があることがわかります。これらは同じファイルを指しています。これは npm や Yarn には当てはまりません。
したがって、npm または Yarn を使用するプロジェクトが 12 個あり、それらのプロジェクトがlodash
使用している場合、 lodash
の異なるコピーが 12 個あるだけでなく、それらのプロジェクト自体がlodash
使用する他の依存関係からのコピーも存在することになります。pnpm では、この特定のバージョンのlodash
必要とするすべてのプロジェクトと依存関係は、同じ単一のグローバル コピーを指します。
[email protected]
のコードのサイズは 5 MB 未満です。マシンに 100 個の冗長コピーを用意するか、それとも 1 つのグローバル コピーだけを用意するか、どちらが良いでしょうか。
結局のところ、pnpm を使用した依存関係のインストールは大幅に高速化され、必要なディスク容量とリソースが少なくなります。複数のプロジェクトで作業したり、クラウド プラットフォームで依存関係を管理したりする開発者にとって、pnpm はパッケージを管理するためのより効率的で高速な方法を提供します。そのため、pnpm は Heroku のような合理化された展開環境に最適です。
使い始める準備はできていますか? 使い方を順を追って説明しましょう。
私たちのマシンで使用している Node.js のバージョンは次のとおりです。
$ node --version v20.18.0
上で述べたように、Corepack は Node.js に付属しているので、 corepack
使用して pnpm を有効にして使用するだけです。プロジェクト用のフォルダーを作成します。次に、次のコマンドを実行します。
~/project-pnpm$ corepack enable pnpm ~/project-pnpm$ corepack use pnpm@latest Installing [email protected] in the project... Already up to date Done in 494ms
これにより、次のようなpackage.json
ファイルが生成されます。
{ "packageManager": "[email protected]+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228" }
これにより、 pnpm-lock.yaml
ファイルも生成されます。
次に、プロジェクトに依存関係を追加します。デモ用に、 GitHub の ベンチマークpackage.json
ファイルにあるdependencies
とdevDependencies
のリストをコピーします。これで、 package.json
ファイルは次のようになります。
{ "version": "0.0.1", "dependencies": { "animate.less": "^2.2.0", "autoprefixer": "^10.4.17", "babel-core": "^6.26.3", "babel-eslint": "^10.1.0", ... "webpack-split-by-path": "^2.0.0", "whatwg-fetch": "^3.6.20" }, "devDependencies": { "nan-as": "^1.6.1" }, "packageManager": "[email protected]+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228" }
次に、パッケージをインストールします。
~/project-pnpm$ pnpm install
pnpm の使い方は npm や yarn とかなり似ているため、直感的に理解できるはずです。以下は、一般的なコマンドのさまざまな使い方を比較した表です ( この投稿から抜粋)。
ここまで、pnpm を使用してプロジェクトを立ち上げて実行する方法を紹介しました (とても簡単ですよね)。次に、Heroku で実行する場合のさまざまなパッケージ マネージャーのビルド時間を比較します。npm、Yarn、pnpm を使用して、同一の依存関係を持つ 3 つのプロジェクトをセットアップしました。
まず、Heroku CLI ( heroku login
) にログインします。
次に、プロジェクト用のアプリを作成します。npm プロジェクトの手順を示します。
~/project-npm$ heroku apps:create --stack heroku-24 npm-timing Creating ⬢ npm-timing... done, stack is heroku-24 https://npm-timing-5d4e30a1c656.herokuapp.com/ | https://git.heroku.com/npm-timing.git
Heroku ログのビルド手順にタイムスタンプを追加して、プロジェクトの実際のビルド時間を計算できるビルドパックを見つけました。そのビルドパックをプロジェクトに追加し、 Node.js の標準ビルドパックの前に実行します。そのためには、次の 2 つのコマンドを実行します。
~/project-npm$ heroku buildpacks:add \ --index=1 \ https://github.com/edmorley/heroku-buildpack-timestamps.git \ --app pnpm-timing ~/project-npm$ heroku buildpacks:add \ --index=2 heroku/nodejs \ --app npm-timing Buildpack added. Next release on npm-timing will use: 1. https://github.com/edmorley/heroku-buildpack-timestamps.git 2. heroku/nodejs Run git push heroku main to create a new release using these buildpacks.
これで完了です。次に、npm で管理されているプロジェクトのコードをプッシュします。
~/project-npm$ git push heroku main ... remote: Updated 4 paths from 5af8e67 remote: Compressing source files... done. remote: Building source: remote: remote: -----> Building on the Heroku-24 stack remote: -----> Using buildpacks: remote: 1. https://github.com/edmorley/heroku-buildpack-timestamps.git remote: 2. heroku/nodejs remote: -----> Timestamp app detected remote: -----> Node.js app detected ... remote: 2024-10-22 22:31:29 -----> Installing dependencies remote: 2024-10-22 22:31:29 Installing node modules remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 added 1435 packages, and audited 1436 packages in 11s remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 184 packages are looking for funding remote: 2024-10-22 22:31:41 run `npm fund` for details remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 96 vulnerabilities (1 low, 38 moderate, 21 high, 36 critical) remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 To address issues that do not require attention, run: remote: 2024-10-22 22:31:41 npm audit fix remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 To address all issues possible (including breaking changes), run: remote: 2024-10-22 22:31:41 npm audit fix --force remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 Some issues need review, and may require choosing remote: 2024-10-22 22:31:41 a different dependency. remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 Run `npm audit` for details. remote: 2024-10-22 22:31:41 npm notice remote: 2024-10-22 22:31:41 npm notice New minor version of npm available! 10.8.2 -> 10.9.0 remote: 2024-10-22 22:31:41 npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.9.0 remote: 2024-10-22 22:31:41 npm notice To update run: npm install -g [email protected] remote: 2024-10-22 22:31:41 npm notice remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 -----> Build remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 -----> Caching build remote: 2024-10-22 22:31:41 - npm cache remote: 2024-10-22 22:31:41 remote: 2024-10-22 22:31:41 -----> Pruning devDependencies remote: 2024-10-22 22:31:44 remote: 2024-10-22 22:31:44 up to date, audited 1435 packages in 4s remote: 2024-10-22 22:31:44 remote: 2024-10-22 22:31:44 184 packages are looking for funding remote: 2024-10-22 22:31:44 run `npm fund` for details remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 96 vulnerabilities (1 low, 38 moderate, 21 high, 36 critical) remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 To address issues that do not require attention, run: remote: 2024-10-22 22:31:45 npm audit fix remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 To address all issues possible (including breaking changes), run: remote: 2024-10-22 22:31:45 npm audit fix --force remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 Some issues need review, and may require choosing remote: 2024-10-22 22:31:45 a different dependency. remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 Run `npm audit` for details. remote: 2024-10-22 22:31:45 npm notice remote: 2024-10-22 22:31:45 npm notice New minor version of npm available! 10.8.2 -> 10.9.0 remote: 2024-10-22 22:31:45 npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.9.0 remote: 2024-10-22 22:31:45 npm notice To update run: npm install -g [email protected] remote: 2024-10-22 22:31:45 npm notice remote: 2024-10-22 22:31:45 remote: 2024-10-22 22:31:45 -----> Build succeeded! ...
Build succeeded
メッセージが表示されるまで、次の手順のタイミングを確認しました。
Installing dependencies
Build
Pruning devDependencies
Caching build
合計で、npm を使用した場合、このビルドには 16 秒かかりました。
タイミング ビルドパックを使用して、pnpm 管理プロジェクトに対しても同じセットアップを実行しました。
~/project-pnpm$ heroku apps:create --stack heroku-24 pnpm-timing ~/project-pnpm$ heroku buildpacks:add \ --index=1 \ https://github.com/edmorley/heroku-buildpack-timestamps.git \ --app pnpm-timing ~/project-pnpm$ heroku buildpacks:add \ --index=2 heroku/nodejs \ --app pnpm-timing ~/project-pnpm$ git push heroku main … remote: 2024-10-22 22:38:34 -----> Installing dependencies remote: 2024-10-22 22:38:34 Running 'pnpm install' with pnpm-lock.yaml … remote: 2024-10-22 22:38:49 remote: 2024-10-22 22:38:49 dependencies: remote: 2024-10-22 22:38:49 + animate.less 2.2.0 remote: 2024-10-22 22:38:49 + autoprefixer 10.4.20 remote: 2024-10-22 22:38:49 + babel-core 6.26.3 … remote: 2024-10-22 22:38:51 -----> Build succeeded!
pnpm を使用した同じビルドでは、わずか 7 秒しかかかりませんでした。
時間の節約は、最初のインストールだけではありません。依存関係キャッシュを使用する後続のビルドも、pnpm を使用すると高速になります。
Node.js 開発を始めた当初は npm を使用していました。数年前に Yarn に切り替えて、最近までそれを使用していました。現在は pnpm に切り替えました。ローカル マシンでは、かなりのディスク領域を解放できます。ビルドも高速化しました。さらに、Heroku が pnpm をサポートしたため、ループが閉じられ、ローカル開発からクラウドへの展開まで、pnpm のみを使用できるようになりました。
楽しいコーディングを!