chore(deps): migrate from npm to pnpm#3
Conversation
Switch package manager to pnpm 10.32.1 for faster, stricter installs and alignment with the SvelteKit/Vite ecosystem (both use pnpm in their own repos). Changes: - Delete package-lock.json, add pnpm-lock.yaml (isolated layout). - Add "packageManager": "pnpm@10.32.1" to package.json so corepack pins the exact version for every contributor and CI. - Flip .gitignore: ignore package-lock.json, stop ignoring pnpm-lock.yaml. Verified: deno task sync --check runs cleanly under pnpm's isolated node_modules layout (105/105 hash matches). The sync script imports only from https://deno.land/std URLs, so pnpm's symlinked layout has zero impact on the deno side of this hybrid project. Follow-up commits update CI, husky, deno.json tasks, package.json build script, and documentation to invoke pnpm instead of npm.
Wire the build infrastructure to pnpm after the lockfile switch in 76fcbbe. Every invocation point that previously ran `npm run X` or `npm ci` now runs its pnpm equivalent. - .github/workflows/ci.yml: add pnpm/action-setup@v4 BEFORE actions/setup-node (order matters — setup-node's cache:pnpm key needs pnpm already on PATH). Swap npm ci for pnpm install --frozen-lockfile so CI refuses to install if lockfile drifts. - .husky/pre-push: 5 local checks now invoke pnpm directly. - deno.json: deno tasks that shell out to the SvelteKit toolchain now call pnpm. The sync task is unchanged (it's pure deno). - package.json build script: swap `npx pagefind` for `pnpm exec pagefind` so no sub-invocation falls back to npm.
Clean up the last developer-facing npm/npx references after the CI/husky/deno.json switch in 3a916ce. Scope is scripts, i18n UI strings, README, and internal package.json script chains. Scripts (error messages and JSDoc usage): - sync-from-3b.ts: 4 console strings (Re-run, Usage, Run, Run) now point at `pnpm sync` / `pnpm sync:diff`. - translation-create.ts: shebang, JSDoc Run:, console.error Usage, and final `pnpm build` reminder. - translation-status.ts: shebang, JSDoc Run:, and two console.log messages pointing users at `pnpm sync` / `pnpm translation:create`. - validate-dates.ts: shebang + JSDoc Usage block. - generate-og-images.ts: JSDoc Usage (now references the named pnpm scripts `og:generate` / `og:generate:force`). - Shebangs use `#!/usr/bin/env -S pnpm exec tsx` (the -S flag makes multi-arg shebangs portable across macOS and Linux; the old `npx tsx` form was already broken on Linux without -S). i18n (user-facing UI string): - messages/en.json + messages/ko.json: `search_dev_notice` now tells users to run `pnpm build && pnpm preview` when search is unavailable in dev. Internal script chains in package.json: - Replace all `npx tsx` with bare `tsx` (node_modules/.bin is on PATH inside pnpm scripts, matching the existing `og:generate` pattern which already used this form — removes inconsistency). - `search:preview` now chains `pnpm build` instead of `npm run build`. README.md: - Install/dev/build/sync/translation instructions all use pnpm. - Added a short note that pnpm version is pinned via `"packageManager"` and picked up by corepack. Deliberately NOT touched (historical/editorial): - PROGRESS.md: session log, preserves the record of when things were built under npm. - src/content/posts/**: blog posts, factual snapshots of prior workflows at publish time.
Prettier formats YAML files by default, so after the npm→pnpm migration it tried to rewrite pnpm-lock.yaml and failed format:check on every run. Add both pnpm-lock.yaml and package-lock.json to .prettierignore. The latter is belt-and-suspenders — it's already in .gitignore, but prettier runs on working-tree files regardless of git state, so a stray lockfile from someone running `npm install` by mistake wouldn't break their format check. Caught by running .husky/pre-push locally before pushing this branch — exactly what the pre-push hook is for.
pnpm's isolated node_modules layout caught a pre-existing phantom dependency: three remark plugins in src/lib/plugins/ use JSDoc type imports from 'mdast' and 'vfile' but neither package was declared in package.json. Under npm's flat-hoist layout, these types were pulled in transitively (probably via mdsvex → mdast-util-from-markdown) and ended up at the top of node_modules/ where TypeScript could find them. The codebase worked by accident. pnpm's isolated layout refuses to hoist undeclared transitives, so svelte-check failed with 5 "Cannot find module 'mdast'/'vfile'" errors after the migration in 76fcbbe. The fix is to declare what we actually use: - @types/mdast — type-only package (no runtime counterpart); used in @param {import('mdast').Root} across 3 remark plugins. - vfile — ships its own types; used in @param {import('vfile').VFile} in remark-reading-time and remark-toc-extract. After this declaration, pnpm check reports 0 errors, 0 warnings across 1005 files (up from 1001 — TypeScript discovered 4 more files now reachable through the properly-resolved type paths). This is not a regression introduced by pnpm — it's pnpm exposing a latent bug that would have eventually broken under a dep bump.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughThe repository migrates its package manager from npm to pnpm: CI, hooks, scripts, Deno tasks, README, localized messages, and CLI usage hints were updated to pnpm equivalents; package.json records Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Follow-up to 205cd9c (`chore(deps): declare @types/mdast and vfile as devDependencies`). Because deno.json has `"nodeModulesDir": "auto"`, deno tracks the npm workspace alongside its own dependencies — when package.json devDependencies change, deno's next run updates deno.lock's specifiers and workspace block to match. Running `deno task sync --check` after the phantom-dep fix caused this drift; committing it now keeps the two lockfiles consistent so CI doesn't regenerate deno.lock on its next sync run. Added entries: - npm:@types/mdast@^4.0.4 → 4.0.4 - npm:vfile@^6.0.3 → 6.0.3
Deploying brandonwie-dev with
|
| Latest commit: |
55f7c58
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://920f0249.brandonwie-dev.pages.dev |
| Branch Preview URL: | https://chore-migrate-npm-to-pnpm.brandonwie-dev.pages.dev |
|
@claude review |
There was a problem hiding this comment.
Pull request overview
Migrates the repository from npm to pnpm (pinned via packageManager) and updates all documented/automated invocation points accordingly, including a fix for phantom dependencies exposed by pnpm’s isolated node_modules layout.
Changes:
- Switch package manager to pnpm and update CI/Husky/Deno tasks/scripts/docs to use
pnpm/pnpm exec. - Add explicit devDependencies (
@types/mdast,vfile) to resolve phantom dependency issues under pnpm. - Exclude lockfiles from Prettier formatting to avoid lockfile corruption.
Reviewed changes
Copilot reviewed 13 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/ci.yml |
Updates CI to install and run via pnpm. |
.husky/pre-push |
Switches pre-push checks from npm run to pnpm. |
deno.json |
Updates Deno tasks to invoke pnpm scripts. |
package.json |
Pins pnpm via packageManager, updates scripts, adds phantom-dep devDependencies. |
.gitignore |
Stops ignoring pnpm-lock.yaml and ignores package-lock.json instead. |
.prettierignore |
Ignores lockfiles to prevent Prettier from rewriting them. |
README.md |
Updates developer instructions to use pnpm. |
scripts/validate-dates.ts |
Updates shebang/usage guidance to pnpm-based commands. |
scripts/translation-status.ts |
Updates shebang and user-facing guidance messages to pnpm. |
scripts/translation-create.ts |
Updates shebang and user-facing guidance messages to pnpm. |
scripts/sync-from-3b.ts |
Updates user-facing “Run …” guidance from npm to pnpm. |
scripts/generate-og-images.ts |
Updates usage docs to refer to pnpm scripts. |
messages/en.json |
Updates dev notice text to pnpm commands. |
messages/ko.json |
Updates dev notice text to pnpm commands. |
pnpm-lock.yaml |
Adds pnpm lockfile (npm lockfile removed). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - uses: pnpm/action-setup@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version-file: '.node-version' | ||
| cache: npm | ||
| cache: pnpm |
There was a problem hiding this comment.
CI installs pnpm via pnpm/action-setup@v4 but doesn’t explicitly pin the pnpm version. If the action defaults to “latest”, CI may run a different pnpm than the packageManager: pnpm@10.32.1 you’ve pinned in package.json, undermining the “exact version on every CI runner” guarantee. Consider configuring the action with with: version: 10.32.1 (or using corepack to activate the packageManager version) so CI matches the pinned version deterministically.
| The exact version is pinned via `"packageManager"` in `package.json` | ||
| (Node 16.10+ picks it up automatically through corepack). |
There was a problem hiding this comment.
README claims “Node 16.10+ picks it up automatically through corepack”, but this repo pins Node via .node-version (currently 24). To avoid confusing contributors, update this note to match the project’s actual Node requirement and (if relevant) mention that corepack may need to be enabled/activated depending on the Node distribution.
| The exact version is pinned via `"packageManager"` in `package.json` | |
| (Node 16.10+ picks it up automatically through corepack). | |
| Use the Node version pinned in `.node-version` (currently Node 24). | |
| The exact pnpm version is pinned via `"packageManager"` in `package.json`; | |
| depending on your Node distribution, you may need to enable/activate | |
| [corepack](https://nodejs.org/api/corepack.html) first (for example, | |
| `corepack enable`). |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
package.json (1)
6-6: Addengines.nodeconstraint to enforce Node.js compatibility.The package.json currently specifies pnpm@10.32.1, which requires Node.js v18.12.0 or newer. Adding an
engines.nodefield with">=18.12.0"will cause package managers to fail early if installed on unsupported runtimes, preventing downstream issues.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` at line 6, Add an engines.node constraint to package.json to enforce Node.js compatibility by adding an "engines" object with "node": ">=18.12.0" (so package managers will fail early on unsupported runtimes); update the package.json top-level object to include the engines field alongside the existing "packageManager": "pnpm@10.32.1" entry to ensure pnpm's Node requirement is explicitly declared.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@package.json`:
- Line 6: Add an engines.node constraint to package.json to enforce Node.js
compatibility by adding an "engines" object with "node": ">=18.12.0" (so package
managers will fail early on unsupported runtimes); update the package.json
top-level object to include the engines field alongside the existing
"packageManager": "pnpm@10.32.1" entry to ensure pnpm's Node requirement is
explicitly declared.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cba131a5-ef0f-4172-a9ee-3c619e04ab0a
⛔ Files ignored due to path filters (3)
deno.lockis excluded by!**/*.lockpackage-lock.jsonis excluded by!**/package-lock.jsonpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
.github/workflows/ci.yml.gitignore.husky/pre-push.prettierignoreREADME.mddeno.jsonmessages/en.jsonmessages/ko.jsonpackage.jsonscripts/generate-og-images.tsscripts/sync-from-3b.tsscripts/translation-create.tsscripts/translation-status.tsscripts/validate-dates.ts
Session log entry for the npm → pnpm migration (PR #3) covering all 6 commits on chore/migrate-npm-to-pnpm, the verification pipeline results (lint/format/build/check/validate:dates + deno sync), and the pending gate on Cloudflare Pages preview deploy. Also adds the milestone bullet at the top of PROGRESS.md.
Summary
"packageManager"field inpackage.jsonso corepack installs the exact version on every contributor machine and CI runner..github/workflows/ci.yml,.husky/pre-push,deno.jsontasks,package.jsonscripts,README.md, error messages insidescripts/*.ts, and thesearch_dev_noticeUI string in bothmessages/en.jsonandmessages/ko.json— to callpnpminstead ofnpm.node_moduleslayout: declare@types/mdastandvfileas explicit devDependencies. Three remark plugins (remark-reading-time,remark-mermaid-component,remark-toc-extract) were importing these types via JSDoc without declaring the packages — they worked accidentally under npm's flat-hoist layout but brokesvelte-checkunder pnpm. This is a real correctness win, not a regression.Why
mdast,vfile) that npm's hoisting was silently hiding. Would have eventually broken under a dependency bump or toolchain upgrade.pnpm-lock.yamlis ~35% smaller thanpackage-lock.json(4,833 vs 7,469 lines) for the same dependency tree — content-addressing instead of duplicated version metadata per install path.Verification
Ran the full Husky
pre-pushpipeline locally end-to-end under pnpm — all 5 steps pass:pnpm lintpnpm format:checkpnpm buildpnpm checkpnpm validate:datesAlso verified the hybrid npm + deno setup still works end-to-end:
deno task sync --check→ 105/105 hash matches, 0 mismatches.scripts/sync-from-3b.ts) imports only fromhttps://deno.land/std@0.220.0/...URLs, never fromnode_modules/— so pnpm's symlinked layout has zero impact on the deno side of this project.Related changes
4b0bcb4(already pushed tomain):docs(brandonwie-dev): update package manager refs to pnpm. Updates the project CLAUDE.md (symlink target) so Claude's per-project instructions reflect the new build commands.Closes #Nfooter.Commits (5 atomic)
76fcbbe—chore(deps): migrate from npm to pnpm— deletepackage-lock.json, addpnpm-lock.yaml, addpackageManagerfield, flip.gitignoreto ignore npm lockfile instead.3a916ce—chore(build): update CI, hooks, and tasks to use pnpm— CI workflow, Husky pre-push,deno.jsontasks,package.jsonbuild script.d1ad882—chore: replace remaining npm/npx references with pnpm— script error messages, JSDoc usage blocks, shebangs, i18n UI strings, README.601df5c—chore(lint): exclude lockfiles from prettier— caught by running.husky/pre-pushlocally; prettier's YAML handler would have corruptedpnpm-lock.yaml.205cd9c—chore(deps): declare @types/mdast and vfile as devDependencies— phantom-dep fix exposed by pnpm's isolated layout.요약 (Korean)
package.json의"packageManager"필드에 버전을 고정했습니다..github/workflows/ci.yml,.husky/pre-push,deno.json의 task,package.json의 script,README.md,scripts/*.ts내부의 에러 메시지,messages/en.json및messages/ko.json의search_dev_noticeUI 문자열)이npm대신pnpm을 호출하도록 업데이트했습니다.node_moduleslayout이 드러낸 잠재적 phantom dependency 버그를 수정했습니다.@types/mdast와vfile을 devDependency로 명시적으로 선언했습니다. 세 개의 remark 플러그인(remark-reading-time,remark-mermaid-component,remark-toc-extract)이 JSDoc을 통해 이 타입들을 import하면서도 해당 패키지를 선언하지 않고 있었는데, npm의 flat-hoist layout에서는 우연히 동작했지만 pnpm에서는svelte-check가 실패했습니다. 이는 회귀가 아니라 실제 정확성 개선입니다.검증
로컬에서 Husky
pre-push파이프라인을 pnpm으로 전체 실행했으며, 5단계 모두 통과했습니다.pnpm lintpnpm format:checkpnpm buildpnpm checkpnpm validate:dates또한 하이브리드 npm + deno 구성이 여전히 정상 동작함을 확인했습니다.
deno task sync --check는 105/105 해시 일치로 통과했습니다.scripts/sync-from-3b.ts는https://deno.land/stdURL만 import하고node_modules/를 사용하지 않으므로, pnpm의 symlink layout이 deno 측에 영향을 주지 않습니다.Type of Change
Merge Strategy
Use "Create a merge commit" (repo default — no
develop_to_main: squashconfigured in PROJECT-CONFIG).Summary by CodeRabbit
Chores
Documentation