【Node.js】npmとpackage.jsonの基礎|install・scripts・semver・devDependencies

【Node.js】npmとpackage.jsonの基礎|install・scripts・semver・devDependencies Node.js

Node.jsで開発するとき、外部のライブラリ(パッケージ)を使わないことはほぼありません。そのパッケージを管理するのがnpm(Node Package Manager)で、プロジェクトの情報や依存パッケージを記録するのがpackage.jsonです。この2つはNode.js開発の土台です。

基本はnpm installでパッケージを入れるだけですが、dependenciesとdevDependenciesの違い^~といったバージョン表記(semver)の意味package-lock.jsonの役割を知らないと、「同じはずなのに動かない」「バージョンが勝手に上がった」といったトラブルにつながります。この記事では、実機で確認しながら、npmとpackage.jsonの基礎を整理します。

先に結論

  • npm init -ypackage.jsonを作り、npm install パッケージ名で追加します。
  • 本番でも使うものはdependencies、開発時だけ使うものは-Dを付けてdevDependenciesに入れます。
  • バージョンの^3.0.1は「3系の範囲で更新可」、~3.0.1は「3.0系の範囲で更新可」、3.0.1は完全一致です。
  • package.jsonscriptsにコマンドを登録し、npm run 名前で実行します。
  • package-lock.jsonは実際に入った正確なバージョンを記録します。消さずにコミットします。
  • 本番やCIでは、ロックファイル通りに入れるnpm ciを使うと再現性が高まります。

インストールしたパッケージの読み込み方はrequireとimportの違い、Node.js本体のインストールは指定したバージョンをインストールする方法、ファイル操作はfsでファイルを読み書きする方法もあわせて参考になります。

スポンサーリンク

package.jsonとは(プロジェクトの設定ファイル)

package.jsonは、プロジェクトの名前・バージョン・依存パッケージ・実行コマンドなどを記録するファイルです。これがあることで、他の人がnpm installするだけで同じパッケージをそろえられます。

package.json
{
  "name": "my-app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

dependenciesは本番でも必要なパッケージ、devDependenciesは開発時だけ使うパッケージ、scriptsはよく使うコマンドの登録欄です。これらの意味を順に見ていきます。

npm init で作る

新しいプロジェクトでは、まずnpm initpackage.jsonを作ります。-yを付けると、質問をすべて既定値で進めて即座に作成できます。

npm-init.sh
# 質問に答えながら作る
npm init

# すべて既定値で即作成する(-y)
npm init -y

実機でも、npm init -ynameversion1.0.0)・mainindex.js)などを含むpackage.jsonが即座に生成されました。nameversionはあとから手で編集できます。

パッケージを追加する(npm install)

パッケージを追加するにはnpm install パッケージ名を実行します。するとnode_modulesフォルダに実体がダウンロードされ、package.jsondependenciesに名前とバージョンが追記されます。

npm-install.sh
# パッケージを追加(dependencies に入る)
npm install express
npm i express          # i は install の短縮形

# package.json に書かれた依存をまとめて入れる(名前なし)
npm install

# 特定バージョンを指定して入れる
npm install express@4.18.2

# パッケージを削除する
npm uninstall express

実機でも、npm installを実行するとpackage.jsondependenciesにパッケージ名と^3.0.1のようなバージョンが追記され、node_modulespackage-lock.jsonが作られました。名前を付けずにnpm installだけを実行すると、package.jsonに書かれた依存をまとめてインストールします(プロジェクトを受け取ったときの最初の操作です)。

dependencies と devDependencies の違い

パッケージには「本番でも動かすのに必要なもの」と「開発時だけ使うもの」があります。後者は-D(または--save-dev)を付けてdevDependenciesに分けて入れます。

dev-dependencies.sh
# 本番でも使う(dependencies)
npm install express

# 開発時だけ使う(devDependencies)。-D を付ける
npm install -D nodemon
npm install --save-dev jest

実機でも、npm install -Dで入れたパッケージはdependenciesではなくdevDependenciesに分けて記録されました。テストツール(jest)、開発用サーバー(nodemon)、ビルドツールなどはdevDependenciesに入れます。こうしておくと、本番環境でnpm install --omit=devを使ったときに、開発専用パッケージを除いて軽くインストールできます。

【重要】semver:^ と ~ の意味

バージョンの先頭に付く^~は、どこまでの自動更新を許可するかを表します。バージョンはメジャー.マイナー.パッチ(例:3.0.1)の3つの数字でできていて、これをセマンティックバージョニング(semver)と呼びます。

表記 意味 許可される更新(例 3.0.1)
^3.0.1 メジャーを固定(既定) 3.0.1 〜 3.x.x(4.0.0は不可)
~3.0.1 マイナーまで固定 3.0.1 〜 3.0.x(3.1.0は不可)
3.0.1 完全一致 3.0.1 のみ
* すべて どのバージョンでも(非推奨)

npm installすると、既定では^が付きます。^は「互換性が保たれる範囲(メジャーが同じ)なら更新してよい」という意味です。バージョンを厳密に固定したいときは、数字だけを書くか、次に説明するpackage-lock.jsonに任せます。

scripts と npm run

よく使うコマンドはpackage.jsonscriptsに登録すると、npm run 名前で実行できます。長いコマンドを覚えなくてよくなり、チームでも共有できます。

scripts.json
{
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest",
    "build": "node build.js"
  }
}
npm-run.sh
npm run dev      # nodemon index.js が動く
npm run build    # node build.js が動く

# start と test は run を省略できる
npm start        # npm run start と同じ
npm test         # npm run test と同じ

# 登録済みスクリプトの一覧を見る
npm run

実機でも、scriptsに登録したコマンドがnpm run 名前で実行でき、npm runだけで登録済みの一覧が表示されました。starttestは特別で、runを省略してnpm startnpm testと書けます。node_modulesに入れたツール(jestなど)は、scriptsの中ではパスを書かずに名前だけで呼べます。

package-lock.json と npm ci

package.json^3.0.1は「3系ならOK」という範囲の指定です。そのため、いつnpm installするかで実際に入るバージョンが変わることがあります。これを防ぐのがpackage-lock.jsonで、実際に入った正確なバージョンを記録します。

npm-ci.sh
# 通常の install(package.json の範囲で解決し、lock を更新することがある)
npm install

# CI や本番では npm ci(package-lock.json 通りに正確に入れる)
npm ci
package-lock.jsonは消さずにコミットする

実機で確認したところ、package-lock.jsonには実際に入ったパッケージの正確なバージョン(依存の依存まで含む)が記録されていました。このファイルがあることで、誰がいつnpm installしても同じバージョンをそろえられます。package-lock.jsonはGitにコミットし、消さないでください。本番やCIでは、ロックファイル通りに入れるnpm ciを使うと、より確実に同じ環境を再現できます(npm cinode_modulesを作り直すため、まっさらな環境向けです)。

グローバルインストール(-g)とnpx

プロジェクトではなく、パソコン全体でコマンドとして使いたいツールは-g(グローバル)で入れます。一方、一度だけ実行したいツールは、インストールせずにnpxで実行できます。

global-npx.sh
# パソコン全体にコマンドとして入れる
npm install -g typescript

# 一度だけ実行したい(インストール不要)
npx create-react-app my-app

# プロジェクト内のツールを npx で実行する
npx jest

グローバルインストールは便利ですが、プロジェクトごとにバージョンがそろわなくなるため、ビルドやテストのツールはdevDependenciesに入れてnpxnpm runで使うのがおすすめです。npxは、まだ入れていないパッケージも一時的に取得して実行できるため、create-react-appのような「最初の1回だけ使うツール」に向いています。

よくある失敗

package-lock.jsonを削除・無視する

これがあるからこそ全員が同じバージョンをそろえられます。Gitにコミットし、消さないでください。本番ではnpm ciを使います。

開発ツールをdependenciesに入れる

jestやnodemonなど開発時だけ使うものは-DdevDependenciesに入れます。本番のインストールを軽くできます。

node_modulesをGitにコミットする

node_modulesnpm installで復元できるため、コミットしません。.gitignoreに追加します。package.jsonpackage-lock.jsonだけをコミットします。

^と~を理解せずバージョンが上がって動かなくなる

^はメジャーが同じ範囲で更新されます。厳密に固定したいなら数字だけを書くか、npm ciでロックファイル通りに入れます。

npm installとnpm ciを取り違える

開発で依存を追加するときはnpm install、本番やCIで再現したいときはnpm ciです。npm cipackage-lock.jsonが必須です。

よくある質問

QdependenciesとdevDependenciesはどう違いますか?
A本番でも動かすのに必要なものがdependencies、テストやビルドなど開発時だけ使うものがdevDependenciesです。npm install -D パッケージ名devDependenciesに入ります。本番ではnpm install --omit=devで開発用を除けます。
Q^3.0.1 の ^ はどういう意味ですか?
Aメジャーバージョン(先頭の数字)が同じ範囲で更新を許可する指定です。^3.0.1なら3.0.1から3.x.xまで上がり、4.0.0には上がりません。~3.0.1はさらに狭く3.0.xまで、数字だけなら完全一致です。
Qpackage-lock.jsonはコミットすべきですか?
Aはい、必ずコミットします。実際に入った正確なバージョンを記録しており、これがあることで全員が同じ環境を再現できます。逆にnode_modulesはコミットせず、.gitignoreに入れます。
Qnpm installとnpm ciはどう使い分けますか?
A開発でパッケージを追加・更新するときはnpm install、本番やCIでpackage-lock.json通りに正確に再現したいときはnpm ciです。npm ciはロックファイルが必須で、node_modulesを作り直します。
Qグローバル(-g)とローカルのどちらで入れるべきですか?
Aプロジェクトで使うビルドやテストのツールは、バージョンをそろえられるローカル(devDependencies)がおすすめです。パソコン全体でコマンドとして使いたいものだけ-gで入れます。一度きりの実行はnpxが便利です。

まとめ

  • npm init -ypackage.jsonを作り、npm installでパッケージを追加します。
  • 本番用はdependencies、開発用は-DdevDependenciesに分けます。
  • ^はメジャー固定、~はマイナー固定、数字だけは完全一致です。
  • scriptsにコマンドを登録し、npm run 名前で実行します。
  • package-lock.jsonは正確なバージョンの記録。消さずにコミットします。
  • 本番やCIではnpm ciで再現性を高めます。

npmとpackage.jsonは、最初は呪文のように見えますが、「dependenciesdevDependenciesの区別」「^~の意味」「package-lock.jsonをコミットする」の3点を押さえれば、安定したNode.js開発の土台になります。