npmパッケージが悪意のあるコードに置き換えられる「サプライチェーン攻撃」が頻繁にITニュースの話題になっています。 つい最近の記事はこんな感じです。
npmってなんぞやって思った人もいると思いますが、Node.jsというクロスプラットフォームに対応したフリーでオープンソースのJavaScript実行環境でJavaScriptのパッケージ(再利用可能なコードのまとまり)を管理するツールです。 Node.jsは色々なところで使われていて、気がついたら自分の環境にも入っていたなんてことはあるかもしれません。
試しにターミナルを開いてnode -vとか実行してみてください。開発に使っている端末であれば、Node.jsはインストール済みなんてことは多いと思います。
% node -v v25.2.1
さて、たびたびnpmが狙われることが発生しています*1。
ちょっと調べたら見つかった情報
npmの代替としてYarnがあるのは知っていましたが、これを機会に比較しようと思い調べていました。 はじめに見つけたのがこれです。
上記はよくまとまっているので見てほしいとして、ざっくりまとめると次のようになるようです。
| ツール | 利点 | 欠点 |
|---|---|---|
| npm | デフォルトで使える | node_modules構造が柔軟すぎて、意図しない依存関係へのアクセスを許容しやすい |
| pnpm | 高速、ディスク効率が良い | シンボリックリンクの扱いに慣れが必要。特にWindowsとの相性に課題 |
| Yarn | npmより高速、安定動作 | PnPモードはnode_modulesの脆弱性を排除するが、一部互換性に課題 |
npmからyarnに切り替え
npmでは次のようにセットアップしていました。yarnに置き換える場合は、どのように実施すればいいでしょうか? 基本的にはコマンドを置き換えるだけで済みます。
まず比較のために、textlintをnpmコマンドを使ってこういう感じでインストールした構成を想定します。
% cd ~/projects % npm init -y # なければ package.json を作成 % npm install --save-dev textlint % npm install --save-dev textlint-rule-preset-ja-technical-writing
去年、こういうブログ記事を書いたのを思い出しました。
これを、Yarnに置き換えてみたいと思います。
まず、何はともあれYarnをインストールします。Yarnのインストール方法はOSによって色々選択肢があるのですが、この方法を使う方が最も簡単でしょう。 ところで、yarnをインストールするのにnpmコマンドを使うのはなんとも複雑です。
% npm install -g yarn
次にyarnを使って、必要なパッケージをインストールするだけです。
% cd ~/projects % rm -r node_modules //必要ないので消す % rm package-lock.json //必要ないので消す % yarn init -y % yarn add --dev textlint % yarn add --dev textlint-rule-preset-ja-technical-writing
あれ?またnode_modulesディレクトリーが作られていますね?
% ls blog2.txt package-lock.json textlint-fix.sh yarn.lock node_modules package.json textlint.sh
調べたところ、次のような理由からでした。
- 次のことから、デフォルトでは有効化されていない
- Yarn v1とv2+で体験が大きく異なり、v2+のPnPは学習コストがかかる
- 一部ツールとの互換性に問題が出やすい
- もし試したい場合はプロジェクトルートでコマンドを実行して、プロジェクトをアップグレードする必要がある
早速やってみることにしました。
# プロジェクト内で Yarn のバージョンを最新(Berry)に設定する % yarn set version stable # PnP モードを有効にする(デフォルトで有効化されるはずですが念のため) % yarn config set nodeLinker pnp % rm -r node_modules //いったん消す % rm yarn.lock //いったん消す % yarn init -y % yarn add --dev textlint % yarn add --dev textlint-rule-preset-ja-technical-writing
実行後確認すると、node_modulesディレクトリーの代わりに.pnpから始まるディレクトリーと.yarnディレクトリーが作られていました。だからなにが違うかというと、ディレクトリーサイズです。 npmを使ってインストールしたときは120MBくらいと出ていましたが、この方法でインストールした場合のディレクトリーサイズは4.4MBでした。この差は大きいですね*2。
この理由の詳細は後述しますが、yarnはパッケージのキャッシュはグローバルにインストールするのがデフォルトだからです。そのため、プロジェクトディスレクトリーは膨れません。つまり、共通するパッケージが重複はしないってことですね。
% ls -a . .gitattributes .yarn textlint-fix.sh .. .gitignore .yarnrc.yml textlint.sh .DS_Store .pnp.cjs blog2.txt yarn.lock .editorconfig .pnp.loader.mjs package.json .git .textlintrc.json README.md
移行後の使い方
先ほどインストールしたtextlintですが、yarnでインストールした場合はどう使えばいいでしょうか。 簡単です。ここでもコマンドを置き換えるだけです。
npx textlint test.md ↓ yarn textlint test.md
実行例です。textlintはPnPモードでも動作するようです。
% yarn textlint blog2.md /Users/ytooyama/working/docwrite/blog2.md 1:16 error 文末が"。"で終わっていません。 理由: 句点は文の境界を明確にし、読み手の理解を助けます 修正: 適切な文末表現で文を完結させ、句点を追加してください 例: 「〜です。」「〜ます。」「〜でした。」など ja-technical-writing/ja-no-mixed-period ✖ 1 problem (1 error, 0 warnings, 0 infos)
キャッシュの話
ちなみにキャッシュは$USER/.yarn/berry/cacheに展開されるようです。
全部zip圧縮されていました。ここがストレージの消費サイズの差として出てくるのかな?
% yarn config get cacheFolder /Users/ytooyama/.yarn/berry/cache % tree /Users/ytooyama/.yarn/berry/cache /Users/ytooyama/.yarn/berry/cache ├── @azu-format-text-npm-1.0.2-e8edacd073-10c0.zip ├── @azu-style-format-npm-1.0.1-c450b7c945-10c0.zip ├── @babel-code-frame-npm-7.27.1-4dbcabb137-10c0.zip ├── @babel-helper-string-parser-npm-7.27.1-d1471e0598-10c0.zip ├── @babel-helper-validator-identifier-npm-7.28.5-1953d49d2b-10c0.zip ├── @babel-parser-npm-7.28.5-f2345a6b62-10c0.zip ├── @babel-types-npm-7.28.5-582d7cca8a-10c0.zip ...
今後は都度、npmからyarnに置き換えていきたいと思います。 PnPモードを使うのは都度検討ですね。動くなら使っていきたいです。
ここら辺にnpmとyarnの比較が載っていました。 ざっくりいうと依存関係の解決が厳格になったり、すでにインストールされたパッケージの改ざんを防ぐ措置が有効になるようです。良いですね。
