npm から pnpm に移行する取り組み


今回の差分のプルリク

今回やったこと
ローカルマシン
- npm/npx の封印
- pnpm のグローバルインストール
-
minimumReleaseAge
の設定pnpm config set --location global minimumReleaseAge 10080
リポジトリ(ローカル開発ができるようになるまで)
- リポジトリで使う pnpm バージョンを指定(package.json の
packageManager
フィールド) - package-lock.json から pnpm-lock.yaml への移行(
pnpm import
) - package.json の npm スクリプト内にある
npm
、npx
を置き換え-
npm
->pnpm
-
npx
->pnpm dlx
-
- pnpm-workspace.yaml を作成
- pnpm-workspace.yaml に
minimumReleaseAge: 10080 # 1 week
を追加 - 動作確認
リポジトリ(CI/CD が通るようになるまで)
- pnpm 利用ジョブに
pnpm/action-setup
アクションを追加 -
actions/setup-node
アクションのwith.cache
を pnpm に変更 -
actions/setup-node
アクションのwith.cache-dependency-path
を pnpm-lock.yaml に変更 -
npm ci
をpnpm install --frozen-lockfile
に置き換え -
npm run
をpnpm run
に置き換え- スクリプト名の直後に
--
がある場合、削除
- スクリプト名の直後に
-
npm ls
をpnpm ls
に置き換え-
--json
の形式が npm と異なるため、後続処理を修正
-
- CI を走らせて、通るようになるまでがんばる
色々やったし、このリポジトリ特有の作業もした。
ある程度は自動化できる部分自動化したいな。

グローバルインストール
npm はいい。node 入れたらくっついてくるから。
pnpm は自分でインストールしないといけない。
ランタイムの管理は mise を使っており、リポジトリごとに node がバラバラに存在する。その都度 pnpm をインストールするのはだるすぎるので、グローバルに一つ持っておきたい。mise と良い感じに組み合わせる方法はあるのか?

見つけた。
どうやら pnpm v10 からは package.json の packageManager
フィールドの pnpm バージョンを勝手に使ってくれるとか。
ということは node とは別に mise で pnpm を管理する必要はないか。代わりに packageManager フィールドを使うことになるわけだけど。
となるので、 mise で pnpm v10 以上を入れておく (mise use pnpm) で良いと思います。
とりあえずこれを実践する。
❯ mise use pnpm --global
mise 2025.8.10 by @jdx – install ✓ installed mise ~/.config/mise/config.toml tools: pnpm@10.16.1
❯ pnpm -v
10.16.1
入った。

npm/npx を封印
せっかく pnpm を入れても npm を使ってしまったら意味ない。
ここは npm/npx を使えないようにする。

使ったら失敗するようにする。真面目に考えてもいいけど、ここは雑に同名のエイリアスを ~/.zshrc
のラストにツッコむ。
alias npx='echo "WARNING: npx は実行しないでください" && false'
alias npm='echo "WARNING: npm は実行しないでください" && false'
末尾にくっつけて source ~/.zshrc
❯ npm
WARNING: npm は実行しないでください
❯ npx
WARNING: npx は実行しないでください
甘えは捨てた。

リポジトリで npm から pnpm へ移行(ローカル環境編)
npm/npx を封印したことでいよいよ npm 使ってるリポジトリでローカル開発ができなくなった。
なんとかしないと。

最初に選ばれたリポジトリは自分のホムペリポジトリでした。
前述した pnpm v10 で corepack 不要で pnpm 自身のバージョン管理が可能に には次のように書いてある。
- ローカルの pnpm を v10 以上にする。
- プロジェクトの devDependencies に、使いたいバージョンの pnpm をインストールする
- プロジェクトの packageManager field に
2
と同じバージョンの pnpm を定義する
まずは pnpm を devDependencies としてインスコするか。
npm はもう使えないのでさっそく pnpm でインストールすることになる??
グローバルの pnpm と同じバージョンにするとちゃんと packageManager のバージョンを使ってくれるか確認できないので、ここは別バージョンにする。
何があるかな?
❯ pnpm info pnpm
pnpm@10.17.0 | MIT | deps: none | versions: 1143
Fast, disk space efficient package manager
https://pnpm.io
keywords: pnpm, pnpm10, dependencies, dependency manager, efficient, fast, hardlinks, install, installer, link, lockfile, modules, monorepo, multi-package, npm, package manager, package.json, packages, prune, rapid, remove, shrinkwrap, symlinks, uninstall, workspace
bin: pnpm, pnpx
dist
.tarball: https://registry.npmjs.org/pnpm/-/pnpm-10.17.0.tgz
.shasum: 530696ed31b10e1c6bf46a441a9b7c1a3159910c
.integrity: sha512-/Oij3Smk7S7FZvtT77sE2MRKDwW8bySnMEaRD7nDznr6NaCYBQBmj6NXM0W9ZEZE+pgzj6FoI1yA9KoXqhf77w==
.unpackedSize: 17.7 MB
maintainers:
- zkochan <z@kochan.io>
- pnpmuser <publish-bot@pnpm.io>
dist-tags:
dev: 6.23.7-202112041634 latest-2: 2.25.7 latest-5: 5.18.10 latest-8: 8.15.9 next-10: 10.17.0 next-8: 8.15.9
latest-10: 10.17.0 latest-3: 3.8.1 latest-6: 6.35.1 latest-9: 9.15.9 next-6: 6.35.1 next-9: 9.15.9
latest-1: 1.43.1 latest-4: 4.14.4 latest-7: 7.33.5 latest: 10.17.0 next-7: 7.33.7 pr4475: 0.0.0-pr4475.1
published yesterday by pnpmuser <publish-bot@pnpm.io>
npm info
と似たノリでpnpm info
できた。ていうか v10 の最新が v10.17.0 なんだが??
mise で入れたグローバルの pnpm は v10.16.2 だ。最新バージョンがレジストリに反映されてないのだろうか。まあいい
リポジトリでは v10.17.0 を使うようにしよう。
`pnpm i -D pnpm@10.17.0`
❯ pnpm i -D pnpm@10.17.0
WARN Moving @biomejs/biome that was installed by a different package manager to "node_modules/.ignored"
WARN Moving @chromatic-com/storybook that was installed by a different package manager to "node_modules/.ignored"
WARN Moving @playwright/experimental-ct-react that was installed by a different package manager to "node_modules/.ignored"
WARN Moving @playwright/test that was installed by a different package manager to "node_modules/.ignored"
WARN Moving @proofdict/textlint-rule-proofdict that was installed by a different package manager to "node_modules/.ignored"
WARN 55 other warnings
╭───────────────────────────────────────────╮
│ │
│ Update available! 10.16.1 → 10.17.0. │
│ Changelog: https://pnpm.io/v/10.17.0 │
│ To update, run: pnpm add -g @pnpm/exe │
│ │
╰───────────────────────────────────────────╯
Downloading storybook@9.0.14: 8.79 MB/8.79 MB, done
Downloading react-icons@5.5.0: 22.22 MB/22.22 MB, done
Downloading sylvester@0.0.12: 7.08 MB/7.08 MB, done
Downloading wordnet-db@3.1.14: 10.55 MB/10.55 MB, done
Downloading @biomejs/cli-darwin-arm64@2.1.1: 13.43 MB/13.43 MB, done
Downloading @img/sharp-libvips-darwin-arm64@1.2.3: 7.57 MB/7.57 MB, done
Downloading kuromoji@0.1.2: 21.83 MB/21.83 MB, done
WARN 4 deprecated subdependencies found: @types/minimatch@6.0.0, glob@7.2.3, inflight@1.0.6, node-domexception@1.0.0
Packages: +1337
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 1417, reused 182, downloaded 1155, added 1337, done
WARN Issues with peer dependencies found
.
├─┬ @storybook/react-vite 9.0.14
│ └─┬ @joshwooding/vite-plugin-react-docgen-typescript 0.6.0
│ └── ✕ unmet peer vite@"^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0": found 7.1.6
└─┬ @storybook/addon-vitest 9.1.7
└── ✕ unmet peer storybook@^9.1.7: found 9.0.14
dependencies:
+ @astrojs/check 0.9.4
+ @astrojs/db 0.15.1 (0.18.0 is available)
+ @astrojs/mdx 4.3.5
+ @astrojs/partytown 2.1.4
+ @astrojs/react 4.3.0 (4.3.1 is available)
+ @astrojs/rss 4.0.12
+ @astrojs/sitemap 3.6.0
+ @astrojs/tailwind 6.0.2
+ @docsearch/css 3.9.0 (4.0.1 is available)
+ @docsearch/react 3.9.0 (4.0.1 is available)
+ @google-analytics/data 4.12.1 (5.2.0 is available)
+ @tailwindcss/aspect-ratio 0.4.2
+ @tailwindcss/typography 0.5.16 (0.5.17 is available)
+ astro 5.13.9
+ astro-meta-tags 0.3.2 (0.4.0 is available)
+ astro-robots-txt 1.0.0
+ date-fns 4.1.0
+ emoji-regex 10.4.0
+ glob 11.0.3
+ natural 8.1.0
+ react 19.1.1
+ react-dom 19.1.1
+ react-icons 5.5.0
+ rehype-external-links 3.0.0
+ rehype-stringify 10.0.1
+ remark-extract-frontmatter 3.2.0
+ remark-frontmatter 5.0.0
+ remark-gfm 4.0.1
+ remark-parse 11.0.0
+ remark-rehype 11.1.2
+ simple-git 3.28.0
+ strip-ansi 7.1.0
+ tailwindcss 3.4.17 (4.1.13 is available)
+ typescript 5.9.2
+ unified 11.0.5
devDependencies:
+ @biomejs/biome 2.1.1 (2.2.4 is available)
+ @chromatic-com/storybook 4.0.1 (4.1.1 is available)
+ @playwright/experimental-ct-react 1.55.0
+ @playwright/test 1.55.0
+ @proofdict/textlint-rule-proofdict 3.1.2
+ @storybook/addon-a11y 9.0.14 (9.1.7 is available)
+ @storybook/addon-docs 9.0.14 (9.1.7 is available)
+ @storybook/addon-links 9.0.14 (9.1.7 is available)
+ @storybook/addon-vitest 9.1.7
+ @storybook/react-vite 9.0.14 (9.1.7 is available)
+ @types/react 19.1.11 (19.1.13 is available)
+ @vitest/browser 3.2.4
+ @vitest/coverage-v8 3.2.4
+ @vitest/ui 3.2.4
+ chromatic 11.29.0 (13.1.5 is available)
+ concurrently 9.2.0 (9.2.1 is available)
+ playwright 1.55.0
+ pnpm 10.17.0
+ storybook 9.0.14 (9.1.7 is available)
+ textlint 14.8.4 (15.2.2 is available)
+ textlint-filter-rule-comments 1.2.2
+ textlint-rule-preset-ja-spacing 2.4.3
+ textlint-rule-preset-ja-technical-writing 10.0.2 (12.0.2 is available)
+ textlint-rule-spellcheck-tech-word 5.0.0
+ vite-plugin-turbosnap 1.0.3
+ vitest 3.2.4
╭ Warning ───────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Ignored build scripts: esbuild, protobufjs, sharp. │
│ Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts. │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────╯
Done in 55.5s using pnpm v10.16.1
色々でた!!!
Ignored build scripts: esbuild, protobufjs, sharp.
ビルドスクリプトがブロックされた。これがセキュリティ向上機能ってことか。
2. pnpmのビルドスクリプト警告対応 が参考になる。あとで考える。
ていうか protobufjs ってどこで使われてるんだ。protobuf と無縁のリポジトリなんだけど。
❯ pnpm ls --depth Infinity protobufjs
Legend: production dependency, optional only, dev only
korosuke613-homepage@2.0.0 /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd (PRIVATE)
dependencies:
@google-analytics/data 4.12.1
└─┬ google-gax 4.6.1
├─┬ @grpc/grpc-js 1.14.0
│ └─┬ @grpc/proto-loader 0.8.0
│ └── protobufjs 7.5.4
├─┬ @grpc/proto-loader 0.7.15
│ └── protobufjs 7.5.4
├─┬ proto3-json-serializer 2.0.2
│ └── protobufjs 7.5.4
└── protobufjs 7.5.4
Google Analyticsェ...

pnpm-lock.yaml が爆誕してた。
yaml なんだ。
ていうか調べたら pnpm import
という機能があるらしい。既存の package-lock.json から移行できると。
変更を取り消してまずは pnpm import する
❯ pnpm import
WARN `node_modules` is present. Lockfile only installation will make it out-of-date
WARN 4 deprecated subdependencies found: @types/minimatch@6.0.0, glob@7.2.3, inflight@1.0.6, node-domexception@1.0.0
Progress: resolved 1416, reused 0, downloaded 0, added 0, done
WARN Issues with peer dependencies found
.
├─┬ @storybook/addon-vitest 9.1.7
│ └── ✕ unmet peer storybook@^9.1.7: found 9.0.14
└─┬ @storybook/react-vite 9.0.14
└─┬ @joshwooding/vite-plugin-react-docgen-typescript 0.6.0
└── ✕ unmet peer vite@"^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0": found 7.1.6
色々出たが今は無視する。
再度 pnpm i -D pnpm@10.17.0
❯ pnpm i -D pnpm@10.17.0
WARN 4 deprecated subdependencies found: @types/minimatch@6.0.0, glob@7.2.3, inflight@1.0.6, node-domexception@1.0.0
Already up to date
Progress: resolved 1417, reused 1337, downloaded 0, added 0, done
WARN Issues with peer dependencies found
.
├─┬ @storybook/addon-vitest 9.1.7
│ └── ✕ unmet peer storybook@^9.1.7: found 9.0.14
└─┬ @storybook/react-vite 9.0.14
└─┬ @joshwooding/vite-plugin-react-docgen-typescript 0.6.0
└── ✕ unmet peer vite@"^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0": found 7.1.6
devDependencies:
+ pnpm 10.17.0
Done in 4.6s using pnpm v10.16.1
先に import したのでスリムな結果に。
package-lock.json くんはサヨナラする。
package.json に packageManager
を加える。
❯ git diff package.json
diff --git a/package.json b/package.json
index 1cef60d..dcaf469 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"name": "Futa Hirakoba",
"url": "https://github.com/korosuke613"
},
+ "packageManager": "pnpm@10.17.0",
"scripts": {
"build": "astro check && tsc --noEmit && astro build",
"build-types": "tsc --noEmit --pretty",
果たして 10.17.0 が使われるのか?
❯ pnpm -v
10.17.0
❯ cd ..
❯ pnpm -v
10.6.5
使われてる〜
じゃあ今度は devDependencies に入れてないバージョンだとどうなるか見るか。
❯ git diff package.json
diff --git a/package.json b/package.json
index 1cef60d..5a968c1 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"name": "Futa Hirakoba",
"url": "https://github.com/korosuke613"
},
+ "packageManager": "pnpm@10.16.0",
"scripts": {
"build": "astro check && tsc --noEmit && astro build",
"build-types": "tsc --noEmit --pretty",
10.16.0 は未インストール。
❯ pnpm -v
10.16.0
ちょっと時間かかったけど普通に実行できたぞ???
これ devDependencies へ入れる必要ないのでは?
ただし、その pnpm 自体はどこかしらにインストールされている必要があり、一般的にはプロジェクトの devDependencies にインストールするものかと思われます。
pnpm v10 で corepack 不要で pnpm 自身のバージョン管理が可能に
なるほど。pnpm 自体は必要だよって書かれているだけなので、mise でグローバルに入れてるので事足りてる可能性あるのか。そういうことにしておこう。

動作確認する。
先ほどビルドスクリプトが走らなかったが、ホームページの dev サーバ起動、ビルド、静的解析、テストをやってみる。
- 開発サーバ
- ✅
pnpm start
- ✅
- SSG
- ✅
pnpm run build && pnpm run preview
- ✅
- 静的解析
- ✅
pnpm run lint
- ✅
- テスト
- ユニットテスト
- ❌
pnpm run test:unit
- ❌
- storybook を使ったやつ
- ❌
pnpm run test:storybook
- ❌
- playwright を使ったやつ
- ✅
pnpm exec playwright install
- ✅
pnpm run test:playwright-ct
- ✅
pnpm run test:playwright-e2e
- ✅
- ユニットテスト
- VRT
- ✅
pnpm run vrt:init
- ✅
pnpm run vrt:regression
- ✅
ページ遷移とかjsも動いたから良さそう
❯ pnpm run test:storybook
> korosuke613-homepage@2.0.0 test:storybook /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd
> vitest run --project=storybook --coverage
failed to load config from /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/vite.config.mts
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Startup Error ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/@storybook+addon-vitest@9.1.7_@vitest+browser@3.2.4_@vitest+runner@3.2.4_react-dom@19.1_c150aa282900596a9da5ced895cd2ef0/node_modules/@storybook/addon-vitest/dist/vitest-plugin/index.mjs:10
import { optionalEnvToBoolean, getInterpretedFile, normalizeStories, validateConfigurationFiles, DEFAULT_FILES_PATTERN, resolvePathInStorybookCache } from 'storybook/internal/common';
^^^^^^^^^^^^^^^^^^^^
SyntaxError: The requested module 'storybook/internal/common' does not provide an export named 'optionalEnvToBoolean'
at ModuleJob._instantiate (node:internal/modules/esm/module_job:220:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:321:5)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:644:26)
at async loadConfigFromBundledFile (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:36230:12)
at async bundleAndLoadConfigFile (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:36116:17)
at async loadConfigFromFile (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:36083:42)
at async resolveConfig (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:35732:22)
at async _createServer (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:28008:67)
at async createViteServer (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vitest@3.2.4_@types+debug@4.1.12_@types+node@24.5.2_@vitest+browser@3.2.4_@vitest+ui@3.2.4_jiti@1.21.7_yaml@2.8.1/node_modules/vitest/dist/chunks/cli-api.BkDphVBG.js:6911:17)
at async createVitest (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vitest@3.2.4_@types+debug@4.1.12_@types+node@24.5.2_@vitest+browser@3.2.4_@vitest+ui@3.2.4_jiti@1.21.7_yaml@2.8.1/node_modules/vitest/dist/chunks/cli-api.BkDphVBG.js:10202:17)
ELIFECYCLE Command failed with exit code 1.
❯ pnpm run test:unit
> korosuke613-homepage@2.0.0 test:unit /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd
> vitest run --project=unit --coverage
failed to load config from /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/vite.config.mts
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Startup Error ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/@storybook+addon-vitest@9.1.7_@vitest+browser@3.2.4_@vitest+runner@3.2.4_react-dom@19.1_c150aa282900596a9da5ced895cd2ef0/node_modules/@storybook/addon-vitest/dist/vitest-plugin/index.mjs:10
import { optionalEnvToBoolean, getInterpretedFile, normalizeStories, validateConfigurationFiles, DEFAULT_FILES_PATTERN, resolvePathInStorybookCache } from 'storybook/internal/common';
^^^^^^^^^^^^^^^^^^^^
SyntaxError: The requested module 'storybook/internal/common' does not provide an export named 'optionalEnvToBoolean'
at ModuleJob._instantiate (node:internal/modules/esm/module_job:220:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:321:5)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:644:26)
at async loadConfigFromBundledFile (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:36230:12)
at async bundleAndLoadConfigFile (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:36116:17)
at async loadConfigFromFile (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:36083:42)
at async resolveConfig (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:35732:22)
at async _createServer (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vite@7.1.6_@types+node@24.5.2_jiti@1.21.7_yaml@2.8.1/node_modules/vite/dist/node/chunks/dep-D5b0Zz6C.js:28008:67)
at async createViteServer (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vitest@3.2.4_@types+debug@4.1.12_@types+node@24.5.2_@vitest+browser@3.2.4_@vitest+ui@3.2.4_jiti@1.21.7_yaml@2.8.1/node_modules/vitest/dist/chunks/cli-api.BkDphVBG.js:6911:17)
at async createVitest (file:///Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/vitest@3.2.4_@types+debug@4.1.12_@types+node@24.5.2_@vitest+browser@3.2.4_@vitest+ui@3.2.4_jiti@1.21.7_yaml@2.8.1/node_modules/vitest/dist/chunks/cli-api.BkDphVBG.js:10202:17)
ELIFECYCLE Command failed with exit code 1.
ユニットテストも storybook も vitest 使ってる。
なーんか vitest 関連が失敗してそう。一回 node_modules
を吹き飛ばしてみたが状況変わらず。
そもそもいくつかのパッケージのバージョンが固定されてなかったため、バージョンが合わずに失敗していたっぽい。バージョンを固定した上で実行したら全て通った。
これはバージョンは固定しようねって話。
また、npm script で npx を使うものがあった[1]ので、pnpm dlx
に置き換え。
❯ git diff package.json
diff --git a/package.json b/package.json
index fc42976..def4565 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
"vrt:regression": "playwright test -c playwright-vrt.config.ts ./src/tests/vrt/regression.spec.ts",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build --debug --disable-telemetry",
- "chromatic": "npx chromatic --project-token=$CHROMATIC_PROJECT_TOKEN"
+ "chromatic": "pnpm dlx chromatic --project-token=$CHROMATIC_PROJECT_TOKEN"
},
"dependencies": {
"@astrojs/check": "0.9.4",
ローカルの環境は完成かも。
-
ていうかなんで
npx
使ってるんだろ?いらなくない? ↩︎

リポジトリで npm から pnpm へ移行(CI 編)
ローカルで移行できても CI では移行できていない。ここからが本当の戦いだ(たぶん CI を何度も実行し直すことになる)

さて、どうするか
とりあえず CI で npm を使ってそうな箇所を洗い出す。
`grep -r "npm" .github/workflows --include="*.yml" --include="*.yaml"`
❯ grep -r "npm" .github/workflows --include="*.yml" --include="*.yaml"
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/ci.yaml: - run: npm run lint
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/ci.yaml: run: npm run build
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/ci.yaml: run: npm run test:unit
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/ci.yaml: run: npm run test:playwright-e2e -- --retries=2 --workers=2
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/ci.yaml: run: npm run test:playwright-ct
.github/workflows/ci.yaml: run: npm run test:storybook
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/ci.yaml: cache: 'npm'
.github/workflows/ci.yaml: run: npm ci
.github/workflows/pages.yml: cache: 'npm'
.github/workflows/pages.yml: run: npm ci
.github/workflows/pages.yml: run: npm run db:update
.github/workflows/pages.yml: cache: 'npm'
.github/workflows/pages.yml: run: npm ci
.github/workflows/pages.yml: npm run build
.github/workflows/pages.yml: npm run build -- --remote
.github/workflows/update-blogs-data.yaml: cache: 'npm'
.github/workflows/update-blogs-data.yaml: run: npm ci
.github/workflows/vrt-init.yaml: cache: 'npm'
.github/workflows/vrt-init.yaml: run: npm ci
.github/workflows/vrt-init.yaml: npm run vrt:init
.github/workflows/vrt-regression.yaml: cache: 'npm'
.github/workflows/vrt-regression.yaml: run: npm ci
.github/workflows/vrt-regression.yaml: npm run vrt:init
.github/workflows/vrt-regression.yaml: npm run vrt:regression -- --retries=1 --grep="update dependencies"
.github/workflows/vrt-regression.yaml: npm run vrt:regression -- --retries=1 --grep="add contents"
.github/workflows/copilot-setup-steps.yml: cache: "npm"
.github/workflows/copilot-setup-steps.yml: run: npm ci
.github/workflows/cache.yaml: cache: 'npm'
.github/workflows/cache.yaml: id: npm-install
.github/workflows/cache.yaml: npm ci
.github/workflows/cache.yaml: PLAYWRIGHT_VERSION=$(npm ls --json @playwright/test | jq --raw-output '.dependencies["@playwright/test"].version')
.github/workflows/cache.yaml: key: playwright-${{ steps.npm-install.outputs.PLAYWRIGHT_VERSION }}
.github/workflows/cache.yaml: run: npm run build
.github/workflows/cache.yaml: run: npm run test:unit
.github/workflows/cache.yaml: cache: 'npm'
.github/workflows/cache.yaml: npm ci
対象ファイル
- .github/workflows/ci.yaml
- .github/workflows/cache.yaml
- .github/workflows/pages.yml
- .github/workflows/vrt-init.yaml
- .github/workflows/vrt-regression.yaml
- .github/workflows/update-blogs-data.yaml
- .github/workflows/copilot-setup-steps.yml
利用内容
- パッケージインストール:
npm ci
- npm スクリプト実行:
npm run
- actions/setup-node のキャッシュ:
cache: 'npm'
- playwright のバージョン特定:
npm ls
- サブディレクトリ(
./tools
)[1]cache-dependency-path: 'package-lock.json'
cache-dependency-path: 'tools/package-lock.json'
上記に加え、pnpm/actions-setup を使って pnpm のセットアップが必要になる。
-
そういえばサブディレクトリの
./tools
の存在を忘れていた。こっちはまだ pnpm 移行できていないので後回しにする。 ↩︎

pnpm/actions-setup 入れる
まずは環境を整えるため、npm を使っているワークフローで pnpm/actions-setup を入れる。
Omit version input to use the version in the packageManager field in the package.json.
steps: - uses: pnpm/action-setup@v4
どうやら with.version
を設定しなければ、packageManager
フィールドのバージョンを使ってくれるらしい。すばらしい。
- name: Install pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
バージョン固定して上記を差し込む。pnpm/actions-setup の README では、setup-node より前に置いてたのでマネした。

pnpm をキャッシュの対象にする
自分は setup-node で npm のキャッシュを取るようにしていた。setup-node は pnpm のキャッシュにも対応している。pnpm に変える場合は次をする。
- setup-node で
cache: 'npm'
をcache: 'pnpm'
に変える - setup-node で
cache-dependency-path
を設定している場合は、package-lock.json
からpnpm-lock.yaml
に変える。
単純に置換した。

npm コマンドの利用箇所を pnpm に変える
変えて行く。
-
npm ci
->pnpm install --frozen-lockfile
-
npm run
->pnpm run
-
npm ls
->pnpm ls
npm ci
、npm run
の置き換えは特に困らないが、npm ls
は pnpm ls
と挙動が異なるようで、今までの使ってたコマンドが動かなくなった。
# pnpm ls はうまくいかない
❯ pnpm ls --json @playwright/test | jq --raw-output '.dependencies["@playwright/test"].version'
jq: error (at <stdin>:16): Cannot index array with string "dependencies"
# npm ls はうまく行く
❯ npm ls --json @playwright/test | jq --raw-output '.dependencies["@playwright/test"].version'
1.55.0
json を見てみる。
[
{
"name": "korosuke613-homepage",
"version": "2.0.0",
"path": "/Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd",
"private": true,
"devDependencies": {
"@playwright/test": {
"from": "@playwright/test",
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz",
"path": "/Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd/node_modules/.pnpm/@playwright+test@1.55.0/node_modules/@playwright/test"
}
}
}
]
{
"version": "2.0.0",
"name": "korosuke613-homepage",
"dependencies": {
...
"@playwright/test": {
"version": "1.55.0",
"resolved": "file:../.pnpm/@playwright+test@1.55.0/node_modules/@playwright/test",
"overridden": false
},
...
}
}
全然 json の内容が違う。スキーマから違う。だから jq のクエリが失敗する。
トップレベルが配列になってるのと、dependencies
じゃなくてdevDependencies
になってるのが異なる部分。
pnpm ls @playwright/test --json | jq -r '.[].devDependencies."@playwright/test".version'
で行けそう。
❯ pnpm ls @playwright/test --json | jq -r '.[].devDependencies."@playwright/test".version'
1.55.0
いけた。

CI が成功するようになるまで
ほとんどのジョブが成功した。及第点ではないか
pnpm run
に --
がいらなかった
playwright test 実行時にテストが見つからないとエラー。
> korosuke613-homepage@2.0.0 test:playwright-e2e /home/runner/work/homepage-2nd/homepage-2nd
> playwright test -c playwright-e2e.config.ts -- --retries=2 --workers=2
Error: No tests found.
Make sure that arguments are regular expressions matching test files.
You may need to escape symbols like "$" or "*" and quote the arguments.
ELIFECYCLE Command failed with exit code 1.
調べたら pnpm run
--
がいらないらしい。
Options listed after the script's name are passed to the executed script.
https://pnpm.io/cli/run#options
これ結構移行してるとハマりそうだなー

VRT だけ失敗しちゃってるけどこれは不安定系なので今回は良しとする...(言い訳)

pnpm のセキュアな機能を使っていく

minimumReleaseAge
minimumReleaseAge
設定してみる。ついでに .npmrc から pnpm-workspace.yaml に移行する。
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true
shamefullyHoist: true
minimumReleaseAge: 10080 # 1 week
minimumReleaseAgeExclude:
期間は 1 週間とした。10080 の単位は「分」らしい。
minimumReleaseAgeExclude
で特定のパッケージは除外できる。
この設定で最近出たパッケージをインストールしてみる。
astro@5.13.9 が昨日(2025/09/19)出てた。今使ってるバージョンは 5.10.1。
❯ pnpm add astro@5.13.9
ERR_PNPM_NO_MATCHING_VERSION No matching version found for astro@5.13.9 while fetching it from https://registry.npmjs.org/
This error happened while installing a direct dependency of /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd
The latest release of astro is "5.13.9".
Other releases are:
* next--format-astro-url: 0.0.0-20220816201344
* next--wasm: 0.0.0-wasm-20220921185024
<省略>
* experimental--adapter-sessions: 0.0.0-adapter-sessions-20250207124921
* legacy: 4.16.19
If you need the full list of all 1227 published versions run "$ pnpm view astro versions".
Progress: resolved 59, reused 59, downloaded 0, added 0
ERR_PNPM_NO_MATCHING_VERSION No matching version found for astro@5.13.9 while fetching it from https://registry.npmjs.org/
そんなバージョンねぇよとのこと。
でもその下には The latest release of astro is "5.13.9".
とある。矛盾してない?笑
pnpm view astro versions
も実行したが、普通に 5.13.9 が出てきた。
とにかくインストールには失敗する。エラーメッセージがわかりづらすぎるが...
今度は astro を除外対象にしてみる。
shamefullyHoist: true
minimumReleaseAge: 10080 # 1 week
minimumReleaseAgeExclude:
+ - "astro"
再度インストール。
❯ pnpm add astro@5.13.9
ERR_PNPM_NO_MATCHING_VERSION No matching version found for vite@7.1.6 while fetching it from https://registry.npmjs.org/
This error happened while installing a direct dependency of /Users/korosuke613/ghq/github.com/korosuke613/homepage-2nd
The latest release of vite is "7.1.6".
Other releases are:
* alpha: 6.0.0-alpha.24
* beta: 7.1.0-beta.1
* previous: 5.4.20
If you need the full list of all 674 published versions run "$ pnpm view vite versions".
Progress: resolved 1422, reused 1340, downloaded 2, added 0
失敗した!でも今度は vite 7.1.6 が存在しないって言われてる。
vite 7.1.6 は一昨日(2025/09/18)リリース。1週間以内だ。
なるほどちゃんと依存関係のリリース日も見てる。すばらしい。
セキュアではあるが、依存の依存を一個一個除外して行くのはきつそうだ。
エラーメッセージも不親切だし、やはり出たばかりの機能と言える。今後もっとよくなりそう。
リポジトリ内の設定として minimumReleaseAge
を設定するのはリモートの環境でも適用させるため。 dependabot や renovate による依存関係のアップデートでなんらかの理由で1週間以内のリリースが入ってしまう場合でもインストールを失敗させられる。はず。