📦
Node.js 向けパッケージマネージャ兼ワークスペース管理ツール pnpm を箇条書きで解説
対象読者
-
pnpm
を使ってみたい方、興味がある方、入門したい方。 -
npm
、yarn
やlerna
よりもワークスペース管理をもっと簡単にしたい方。 -
npm
、yarn
のインストール速度が遅く感じる、もしくは容量を多く取られて困ると感じている方。
前提知識
pnpm とは
-
yarn
やlerna
のようなnpm
の代替ツールです。 - 組み込みでワークスペース (モノリシックリポジトリ) へのサポートがあります。
- より厳格に依存を管理できます。
-
node_modules/
の構造に仕掛けがあります。 - 依存の依存、のようなものに対し、
pnpm
プロジェクトからアクセスできないようにしています。 - (例外) 一部の
prettier
やeslint
などのツールのプラグインはpublic-hoist-pattern
設定で除外することができます。 - (例外) 上記含めた、有名なプラグインはデフォルトで除外されています。
- (例外) この挙動は、設定をすることで最初はゆるくして徐々に強くしていくことも可能です。
- 更に詳しい解説は pnpm 作者ブログの Flat node_modules is not the only way (日本語訳) も参照してみてください。
-
- パッケージインストールはハードリンクで実現されます。
- あらゆるケースで
npm
,yarn
, Yarnのプラグアンドプレイ より速く、これらは基本コピーで実現されているので、容量も節約できます。 - グローバルストア (
~/.pnpm-store
) という場所で一括管理されます。 - (応用)
git
のように、ハッシュ値をもとに、ファイル単位でキャッシュされます。 - (応用)
pnpm store status
を用いて変更を誤って加えるなどの不整合が起きていないか検証できます。
- あらゆるケースで
- 誰が使っている?
- 誰が使ってるの ? - pnpm.io - Svelte Kit や最近では Vue.js、Vite なども
-
pnpm/pnpm によると、Microsoft の Rush チームが
pnpm
を数百のプロジェクトで使っているとのことです。
インストール
-
npm
がある場合はnpm i -g pnpm
-
npm
がなくても使えます。その他の方法は インストール を参照してください。 -
GitHub Actions 向けには pnpm/action-setup が使えます。
- キャッシュを効かせる方法については この記事.他のツールとの連携.GitHub Actions を参照してください。
基本的な使い方
-
pnpm
コマンドのみを使います。npm
,yarn
,lerna
を使う必要はありません。 - (応用)
pnpx
は非推奨になりました。pnpm exec
とpnpm dlx
を使います。 - (細かい)
devDepedencies
にpnpm
自体は 入れない ものしか見たことがありません。グローバルインストールしておくことになるでしょう。 -
pnpm
自体の更新もpnpm i -g pnpm
で可能です。 - パッケージインストールは
npm
と同様の方法で、pnpm add
/pnpm i
を用いて行なえます。 - ロックファイルは
pnpm-lock.yaml
になります。package-lock.json
は生成されず、無視されます。
ワークスペース
ワークスペースになる条件
-
pnpm-workspace.yaml
を設置することです。
ワークスペースのディレクトリ構造の例
▾ <ワークスペースのルート>/
▾ packages/
▸ core/
▸ cli/
▾ doc/
▾ node_modules/
▸ .bin/
▸ src/ # など
package.json
▸ node_modules/
pnpm-workspace.yaml
pnpm-lock.yaml
.npmrc # 必須ではない
package.json
- (例外) shared-workspace-lockfile を変更すると
pnpm-lock.yaml
は子パッケージ側にも生成できます。
pnpm-workspace.yaml
の例
packages:
# packages/ と components/ のすべてのサブディレクトリを含める
- 'packages/**'
- 'components/**'
# test ディレクトリに含まれるすべてのパッケージを除外する
- '!**/test/**'
-
他にもいくつかの ワークスペースレベルでの設定 が可能です。
-
(細かい) ワークスペースルートを明示的に含める必要はありません。
ワークスペース内でのライフサイクル
-
pnpm install
はどこで使っても同じ効果です。-
pnpm install
を行うとすべての子パッケージ内の依存をインストールします。 - 子パッケージの依存を含めたすべての依存の 実体 はワークスペースのルートのバーチャルストア (
node_modules/.pnpm/
) に集約され、そこからシンボリックリンクが子パッケージのnode_modules/
内に貼られます。 - (細かい) バーチャルストア (
.pnpm/
) の中身は、前述したグローバルストアと同じものへのハードリンクです。 - (例外)
recursive-install
設定を使っている場合は、個別にインストールすることもできます。
-
- 作業ディレクトリに依存するコマンドは
--workspace-root
,-w
を指定することでルートから実行できます。 -
pnpm run ...
は現在のディレクトリにあるpackage.json
を見ます。 - すべてのパッケージの特定の名前のスクリプトを実行する方法はいくつかあります。
-
--recursive, -r
を指定する。 - (例外)
"build": "pnpm run build -r"
のように書くとループします。以下で対処できます。 -
フィルタリング を用いる。
-
pnpm run build --filter ./packages
で./packages
配下にある子パッケージ全てで実行します。 -
--filter "*core"
で名前 (=packge.json
の.name
) にcore
と末尾につく子パッケージを指定できます。 - (応用) さらに変更があったパッケージ、及びその依存、といった指定もできます。詳しくはドキュメントを参照してください。
-
-
- ワークスペース内で依存関係を定義するには ワークスペースプロトコル (workspace:) を使用します。
-
package.json
に"@my-org/my-package": "workspace:*"
と書くと同名の子パッケージを依存として加えます。node_modules/
内から対象へ直接シンボリックリンクが張られます。 -
"foo": "workspace:../foo"
のように、相対パスを使うこともできます。 -
pnpm publish
時には、ワークスペースプロトコルを使わない形に変換されます。
-
その他
- モノリポでのリリース管理
-
pnpm と Changesets を組み合わせて使用する ことができます。
- (個人的) こちらも記事にしたい。
- (個人的) Changesets はモノリポでなくとも semantic-release の代替として非常に良いライフサイクルと体験をもたらしてくれると感じました。
-
pnpm と Changesets を組み合わせて使用する ことができます。
- 孫依存含め、複数のバージョン存在している
esbuild
を統一したい- インストール処理でトラブルになるので孫依存に存在しているケースではこの対処をしたほうが良いです。
-
.pnpmfile.cjs
が役に立ちます。
const { esbuild } = require('./package.json').devDependencies; function readPackage(pkg) { for (const section of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) { if (pkg[section].esbuild) { pkg[section].esbuild = esbuild; } } return pkg; } module.exports = { hooks: { readPackage, }, };
- その他、似たような問題が起きる場合は、
.pnpmfile.cjs
を活用することで対処できるかもしれません。
-
pnpm
以外を使用した際にエラーにする方法。- これは非常に役立ちますが、注意点として、公開用のライブラリパッケージでは使用できません。ライブラリ使用者が
npm
を使用していた際にエラーになるからです。"private": true
なら使える、と機械的に判断して良いでしょう。ワークスペースルートでも使えます。 -
only-allow
を使用する方法。 -
engine-strcit
設定をtrue
にしてpackage.json
にengines
項を記述する方法。
; .npmrc engine-strict=true
"engines": { "npm": "forbidden, use pnpm", "pnpm": ">=6", "yarn": "forbidden, use pnpm" }
- これは非常に役立ちますが、注意点として、公開用のライブラリパッケージでは使用できません。ライブラリ使用者が
他のツールとの連携
GitHub Actions
- npm/action-setup でセットアップします。 セットアップ自体に Node.js は必要ありません。
- 参考になりそうなもの:
- キャッシュの例
cache-pnpm: name: Cache Pnpm runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/cache@v2 with: path: ~/.pnpm-store key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | ${{ runner.os }}- - uses: pnpm/action-setup@v2 with: version: 6 run_install: | - recursive: true args: [--frozen-lockfile, --prefer-offline] build: needs: [cache-pnpm] # ...
dependabot
- 執筆時点未対応
- Feature request: support the pnpm package manager dependabot/dependabot-core#1736
- 執筆時点でオープンのままですが、貢献者を探しているようです。
renovate
- 公式対応。ちゃんと動いているのをちらほら見かけました。
npm
,yarn
, andpnpm
are all supported.
引用元: https://docs.renovatebot.com/javascript/
質問等あれば
- ドキュメントの一部を翻訳したりしていますが無関係です。twitter や zenn のコメント欄でよければ何でも聞いてください。
pnpm
や Changesets 、 GitHub Actions との連携など。
PRs are welcome
- リンク切れ、typo 修正、他なんでも、1bit の変更でもお気軽に PR ください。
Discussion