Repository navigation
Migrate client and client-v3 from npm to pnpm - #1422
Conversation
Client V3 Test Results209 tests 209 ✅ 0s ⏱️ Results for commit 0bbb13b. ♻️ This comment has been updated with latest results. |
Client Test Results95 tests 95 ✅ 0s ⏱️ Results for commit 0bbb13b. ♻️ This comment has been updated with latest results. |
Playwright E2E Results (chromium)222 tests 222 ✅ 2m 9s ⏱️ Results for commit 0bbb13b. ♻️ This comment has been updated with latest results. |
Playwright E2E Results (firefox)222 tests 222 ✅ 2m 10s ⏱️ Results for commit 0bbb13b. ♻️ This comment has been updated with latest results. |
Python Test Results 1 files 1 suites 46s ⏱️ Results for commit 0bbb13b. ♻️ This comment has been updated with latest results. |
4bd1bd8 to
9bdaaa6
Compare
Replaces npm with pnpm 12.5.1 as the package manager for both `client/` and `client-v3/` (each keeps its own independent pnpm-lock.yaml — no shared workspace). This directly addresses the recurring npm lockfile problems recorded against this repo: npm only records the optional platform/CPU package variants it actually installed locally (npm/cli #7961/#8320), so a lockfile regenerated on macOS silently drops the Linux CI variants, and separately, targeted `npm install` runs have been observed to non-deterministically drop unrelated optional dependency entries. pnpm resolves from registry metadata rather than local `node_modules`, so the lockfile is platform-independent by construction — verified by spot-checking the generated pnpm-lock.yaml for @rolldown/binding-* entries across all target platforms, not just darwin-arm64 (the machine that generated it). Electron is gone (#1416), which removes the one blocker to a clean pnpm migration: Electron Forge needs pnpm's `node-linker=hoisted`, giving up its strict node_modules; with Electron gone, both remaining projects use pnpm's default strict linker. - `packageManager: "pnpm@12.5.1"` pinned in both package.json files; `engines.npm` removed (meaningless under pnpm). - `pnpm-workspace.yaml` added per project (pnpm 10+ moved `overrides` and `onlyBuiltDependencies` out of package.json's `pnpm` field into this file). Each allowlists the install/postinstall scripts actually needed: `@parcel/watcher` (native file-watcher binding used by chokidar for HMR/watch mode), `vue-demi` (client-v3 only — selects the Vue 2 vs 3 interop shim, functionally required), `esbuild` (client only — downloads its native binary, required to run at all), `bootstrap-vue`/`core-js` (both harmless donation-message no-ops, allowed for completeness). - `client/.npmrc`: dropped `legacy-peer-deps=true` (npm-specific, no pnpm equivalent needed — pnpm warns rather than hard-fails on the pre-existing `@vue/test-utils@2.5.1` peer mismatch against vue@2.7.14; `pnpm peers check` shows the warning, but it's cosmetic: the full test suite already exercises this pairing and passes). - CI (`nodelint.yml`, `client-test.yml`, `playwright-test.yml`, `build-server.yml`) and the two Dockerfile frontend build stages switched from `actions/setup-node` + `npm ci` to `pnpm/action-setup@v4` + `actions/setup-node` (cache: pnpm) + `pnpm install --frozen-lockfile`. `client-test.yml` also picked up a pre-existing actions/checkout@v3 → v4 bump for consistency with the rest of the CI (unrelated drift, fixed while touching the file). `dependabot.yml` needs no change: its `npm` ecosystem key already covers pnpm-lock.yaml. - `scripts/update-version.sh`, `README.md`, `client-v3/README.md` and `documentation/development.md` updated to reference pnpm instead of npm. Also fixed two pre-existing doc inaccuracies noticed while editing: the root README's "Client" build instructions pointed at `client` while describing client-v3's output path, and `development.md`'s "Project Structure" section had dropped `client-v3` entirely (introduced by the Electron-removal pass in #1416, which turned "three main components: server/client/electron" into "two: server/client" without noticing client-v3 was the actual third). Testing (both projects): typecheck, ci-lint, full unit test suite, and production build all pass under pnpm. The full 221-test Playwright suite against client-v3 also passes cleanly under pnpm (confirmed with a temporary, since-reverted port/tmpfile override to rule out interference from an unrelated concurrent E2E run on this machine — PR #1420 will fix that class of collision generally, but it hasn't merged yet). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every job broke with 'No pnpm version is specified' — pnpm/action-setup resolves package_json_file relative to GITHUB_WORKSPACE (repo root) by default, not the job's defaults.run.working-directory (that setting only applies to run: steps, not uses: steps), and there's no root package.json. Pass package_json_file explicitly per job. Also fixes the SonarCloud Quality Gate failure (C Security Rating on New Code, githubactions:S7637 x8): pin pnpm/action-setup to the exact commit the v4 tag currently resolves to, rather than a mutable major-version tag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
9bdaaa6 to
b3fd9bc
Compare
Tim020
left a comment
There was a problem hiding this comment.
Review round 1
Reviewed head: b3fd9bc1899809e0a2444389ab7cb569c6837ac6
Verdict: changes needed. The migration itself is solid: CI is fully green (unit, lint, typecheck, Playwright on both browsers, Docker, and server builds on all 3 OSes), and pnpm install --frozen-lockfile works cleanly with pnpm 12.5.1. One real regression needs fixing before merge: scripts/update-version.sh breaks under pnpm 12. Engine-strict enforcement is also silently lost.
Previous round follow-up
First round, so there are no previous threads.
SonarQube
Quality gate passed. Data came from the anonymous SonarCloud API and matches the analysed commit for this head SHA.
- All 5 conditions pass: new reliability, security and maintainability ratings are all A; new duplication 0.0% (limit 3%); hotspots reviewed 100%.
- 0 issues and 0 security hotspots.
- Coverage isn't a gate condition here, and none was computed because there are no new coverable lines.
Nothing needed to pass.
New findings
- Critical: 0
- Important: 3
update-version.shfails on the second bump (reproduced).engine-strictis no longer honoured.- There are no
corepack enableinstructions.
- Suggestion: 3
- The
onlyBuiltDependenciesblocks do nothing. - The Dockerfile duplicates the pnpm version.
- A
devEnginesguard would stop accidentalnpm install.
- The
Findings that couldn't be anchored to diff lines:
-
Suggestion: some
npm/npxreferences are left over outside the diff hunks:- the
lintandci-lintscripts in bothpackage.jsonfiles (npm run format && ...); client/scripts/eslint-filter.sh:83;client/README.md(stillnpm ci/npm run ...);client-v3/e2e/README.md;- comments in
client-v3/e2e/global-setup.ts:34,client-v3/src/js/yjs/draftWriterFixture.test.ts:19andserver/test/utils/script/test_draft_writer_interop.py:11.
The scripts still work because npm ships with Node, but mixing package managers is confusing, and
npm runskips pnpm's.npmrc/workspace settings. - the
-
Note: the root
CLAUDE.mdis gitignored, so this PR can't change it, but the local copy still documentsnpm ci/ npm 11.x throughout. Worth updating locally after merge. -
Note: Dependabot's
npmecosystem does cover pnpm, but its support for pnpm 12 / lockfile v9 hasn't been verified here. Watch its first run after merge.
Strengths
pnpm/action-setupis pinned by SHA and placed beforesetup-nodewithcache: pnpmin every workflow.package_json_filekeeps the version in one place.- The
globoverride moved correctly topnpm-workspace.yaml; the lockfile resolves onlyglob@13.0.6. - The
allowBuildsentries each come with a comment explaining why the build is allowed. - Dockerfile layer order is right: manifest, lockfile and workspace yaml are copied before install, then the source.
documentation/development.mdnow correctly describes the three components and theui-old/output path. The old README text wrongly saidclientbuilds toserver/static/.client-v3'sprebuildhook still runs under pnpm 12 (verified).
🤖 Generated with Claude Code
Each finding was reproduced before fixing. - scripts/update-version.sh: pnpm 12's `pnpm version` is not a passthrough to `npm version`; it has a clean-working-tree check, so the second bump failed (ERR_PNPM_UNCLEAN_WORKING_TREE) and left a half-applied version bump. Add --no-git-checks. Verified end to end on a dirty tree. - Engine enforcement: pnpm 12 silently ignores `engine-strict` in .npmrc (an install with an impossible dependency engine succeeded). Set `engineStrict: true` in both pnpm-workspace.yaml files, delete both .npmrc files and the Dockerfile COPY for client/.npmrc. Verified it now rejects Node 24.12.0 for nopt@10.0.1, as npm's engine-strict used to. - pnpm-workspace.yaml: drop `onlyBuiltDependencies` (a no-op in pnpm 12; only `allowBuilds` counts) and set core-js/bootstrap-vue to false (their postinstalls only print donation messages). - Dockerfile: plain `corepack enable`; the version comes from `packageManager` only. Built both frontend stages locally and confirmed `pnpm --version` is 12.5.1 in each. - Add devEngines.packageManager (pnpm, onFail: error) to both package.json files so `npm install` is refused (EBADDEVENGINES) and cannot create a package-lock.json; ignore package-lock.json too. This also makes any `npm run` inside a script fail, so convert the lint/ci-lint scripts and eslint-filter.sh to pnpm. - Docs: add `corepack enable` guidance (Node 24 ships corepack switched off) to README.md, client/README.md, client-v3/README.md and documentation/development.md; convert remaining npm/npx references in client/README.md, client-v3/e2e/README.md and code comments. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review round 1: responseAll three Important findings and all three Suggestions are fixed in 65d2c7d, and each inline thread has a reply. I reproduced every claim before changing anything (details in the threads); the two that mattered most were real regressions from my original change ( Unanchored findings
Verification of the fix commit
One thing I noticed but did not change: pnpm 12.6.0 is already out, and this PR pins 12.5.1. That seems like a separate bump rather than something to fold into this PR. 🤖 Generated with Claude Code |
Tim020
left a comment
There was a problem hiding this comment.
Review round 2
Reviewed head: 65d2c7decb55d0e6612282c95d99f4002119f306. The incremental diff is b3fd9bc..65d2c7d, a fast-forward with no rebase.
Verdict: ready to merge. Every round 1 finding is fixed and verified against the code, CI is fully green on 65d2c7d (unit, lint, typecheck, Playwright 222/222 on both browsers, docker-build-and-run, server builds on 3 OSes), and the one new item below is an optional Suggestion.
Previous round follow-up
- Verified fixed: 6 of 6. Each thread has a verification reply and is resolved:
- Still open: 0. Fix incomplete: 0. Needs human decision: 0.
- Unanchored round 1 items: the leftover
npm/npxreferences are all converted. Converting the nestednpm runcalls inside thelint/ci-lintscripts turned out to be required, not just tidy, becausedevEnginesmakes npm refuse them.
SonarQube
Quality gate passed. Data came from the anonymous SonarCloud API, and the analysed commit matches 65d2c7d.
- All 5 conditions pass: new reliability, security and maintainability ratings are all A; new duplication 0.0% (limit 3%); hotspots reviewed 100%.
- 0 issues and 0 security hotspots.
Nothing needed to pass.
New findings (in the round 2 changes)
- Critical: 0
- Important: 0
- Suggestion: 1.
engines.node(>=24.0.0) is lower than the real floor the dependencies need (24.15.0), and withengineStrictthat gap now shows up as a confusing transitive-dependency error. This is not a regression; see the inline comment.
Unanchored notes:
- Dependabot on pnpm 12 / lockfile v9 is still unverified; you've said you'll watch its first run after merge.
- A pnpm 12.6.0 bump is reasonably left for a separate PR.
Strengths
- Each fix was reproduced before it was changed and verified afterwards: engine enforcement was confirmed back in the real project, both Docker stages were built locally, and
update-version.shwas run end to end on a dirty tree. The replies record that evidence, which made this round quick to verify. - The
allowBuildscomments now say, for every entry, why it runs or is skipped. documentation/development.mdexplains whynpm installis refused on purpose, which heads off confusion for new contributors.
🤖 Generated with Claude Code
Review round 2 suggestion. With engineStrict now working, the declared `>=24.0.0` was lower than what the install needs: abbrev@5.0.0 and nopt@10.0.1 (in both lockfiles) require ^24.15.0 on the 24 line, the highest floor of any dependency. Note: pnpm 12 does not enforce the root project's own `engines`, even with engineStrict (checked with an impossible `>=99`), so this is accurate documentation rather than a friendlier failure; someone on Node 24.12 still gets the transitive-dependency error. Also update "Node 24.x" in the READMEs and development docs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|



Summary
Switches
client/andclient-v3/from npm to pnpm 12.5.1 (each keeps its own independentpnpm-lock.yaml— no shared workspace). Electron's removal (#1416) cleared the way for this: Electron Forge needed pnpm'snode-linker=hoisted, which would have meant giving up pnpm's strictnode_modulesfor at least one project. With Electron gone, both remaining projects use pnpm's default strict linker.Why
Recurring npm lockfile problems recorded against this repo:
npm installruns have been observed to non-deterministically drop unrelated optional dependency entries from the lockfile.pnpm resolves from registry metadata rather than local
node_modules, so the lockfile is platform-independent by construction. Verified by spot-checking the generated lockfile for@rolldown/binding-*entries across all target platforms, not just darwin-arm64 (the machine that generated it).What changed
packageManager: "pnpm@12.5.1"pinned in bothpackage.json;engines.npmremoved.pnpm-workspace.yamladded per project (pnpm 10+ movedoverrides/onlyBuiltDependenciesout ofpackage.json'spnpmfield into this file — a real breaking change I hit while doing this migration). Each allowlists only the install/postinstall scripts actually needed, with a comment on each explaining what it does:@parcel/watcher(both) — native file-watcher binding used by chokidar for HMR/watch modevue-demi(client-v3) — selects the Vue 2 vs 3 interop shim; functionally requiredesbuild(client) — downloads its native binary; required to run at allbootstrap-vue,core-js(both) — harmless donation-message no-ops, allowed for completenessclient/.npmrc: droppedlegacy-peer-deps=true(npm-specific). pnpm warns rather than hard-fails on the pre-existing@vue/test-utils@2.5.1peer mismatch againstvue@2.7.14—pnpm peers checkshows the warning, but it's cosmetic; the full test suite already exercises this pairing and passes.nodelint.yml,client-test.yml,playwright-test.yml,build-server.yml) and both Dockerfile frontend build stages switched topnpm/action-setup@v4+actions/setup-node(cache: pnpm) +pnpm install --frozen-lockfile.client-test.ymlalso picked up a pre-existingactions/checkout@v3→v4bump for consistency (unrelated drift, fixed while touching the file).dependabot.ymlneeds no change — itsnpmecosystem key already coverspnpm-lock.yaml.scripts/update-version.sh,README.md,client-v3/README.md,documentation/development.mdupdated to reference pnpm. Also fixed two pre-existing doc inaccuracies noticed while editing: the root README's "Client" build instructions pointed atclientwhile describing client-v3's output path, anddevelopment.md's "Project Structure" section had droppedclient-v3entirely (introduced by the Electron-removal pass in Remove the Electron desktop app (web-only again) #1416, which turned "three main components: server/client/electron" into "two: server/client" without noticing client-v3 was the actual third).Testing
Both projects, from a clean
pnpm install --frozen-lockfile:ci-lint, full unit test suite, and production build all pass.Not covered here
client/still carries an@vue/test-utilspeer-dependency warning (see above) — pre-existing, not introduced by this change, and not a functional problem.🤖 Generated with Claude Code