JavaScriptに関するお知らせ

SINCE2019
>
【JS】npm runとnpxの違い、関係性について
JavaScript

【JS】npm runとnpxの違い、関係性について


こんにちは!npmで自分を管理したい Mizutani(@sirycity) です。「持ってる資格一覧」とか「契約してるサブスクリプション一覧」とかパッと分かると便利そう。

今回はそんなnpmの機能についてです。npm runとnpxについて説明していくよ。

※今回は文字多め。

短い説明

  • npm runはnpmで管理してるライブラリの何かしらのコマンドをpackage.jsonに登録して実行できる機能
  • npxはそれをわざわざ登録しなくても実行できる機能

長い説明

  • むかしのJSはWebのデザインを構成する言語でした。JSが発達してくるとすげーデザインが作れるコードを書いた人が「みんなもこれ使っていいよー」ってコードを公開しました。これがライブラリ。

  • そうやってみんながライブラリを使い始めますが、ライブラリがいっぱいになるとどれ使ってるかわからなくなるので「このライブラリ使ってるよー」みたいなのがわかる機能が欲しくなりました。これがパッケージマネージャー。そう、npmです。そしてnpmの設定ファイルの名前がpackage.json。

  • JSがさらに発達すると「デザイン以外にもJSって使えるんじゃね?」って考える人が現れました。これがNode.js。

  • JSを書いてる人は「もっと効率化したくね?」ってずっと思ってましたが、Node.jsが誕生したことによってついにタスクランナーができました。人間がやる作業を自動化してくれるすげー奴です。タスクランナーもjsでできてるので、npmで管理できます。

  • この辺りでnpmの使い方がちょっと変わってきましたね。npmが「デザインを作るライブラリ」だけじゃなくて、「効率化のためのライブラリ」まで管理するようになりました。この2つは全然用途が違うし区別したいです...こうしてnpmに"dependencies"と"devDependencies"が誕生しました。

  • タスクランナーは当たり前だけど「今から処理して!」みたいなスイッチオンの命令を出さなきゃ駄目ですよね。アプリなわけだし。「じゃあスイッチオンをnpmからできたら便利じゃね?だってタスクランナーはnpmの中にあんじゃん?」って考えた人がいて、npmからパッケージを実行できるnpm scriptが誕生しました。package.jsonの中にある"script"ってやつですね。

  • こうしてJSが急発達してプロジェクトもいっぱいできてくると「いろんなプロジェクトで使いまくるやつを毎回npm scriptに書くの面倒じゃね?」って発想が出てきます。これで誕生したのがnpm globalです。globalに入れておけばどこのプロジェクトでも使えるし、npm scriptも不要です。

  • 「globalだとnpm script書かなくても実行できるの!?じゃあその機能別にlocalにあってもよくね?」ってことで、普通のライブラリもnpm scriptを書かずに直接実行できるようになりました。これがnpx。

以上です。長い。次に実際の使用例を見てみよう。

使用例

npm script

まずはpackage.jsonを見てみよう。こんな感じ(一部略)

{
  "private": true,
  "scripts": {
    "lint": "eslint --fix '**/{*.js,*.jsx,*.ts,*.tsx}'"
  },
  "dependencies": {
    "react": "17.0.1"
  },
  "devDependencies": {
    "typescript": "^4.1.3"
  }
}

今回はscriptの話をするのでscriptだけにしとく。

{
  "scripts": {
    "lint": "eslint --fix '**/{*.js,*.jsx,*.ts,*.tsx}'"
  }
}

ここの左のlintってのをnpm scriptに打つと右側のeslint...ってやつがコマンドラインで実行されるイメージ。ためしに適当なscriptを追加してみますね。

{
  "scripts": {
    "lint": "eslint --fix '**/{*.js,*.jsx,*.ts,*.tsx}'",
    "hello": "echo 'こんにちは!'"
  }
}

この状態でnpm scriptを実行すると...

npm run hello
// こんにちは!

実行できた。こんな感じでnpm scriptは実行できます。↑のESLintも同じことだね。

npm run lint
// ここでESLintがエラーとかないか探す

npm global

ところでこのESLint、もともとはnpmでインストールしたライブラリなわけですね。そりゃそうだ、最初から入ってるわけじゃないからね。こんな感じでインストールしたわけです。

npm install -D eslint

ESLintの場合はこれでOK。でも、世の中にはいろんな場所で使いたいライブラリとかもあるわけです。例えばデプロイをするライブラリとかはいろんな所で使うじゃん?いろんなアプリをデプロイしたいし。そういうライブラリはglobal空間に置いときたいわけです。例としてfirebaseにデプロイするfirebase-toolsとかはこう。

npm install -g firebase-tools

余談だけど、もしESLintを全てのプロジェクトで全く同じ設定で使うならグローバルに入れても良いわけ。こんな風に。

npm install -g eslint

んで、globalに入れたライブラリはnpm scriptは使えないです。だってscript書く所がないもんね。(厳密にはあるんだけど割愛)

なので、npm run とかやらなくてもそのままいきなり実行できちゃう。こんな感じで。

firebase deploy

逆に言えば、もしfirebaseを使ってるプロジェクトが1個しかないなら...

1.こんな風にインストールして、

npm install -D firebase-tools

2.package.jsonに書いて、

{
  "scripts": {
    "deploy": "firebase deploy"
  }
}

3.こうやってnpm scriptで起動!ってこともできるわけ。

npm run deploy

npx

ESLintってコマンドが長いです。こんな風に。

{
  "scripts": {
    "lint": "eslint --fix '**/{*.js,*.jsx,*.ts,*.tsx}'"
  }
}

ところが、ものによってはもっと短いのもあるわけです。例えばtscとかtscだけです。

{
  "scripts": {
    "tsc": "tsc"
  }
}
npm run tsc

なんか、わざわざ書くまでもない感じしますよね。こういうやつを直接実行できるのがnpxです。tscの場合はこう。

npx tsc

その気になればESLintもこうやってできる。

npx eslint '**/{*.js,*.jsx,*.ts,*.tsx}'

だいぶ分かったかな?

対応表

localのnpm scriptとglobalとnpxの対応表はこんな感じ。

{
  "scripts": {
    "AAAAA": "XXXXX"
  }
}
// localのnpm script
npm i -D AAAAA
npm run AAAAA

// global
npm i -g AAAAA
XXXXX

// npx
npm i -D AAAAA
npx XXXXX

さいごに

npmのこの辺難しいけど、理解したらめっちゃ便利。と言いつつ僕も完璧には分かっていないけど。

みんなもnpm、やってみてくれよな!

PREV
2021-01-30
【JS】named exportとdefault exportの違いやReactでの使い分け