📌

pindeps - package.json のバージョン指定を完全固定する

に公開

モニクル Advent Calendar 2025 18日目の記事です。

https://adventar.org/calendars/12446

最近、npmのライブラリでよく脆弱性が発見され、依存関係のバージョンを確認する機会が増えました。

DependabotSnykなどを導入していれば脆弱性のアラートが届きますが、使っているパッケージマネージャーが対応していない場合や、ライブラリの特定の機能が使えるかどうかでバージョンを確認したい際に、package.jsonのバージョン指定が曖昧(^~)だと正確なバージョンを特定するのが面倒だと感じました。

この記事ではpackage.jsonpnpm-workspace.yamlのバージョン指定をロックファイルを解析して完全固定して、表層の依存関係のバージョンを見やすくするためのCLIツールを紹介します。

https://github.com/ryuapp/pindeps

npx pindeps@latest
package.json
{
  "dependencies": {
-   "pindeps": "^0.2.0"
+   "pindeps": "0.4.0"
  }
}

なぜバージョン指定を完全固定するのか

次のようなpackage.jsonがある場合、reactのバージョンはいくつでしょうか?

package.json
{
  "dependencies": {
    "react": "^19.1.0"
  }
}

ここから分かることは19.1.0以上、20.0.0未満のバージョンがインストールされている可能性がある、ということだけです。

実際のバージョンを確認するには使用しているパッケージマネージャーのロックファイル(package-lock.jsonpnpm-lock.yamlなど)を参照するか、npm outdatedpnpm outdatedのようなコマンドで確認する必要があります。

では、次のpackage.jsonの場合はどうでしょうか?

package.json
{
  "dependencies": {
    "react": "19.2.0"
  }
}

バージョンを完全固定した場合は、ロックファイルを参照したりコマンドの実行をすることなく、package.jsonを見ただけでreactのバージョンが分かるようになります。

^~などのSemVerに従ったバージョン指定は、配布を前提としたライブラリでない限り、ロックファイルでバージョンが管理されているため、意味がないケースが多く、むしろバージョンの特定が難しくなるデメリットの方が大きいと考えています。

そのため、配布を前提としないソフトウェア開発を行っている場合、バージョンは視認性の観点から完全固定した方が良いと考えています。

詳細なメリット、デメリットは以下の記事が参考になるので、詳しく知りたい方はご覧ください。

https://zenn.dev/nekoya/articles/c6057fbb896391

https://docs.renovatebot.com/dependency-pinning/

では、途中でバージョンを完全固定したい場合はどうすればいいでしょうか?
ロックファイルやコマンドでバージョンを確認して、手作業でpackage.jsonを書き換えるのは面倒です。

そこで、その作業を自動で行ってくれるツールとしてpindepsを作成しました。

pindepsでできること

pindepsはpackage.jsonpnpm-workspace.yamldeno.json(c)で使用されている依存関係のバージョン指定をロックファイルを解析して完全固定に書き換えます。主要なJavaScriptのパッケージマネージャー全てに対応しています。(古いバージョンは除く)

プロジェクトのルートで次のコマンドを実行するだけで固定できます。

# npm
npx pindeps@latest
# Yarn
yarn dlx pindeps
# pnpm
pnpm dlx pindeps
# Bun
bunx pindeps@latest

# Deno (パーミッションフラグ付き)
deno x -rWR="." pindeps

コマンドを実行した後は使用しているパッケージマネージャーでロックファイルを更新しましょう。
大規模プロジェクトの場合は、内部の依存関係のバージョンも変わる可能性があるため、ロックファイルの差分を確認することをおすすめします。

また、新しくライブラリをインストールする際にバージョン指定が完全固定されるように.npmrcでsave-exactの設定をしておくことをおすすめします。

.npmrc
# npm, pnpm, Bunで有効
save-exact=true

これで新規インストール時にもフラグなしで固定されたバージョンが保存されるようになります。

ライブラリ開発向けの機能

ライブラリの依存(dependencies)のバージョンを固定してしまうと、利用者が柔軟にバージョンを選べなくなり、インストールサイズの肥大化や依存ライブラリに脆弱性が発見されるたびにバージョンを上げて再配布する手間などが生じるため、SemVerを使った柔軟なバージョン指定を行うのが一般的です。

ただし、devDependenciesに関しては利用者側には関係がないため、バージョンを完全固定するメリットが残っています。pindepsでは--devフラグを付けることで、devDependenciesのみバージョンを完全固定することができます。

npx pindeps@latest --dev

まだ実装していませんが、将来的には--checkフラグを追加して、devDependenciesのバージョンが完全固定されているかなどをCI上でチェックできるようにする予定です。

まとめ

  • pindepsはpackage.json内のバージョン指定をロックファイルを解析して完全固定するCLI
  • 完全固定することで依存しているバージョンが分かりやすくなる
  • ライブラリ開発の場合は--devフラグを使ってdevDependenciesのみを固定することも可能

今後は細かいバグ修正や、ライブラリ開発向けの機能を拡充しようと考えています。
バグや要望等あればGitHubのissuesで報告いただけますと幸いです。

https://github.com/ryuapp/pindeps

謝辞

記事とライブラリ名は以下の記事からインスピレーションを受けたものです。
この場をお借りして感謝申し上げます。

https://zenn.dev/shunsuke_suzuki/articles/pinact-pin-github-actions-version

株式会社モニクル

Discussion