npmの個人メモ

フロントエンドを開発するときに絶対にでてくるnpm。

ちゃんとキャッチアップしたことがなかったので、ググってよく見られてそうな以下の記事を読みます。

zenn.dev

Node.jsを使用するうえでnpmはちゃんと理解しておいた方がいいです。

とのこと。はい。理解します。

npmとは

  • Node.jsのパッケージを管理するシステム

パッケージとは

  • モジュールとは
    • 他のプログラムから利用することを目的としたクラスや関数などのプログラム
  • パッケージとは
    • モジュールをまとめて機能するようにしたもの
    • ライブラリとも呼ばれる
  • npmの独自定義
    • npmに存在するのはパッケージとモジュール

npmの必要性

  • 以下の問題を解決するためにnpmが必要
    • パッケージの依存関係
    • パッケージの競合関係
      • パッケージxで使用しているパッケージyが新しくインストールするパッケージzでも使われているとか

Yarn

  • npmよりもインストールが速く、セキュリティが高い特徴がある
  • npmと互換性があるパッケージ管理システム
  • 筆者は改善されてきたnpmで十分かな、らしい

npmのインストール

  • Node.jsをインストールすると同時にインストールされる
  • Node.jsにはnvm(Node Version Manager)というNode.jsのバージョン管理システムで管理できる
  • Node.jsは頻繁にバージョンアップされるからできるだけnvmを入れよう

Linux

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

# bashを開き直す

$ tail -n 3 ~/.bashrc 
export NVM_DIR="$HOME/.config/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

# nvmのバージョン
$ nvm --version
0.39.1

github.com

使い方

# nodejsのインストール
$ nvm install 16.13.2

# インストールされているバージョンの一覧
$ nvm list
->     v16.14.1
         system
default -> 16.14.1 (-> v16.14.1)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v16.14.1) (default)
stable -> 16.14 (-> v16.14.1) (default)
lts/* -> lts/gallium (-> v16.14.1)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.10 (-> N/A)
lts/fermium -> v14.19.0 (-> N/A)
lts/gallium -> v16.14.1
# galiiumだけインストールされてるってことかな。
# リストアップされているのはLTSバージョンかな。

# バージョン切り替え
$ nvm use 16.14.1

# アンインストール
$ nvm uninstall 16.14.1

# nvmバージョン
$ nvm --version

# 利用しているnodeのバージョン?
$ nvm version

npmの使い方

プロジェクトの作成

  • npmはプロジェクト内でパッケージを管理するものなのでプロジェクトを作成する必要がある

初期化

  • npm init でpackage.jsonを作成する
  • package.jsonを作成しないでもパッケージのインストールはできる
    • ただしnpm管理にならないので恩恵にあずかれない
      • 依存関係の管理ができないってことかな
  • package.jsonが無いところで、npm install xxxしたらインストールできた
# initで初期化(package.jsonを作成)
# -y つけると全部初期値で作成
$ npm init -y

Wrote to /home/xxxxx/npm_sample/package.json:

{
  "name": "npm_sample", // パッケージの名前
  "version": "1.0.0",
  "description": "", // npmリポジトリの説明欄
  "main": "index.js",  // メインファイル(一番最初に実行されるファイル) 
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [], // パッケージを公開したときの検索ワード
  "author": "",
  "license": "ISC"
}

proxy

  • proxyをかましたい場合に設定可能
# 設定の確認
$ npm config list
; node bin location = /home/xxxx/.config/nvm/versions/node/v16.14.1/bin/node
; cwd = /home/xxxx/npm_sample
; HOME = /home/xxxx
; Run `npm config ls -l` to show all defaults.

# 詳細情報
$ npm config ls -l

# configのset
$ npm config set proxy [proxy url]

インストール

# ローカルインストール(install -> iでもよい)
$ npm install typescript
⸨⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⸩ ⠴ idealTree:npm_sample: sill idealTree buildDeps
added 1 package, and audited 2 packages in 24s
found 0 vulnerabilities

# バージョン指定のインストール
# vueの最新は3.xだが、2.6.xを入れたい場合
$ npm info vue
vue@3.2.31 | MIT | deps: 5 | versions: 370
# ...省略
dist-tags:
csp: 1.0.28-csp  latest: 3.2.31   legacy: 2.6.14   next: 3.2.31     

# 2.6.14がlegacyらしい
$ npm i vue@2.6.14
added 1 package, and audited 3 packages in 2s
found 0 vulnerabilities

$ cat package.json
# 抜粋。vue2.6.14がインストールできた
# --saveしなくてもdepencenciesに追加されるのね
  "dependencies": {
    "typescript": "^4.6.2",
    "vue": "^2.6.14"
  }

# devDependenciesへの追加
# --save-dev または -D
$ npm i -D webpack
$ cat package.json
# 抜粋。webpakがdevの方に追加された。
  "devDependencies": {
    "webpack": "^5.70.0"
  }

package.jsonに設定されたパッケージのインストール

$ cd [ package.jsonがあるディレクトリ]
$ npm install

npmのバージョンアップ

npm自信をグローバルインストールする

$ npm install -g npm

不思議だな。

パッケージ一覧

# 直接インストールしたもの npm list --depth=0と同じ
# インストールしたパッケージが依存しているパッケージは表示されない
$ npm list

# 表示形式が違うバリエーションがある
$ npm ls
$ npm la
$ npm ll

# パッケージが依存しているパッケージも表示
$ npm list --depth=1
npm_sample@ /home/xxxx/npm_sample
├── typescript@4.6.2
├── vue@2.6.13
└─┬ webpack@5.70.0
  ├── @types/eslint-scope@3.7.3
  ├── @types/estree@0.0.51
  ├── @webassemblyjs/ast@1.11.1
  ├── @webassemblyjs/wasm-edit@1.11.1
...

# グローバルインストールパッケージ一覧
$ npm list -g

パッケージ情報の検索/取得

# npmリポジトリから検索
$ npm search vue
NAME                      | DESCRIPTION          | AUTHOR          | DATE       | VERSION  | KEYWORDS
vue                       | The progressive…     | =yyx990803…     | 2022-02-12 | 3.2.31   | vue     

# パッケージ詳細情報の表示(info -> view  or showのエイリアスあるどれも同じ)
$ npm info vue

# パッケージの最新バージョン
$ npm info vue version
3.2.31

# インストール可能なパッケージ一覧
$ npm info vue versions
[
  '0.0.0',          '0.6.0',                '0.7.0',
  '0.7.1',          '0.7.3',                '0.7.4',
...

パッケージのアップデート

# アップデートの確認
$ npm outdated
Package   Current  Wanted  Latest  Location               Depended by
@vue/cli    5.0.2   5.0.3   5.0.3  node_modules/@vue/cli  npm_sample
vue        2.6.14  2.6.14  3.2.31  node_modules/vue       npm_sample
# 多分wantedにあげた方がよい。vueのLatestはメジャーバージョンが上がって破壊的な変更が入っているので、wantedと一致してない

# アップデート
$ npm update @vue/cli

# パッケージ名省略で全部アップデート
$ npm update

# npm updateで@vue/cliだけアップデートされた。wantedまで上がるということか
$ npm outdated
Package  Current  Wanted  Latest  Location          Depended by
vue       2.6.14  2.6.14  3.2.31  node_modules/vue  npm_sample

# でもpakcage.jsonに反映されないな。自動反映されないのか?
$ cat package.json 
{
  "dependencies": {
    "vue": "^2.6.13"
  },
  "devDependencies": {
    "@vue/cli": "^5.0.2",
    "typescript": "^4.6.2"
  }
}
# ^5.0.2 は5.0.3を含むから、別に更新しなくていいのか。

npm-check-updates

  • 複数のパッケージのアップデータを便利できる
$ npm i -g npm-check-updates
$ ncu
Checking /home/xxxxx/npm_sample/package.json
[====================] 3/3 100%
 vue       ^2.6.13  →  ^3.2.31     
 @vue/cli   ^5.0.2  →   ^5.0.3     
# vue/cliの5.0.3はインストールされているはずだけどpackage.jsonベースの確認

# package.jsonへの反映
$ ncu -u
Upgrading /home/xxxxx/npm_sample/package.json
[====================] 3/3 100%

 vue       ^2.6.13  →  ^3.2.31     
 @vue/cli   ^5.0.2  →   ^5.0.3     

Run npm install to install new versions.

# vueと@vue/cliがバーアップされてしまった。
$ cat package.json 
{
  "dependencies": {
    "vue": "^3.2.31"
  },
  "devDependencies": {
    "@vue/cli": "^5.0.3",
    "typescript": "^4.6.2"
  }
}

# vueは除外したいな
$ ncu -u -x vue
Upgrading /home/xxxx/npm_sample/package.json
[====================] 2/2 100%

 @vue/cli  ^5.0.2  →  ^5.0.3     

# ncu のパッケージ指定は正規表現も使える

ncuは以下で確認

github.com

アンインストール

# uninstallじゃなくて、remove, rm, r, un, unlink でも同じ
$ npm uninstall @vue/cli
$ npm uninstall -g @vue/cli # グローバル

# package.jsonへの反映
# 試してみると、--saveを指定しなくてもpackage.jsonに反映される
# npmのバージョンの問題かな
$ npm un --save @vue/cli

ローカルインストールの罠

PATHを通す。

# グローバルインストールの場所
$ npm root -g
/home/xxxx/.config/nvm/versions/node/v16.14.1/lib/node_modules

# パッケージを探す探索パスは以下でも確認可能
# nodejsのグローバル変数を出力している
# node -e でワンライナーを実行できるのね
$ node -e "console.log(global.module.paths)"
[
  '/home/xxxx/npm_sample/node_modules',
  '/home/xxxx/node_modules',
  '/home/node_modules',
  '/node_modules'
]
# 上から順にモジュールを検索するのか。一つ目はローカルだな。

# npmコマンドのインストール先
# グローバル
$ npm bin -g
/home/xxxx/.config/nvm/versions/node/v16.14.1/bin

# ローカル。ローカルパッケージディレクトリにて
$ npm bin
/home/xxxx/npm_sample/node_modules/.bin

# 以下にパスを通すとglobal.module.pathsに反映される
$ export NODE_PATH=<PATH>

ローカルパッケージの実行

# ./node_modules/.bin への絶対パスを返す
$ npm bin tsc
/home/xxxx/npm_sample/node_modules/.bin

$ ll ./node_modules/.bin/
lrwxrwxrwx 1 xxxx xxx    21 Mar 19 13:40 tsc -> ../typescript/bin/tsc
lrwxrwxrwx 1 xxxx xxx    22 Mar 19 13:48 vue -> ../@vue/cli/bin/vue.js
# .binには各node_modules以下のコマンドへのシンボリックリンクがある
# 直接.bin以下を叩いてもよいが面倒なので、npxがあるよってのがこのあとの話

npx

  • ./node_modules/.binからコマンドを探して実行してくれる
  • コマンドがインストールされてなくても探して実行してくれる
    • え?npm installしてなくてもってことかな
$  npx webpack
Need to install the following packages:
  webpack
Ok to proceed? (y) y
# インストールを求められるだけだな、npmのバージョンで違いがあるのかな

# ローカルインストールされてれば以下のように実行可能
$ npx tsc -V
Version 4.6.2

# urlからもnpxやnpm installできる?
$ npx https://github.com/Microsoft/TypeScript.git
npm ERR! could not determine executable to run
# エラーになった。別のパッケージならできるのかな。

$ npm install https://github.com/Microsoft/TypeScript.git
⸨#########⠂⠂⠂⠂⠂⠂⠂⠂⠂⸩ ⠸ reify:typescript: sill audit bulk request { typescript: [ '
# なんか時間かかるので止めた

npxやnpm installでURLを指定するのは、あまり一般的ではないのかな。便利に使えるケースもあまり無い気もするし。公開されてない社内レポジトリのパッケージを使うとき、とかかしら。

package.json

  • package.jsonとはプロジェクトの構成、構造を示した設計書のようなもの
    • プロジェクト=パッケージ

name

  • パッケージの名前
  • @/の間にユーザーネーム(or組織名)をいれて名前空間とするscoped packages形式のネーミングも多い
    • @types/reactとか@babel/coreとか@vue/cliとか
    • node_modules以下だと、@vueとかでディレクトリが切れてそれ以下にcliとかのディレクトリがある。
    • @vueで一つのリポジトリになってて、モノレポになっているんだと思う

version

  • 決まりは無いが、大抵はセマンティックバージョニングに則ってる

semver.org

description

  • パッケージの説明、npm searchで表示される

private

  • パッケージを公開しない設定
  • 開発時は「true」にした方が良い

main

  • そのパッケージをインストールする際に開始となるファイル

bin

$ ll ./node_modules/.bin/ | grep tsc
lrwxrwxrwx 1 xxxx xxxx 21 Mar 19 13:40 tsc -> ../typescript/bin/tsc

$ cat ./node_modules/typescript/package.json | grep bin
    "bin": {
        "tsc": "./bin/tsc",
        "tsserver": "./bin/tsserver"

scripts

  • コマンドのエイリアス
  • npm run xxxxで実行
  • 慣習的に使われるエイリアスがある
    • start: プログラムを実行する
    • test : テストを実行する
  • 名前の先頭にpreをつけるとついてないコマンドを実行する前にpreがついたコマンドが実行される
    • postもある
    • preとかpostとかあまり使われてなさそう。見かけない。

dependencies

  • npm insall vue --saveでpackage.jsonのdependenciesに追記される
    • ローカル環境だと--saveつけなくても追記される

npm 5.0.0からのデフォルトの挙動らしい.自分のローカルは「8.5.0」、「5.0.0」が出たのはもう5年前らしい。 qiita.com

package-lock.json もなんか最新バージョンと違うきもするが、あとで調べよう。

  • dependenciesのバージョンに指定されている文字
    • ^ : メジャーバージョンを固定(マイナー、パッチはあがる)
    • ~:メジャー、マイナーバージョンを固定(パッチはあがる)
    • なし:全部一致

devDependencies

  • --save-dev-Dをつける
  • 開発やテスト時につかうパッケージを設定する

optionalDependencies

  • インストールに失敗しても、他のインストールの処理が続行される
    • なくても問題ないパッケージ
  • あまり設定することない
    • いつ設定するんだろうか

package-lock.json

  • パッケージのバージョンをロックするファイル
  • npm installで作成される
  • npm installでインストールするたびに更新される?
    • npm 8x だとそうでもないような挙動に見える、バージョン差分かな
  • 複数人で開発するときにバージョンをあわせられる
  • git管理する
  • 人の手では更新しない
    • npm updateで更新される。(package.jsonは更新されないようだ)
  • pakage-lock.jsonでインストールするには、npm insltallではなくてnpm ciを利用する?
    • npm 8xではnpm installでもpackage-lock.jsonを見てそうだった

パッケージとモジュール

  • npmでのモジュールとは
    • node_modulesディレクトリに存在し、rquire()関数で読み込んで使用されるもの
    • 大抵はpackage.jsonで管理され、mainフィールドを持っているパッケージ
  • npmのパッケージとは
    • package.jsonで管理しているプロジェクトのこと
    • あるパッケージAにインストールされて、そのパッケージAから読み込まれれてたら、それはモジュール
      • そのパッケージA内でコマンドラインなどで単体に使用される場合はパッケージ

まとめ

  • 最初は全てパッケージ
  • パッケージを他のパッケージインストールして、その使われ方によってパッケージとモジュールにわかれる
  • 単体で使用するならパッケージ。読み込まれて使用されるならモジュール。

なんとなくわかったが。。JSでimport or requireされるJSはモジュールになる、ということかな。(基本はパッケージ)

パッケージの公開

  • パッケージは以下からダウンロードしている
    • npm installで通信している先
  • 誰でもパッケージを登録することが可能
    • アカウント作成が必要
  • 削除したパッケージ名+バージョンは2度と登録するこはできない
  • npmNode Package Managerの略ではなくて、npm is Not An Acronymらしい。
    • バクロニムと呼ばれるものらしい。

www.npmjs.com

試してみたいけど、なにもできないソースあげてもなぁ。

$ npm login # npmサイトにログインする。アカウント必要。ワンタイムパスも。
$ npm publish # 公開
$ npm logout # ログアウトは忘れずに

# アップデート
# package.jsonのバージョンを更新
$ npm publish # ログインしてからね

# 削除
$ npm unpublish パッケージ名