Skip to content

WEBDEV-8724: Migrate elements to pnpm - #63

Merged
jbuckner merged 7 commits into
mainfrom
WEBDEV-8724-pnpm
Aug 4, 2026
Merged

WEBDEV-8724: Migrate elements to pnpm#63
jbuckner merged 7 commits into
mainfrom
WEBDEV-8724-pnpm

Conversation

@jbuckner

@jbuckner jbuckner commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Migrates the repo from npm to pnpm 11. Same setup as the offshoot migration (WEBDEV-8502).

  • packageManager + engines pins (pnpm 11.13.0)
  • pnpm-workspace.yaml with nodeLinker: isolated and the WEBDEV-8544 supply-chain cooldown (14 days), @internetarchive/* excluded
  • pnpm-lock.yaml replaces package-lock.json; .npmrc sets package-lock=false
  • CI installs/tests with pnpm; the publish step stays on npm publish so provenance keeps working
  • prettier pinned to 3.6.2 so regenerating the lockfile didn't jump to 3.9 and reformat the codebase

nodeLinker: isolated means each package only sees the deps it declares. That caught three phantom imports in eslint.config.mjs: @typescript-eslint/parser, @eslint/js, and @eslint/eslintrc. They were resolving only because eslint and @typescript-eslint/eslint-plugin pull them in and hoisting flattened them to the root. All three are declared now, at the versions that were already being hoisted, so nothing changed at runtime.

Versions are refreshed within the existing ranges: vitest 4.1.10, typescript-eslint 8.64.0, tsc-alias 1.9.1, eslint 9.39.5. No package.json ranges changed, so lit keeps its ^2.8.0 || ^3.3.2 dual support. The pnpm pin is 11.13.0 and not the latest 11.17.0 because 11.13.0 is the newest release over 14 days old, so the package manager clears the same cooldown bar we set for dependencies.

No pnpm-only preinstall guard: this is a published library, and a dependency's preinstall runs during consumers' installs (npm would fail, pnpm would warn). packageManager + the committed lockfile still steer devs to pnpm.

Local: install / build / madge / 164 tests all green under isolated. No runtime code touched. pnpm run lint still exits 1 on pre-existing prettier drift in demo/app-root.ts and demo/story-template.test.ts. Both are already red on main and untouched here, so they need their own ticket.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nou2gTKyfJ1Z2gL4RjVqdS

Switch the repo from npm to pnpm 11.

- packageManager + engines pins, and a preinstall guard
  (support/only-pnpm.cjs) so npm/yarn/bun installs fail fast
- .npmrc drops package-lock; pnpm-workspace.yaml uses nodeLinker: hoisted
  and the WEBDEV-8544 supply-chain cooldown (14 days), with
  @internetarchive/* excluded
- allowBuilds: esbuild, which vite and vitest need
- pnpm-lock.yaml replaces package-lock.json
- CI installs/tests with pnpm; publish stays on npm so provenance keeps
  working
- pinned prettier to 3.6.2 so regenerating the lockfile didn't pull 3.9
  and reformat the whole codebase
- README dev/publish steps use pnpm

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTzdCHTRpPpn9VUnFCFn3R
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-04 21:57 UTC

@codecov-commenter

codecov-commenter commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.62%. Comparing base (a1b6049) to head (e0a7fb3).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #63   +/-   ##
=======================================
  Coverage   78.62%   78.62%           
=======================================
  Files          17       17           
  Lines         697      697           
  Branches      189      189           
=======================================
  Hits          548      548           
  Misses        100      100           
  Partials       49       49           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

jbuckner and others added 2 commits July 14, 2026 12:51
npm runs a dependency's preinstall script, and the guard shipped in the
published package.json, so `npm install @internetarchive/elements` failed
for consumers: the guard file isn't in the tarball, so node hit
MODULE_NOT_FOUND and the install aborted. pnpm consumers installed fine
but got a spurious "ignored build scripts" warning naming elements.

The guard belongs in an app, not a published library. packageManager,
engines, and the committed pnpm lockfile still steer devs to pnpm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTzdCHTRpPpn9VUnFCFn3R
Switch nodeLinker from hoisted to isolated so each package only sees the
deps it declares. That immediately caught three phantom imports in
eslint.config.mjs (@typescript-eslint/parser, @eslint/js,
@eslint/eslintrc) which were only resolving because eslint and
@typescript-eslint/eslint-plugin pull them in and hoisting flattened them
to the root. They're declared now, at the versions that were already
being hoisted, so nothing changes at runtime.

Also bump the pnpm pin to 11.13.0 and refresh the lockfile within the
existing ranges (vitest 4.1.10, typescript-eslint 8.64.0, tsc-alias
1.9.1, eslint 9.39.5). 11.13.0 is the newest pnpm that's more than 14
days old, so the package manager itself clears the same cooldown bar
WEBDEV-8544 set for dependencies. Ranges in package.json are untouched,
so lit keeps its ^2.8.0 || ^3.3.2 dual support and prettier stays pinned
at 3.6.2.

Install, build, madge, and all 164 tests pass under isolated linking.
`pnpm run lint` still exits 1 on pre-existing prettier drift in
demo/app-root.ts and demo/story-template.test.ts, which is already red on
main and untouched here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nou2gTKyfJ1Z2gL4RjVqdS
Copilot AI review requested due to automatic review settings July 28, 2026 23:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the repository’s package management and CI install/test flows from npm to pnpm 11, aligning local development and GitHub Actions with a pnpm lockfile–driven workflow while keeping npm for publishing/provenance.

Changes:

  • Pin pnpm via packageManager/engines, and pin Prettier to avoid lockfile-induced formatting churn.
  • Add pnpm workspace configuration (nodeLinker, supply-chain cooldown, approved build scripts) and prevent package-lock.json creation via .npmrc.
  • Update GitHub Actions workflows and developer docs to install/build/test using pnpm with lockfile freezing.

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Updates dev/versioning commands to pnpm.
pnpm-workspace.yaml Adds pnpm workspace-level settings (linker, minimum release age, build approvals).
package.json Pins pnpm via packageManager/engines and pins Prettier version.
.npmrc Enforces engines and disables package-lock.json generation.
.github/workflows/pr-preview.yml Switches PR preview build/install to pnpm + pnpm caching.
.github/workflows/npm-publish.yml Switches install/test to pnpm (publish remains npm).
.github/workflows/gh-pages-main.yml Switches GH Pages build/install to pnpm + pnpm caching.
.github/workflows/ci.yml Switches CI install/test to pnpm + pnpm caching and pnpm exec for Playwright.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md Outdated
Comment thread pnpm-workspace.yaml
Step 2 told you to push the tag created in the "npm version" step, but
step 1 is `pnpm version` now. Caught by Copilot on the PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nou2gTKyfJ1Z2gL4RjVqdS
Copilot AI review requested due to automatic review settings August 3, 2026 21:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

Workflow step ordering and the newly strict Node engine requirement are likely to cause avoidable CI/dev friction and should be addressed before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (6)

.github/workflows/ci.yml:16

  • pnpm/action-setup runs before actions/setup-node, so pnpm gets installed/configured using the runner’s default Node runtime rather than the pinned Node 24. Swapping the order avoids subtle version/caching mismatches and matches the action’s recommended usage pattern.
    - uses: actions/checkout@v6
    - uses: pnpm/action-setup@v4
    - uses: actions/setup-node@v6

.github/workflows/npm-publish.yml:16

  • pnpm/action-setup is currently executed before actions/setup-node, which means pnpm is installed/configured under the runner’s default Node rather than the workflow’s pinned Node 24. Reordering these steps avoids subtle runtime/caching mismatches.
      - uses: actions/checkout@v6
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v6

.github/workflows/pr-preview.yml:26

  • pnpm/action-setup is placed before actions/setup-node, so pnpm is installed/configured using the runner’s default Node rather than the pinned Node 24. Reordering keeps tool installation consistent with the Node version used for the build.
      - name: Checkout
        uses: actions/checkout@v6
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v6

.github/workflows/gh-pages-main.yml:29

  • pnpm/action-setup runs before actions/setup-node, so pnpm is installed/configured under the runner’s default Node instead of the pinned Node 24. Swap the order so pnpm setup happens after Node is set up.
      - uses: pnpm/action-setup@v4

      - name: Set up Node 🟢
        uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: pnpm

package.json:10

  • Setting engines.node to >=24 (combined with engine-strict=true in .npmrc) hard-requires Node 24+ for all contributors, even though key dev tooling here (e.g. Vite 7.3.6) declares support for Node ^20.19.0 || >=22.12.0 in the lockfile. If Node 24-only is not intentional, consider aligning the engine range with the toolchain’s declared minimums to avoid blocking otherwise-supported environments.
    "node": ">=24",

README.md:92

  • Since the repo now enforces tool versions via packageManager/engines (and engine-strict), it’s helpful to call out the required Node/pnpm versions in the Development snippet so contributors understand install failures immediately.
pnpm install
pnpm run dev
  • Files reviewed: 8/10 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@jbuckner jbuckner left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the six suppressed comments. Two are useful, four are wrong.

The pnpm/action-setup before actions/setup-node ordering (4 comments): keeping it. That order is required, not incidental. All four workflows set cache: pnpm on setup-node, and that cache resolution shells out to pnpm store path, so pnpm has to be on PATH before setup-node runs. Swapping them fails with Unable to locate executable file: pnpm. It is also the order pnpm's own CI docs prescribe, and CI is green on this PR as written.

The stated mechanism is a misunderstanding too. pnpm/action-setup installs a standalone pnpm shim, it does not bind pnpm to whichever Node happened to be active at install time. setup-node then puts Node 24 on PATH for every later step, which is where pnpm install and pnpm run test actually run.

README Node/pnpm versions: done. Added to the Development section, including the engine-strict=true note so a failed install is self-explanatory.

engines.node: ">=24": fair flag, and it matters more than the comment says. Not contributor friction, consumer friction. This is a published package, so npm checks a consumer's Node against our engines and emits EBADENGINE, and any consumer with engine-strict=true gets a hard failure. Node 24 is what the dev toolchain needs, not what the compiled dist/ needs. Deciding this one before merge.

engine-strict makes an older Node or a different package manager fail the
install outright, so say what's required up front instead of leaving
people to decode the error. Suggested by Copilot on the PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nou2gTKyfJ1Z2gL4RjVqdS
Copilot AI review requested due to automatic review settings August 3, 2026 22:00

@jbuckner jbuckner left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decision on engines.node: keeping >=24.

It states what we build and test against, and every IA consumer is on Node 24 already. Consumers below that get an EBADENGINE warning rather than a failure, unless they set engine-strict themselves, which is an acceptable trade for having the field say something true about our support.

Worth recording that the alternative I looked at, loosening to vite's >=20.19.0, was wrong in both directions: the shipped dist/ is es2021 ESM whose runtime deps declare almost no Node floor at all, so 20.19 still overclaims for consumers, and it sits below pnpm 11's own >=22.13 so it would not have gated contributors either.

README now states the Node 24 / pnpm 11 requirement and the engine-strict behavior (79c0a89).

Resolves the one conflict: 0.2.11 (#69) bumped package-lock.json on main
and this branch deletes it, so the deletion wins. package.json auto-merged
and picked up the 0.2.11 version along with #65's ia-button changes; the
pnpm bits, the lit dual range and the prettier pin all came through
unchanged. Lockfile needed no update since neither commit touched deps.

The PR had gone CONFLICTING, which is also why App CI stopped running on
the last two pushes: GitHub can't build a merge ref for a dirty PR, so the
pull_request workflows never fired.

Clean frozen install, build, madge and 164 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nou2gTKyfJ1Z2gL4RjVqdS

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

CI workflows install pnpm before setting up the pinned Node version (risking non-deterministic installs), and the README overstates what engine-strict enforces regarding package managers.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (5)

README.md:91

  • .npmrc's engine-strict=true enforces the engines checks (notably Node), but it doesn't reliably prevent using a different package manager (npm typically ignores packageManager and engines.pnpm). This sentence may mislead contributors into thinking npm install will be blocked purely because it's npm.
Requires Node 24+ and pnpm 11+. `.npmrc` sets `engine-strict=true`, so an older
Node or a different package manager fails the install rather than warning.

.github/workflows/ci.yml:19

  • pnpm/action-setup is run before actions/setup-node, which means pnpm may be installed using whatever Node version is preinstalled on the runner (not the pinned Node 24). For reproducibility, set up Node first, then install pnpm.
    - uses: pnpm/action-setup@v4
    - uses: actions/setup-node@v6
      with:
        node-version: 24
        cache: pnpm

.github/workflows/npm-publish.yml:16

  • pnpm/action-setup should come after actions/setup-node so pnpm is installed under the intended Node version (24) rather than the runner's default Node. This avoids subtle mismatches if the runner image changes.
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v6

.github/workflows/gh-pages-main.yml:26

  • pnpm/action-setup runs before actions/setup-node, so pnpm may be installed with the runner's default Node instead of the pinned Node 24. Reordering improves determinism across runner image updates.
      - uses: pnpm/action-setup@v4

      - name: Set up Node 🟢
        uses: actions/setup-node@v6

.github/workflows/pr-preview.yml:29

  • pnpm/action-setup should be placed after actions/setup-node so pnpm is installed using the workflow's pinned Node version (24) rather than the runner default. This reduces CI flakiness when runner images update.
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: pnpm
  • Files reviewed: 8/10 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 3, 2026 22:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

The updated README currently overstates what engine-strict enforces (it won’t fail installs just due to using a different package manager), so the requirements documentation should be corrected.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (1)

README.md:91

  • The note about .npmrc/engine-strict is slightly inaccurate: engine-strict enforces the Node engine check, but it doesn’t make installs fail just because a different package manager is used (and npm won’t validate the pnpm engine field). Consider rewording so it only claims enforcement of the Node version, and point to packageManager/Corepack for pnpm selection.
Requires Node 24+ and pnpm 11+. `.npmrc` sets `engine-strict=true`, so an older
Node or a different package manager fails the install rather than warning.
  • Files reviewed: 8/10 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

The note claimed a different package manager fails the install. It doesn't.
Verified against npm 11.11.0: with engine-strict=true, an impossible
engines.node hard-errors, but an impossible engines.pnpm installs fine, so
npm never looks at the pnpm engine. Combined with dropping the preinstall
guard, `npm install` here works and just produces a hoisted tree that
diverges from pnpm-lock.yaml. Say that instead of implying it's blocked.

Caught by Copilot on the PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nou2gTKyfJ1Z2gL4RjVqdS
Copilot AI review requested due to automatic review settings August 3, 2026 22:37

@jbuckner jbuckner left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The engine-strict catch is correct and I have fixed it in e0a7fb3. Verified against npm 11.11.0 with two throwaway projects, both with engine-strict=true in .npmrc:

  • engines.pnpm: ">=99"npm install succeeds. npm never looks at the pnpm engine.
  • engines.node: ">=99"npm install hard-errors with Unsupported engine.

So engine-strict gates Node and nothing else. Combined with deliberately having no preinstall guard, npm install here genuinely works, it just yields a hoisted tree that diverges from pnpm-lock.yaml, and package-lock=false means it does not even leave a lockfile behind as evidence. The README now says that rather than implying npm is blocked.

The four workflow-ordering comments are unchanged from the previous round, so see my earlier reply: cache: pnpm on setup-node shells out to pnpm store path, so pnpm/action-setup has to run first or the step fails outright. That order is what pnpm documents and what is currently passing CI.

Unrelated to this PR, but surfaced while checking the above: README.md already fails prettier --check on main, and the lint script only globs **/*.ts, so no non-TS file is ever format-checked. Going in the same follow-up ticket as the demo/ prettier drift.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The migration changes are internally consistent across docs/config/CI and no concrete correctness issues were identified in the reviewed diffs.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 8/10 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@jbuckner

jbuckner commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

Filed the lint follow-up as WEBDEV-8836, out of scope here.

Scoped it while writing it up: 10 of 58 tracked files fail prettier --check, but only 2 of them (demo/app-root.ts, demo/story-template.test.ts) are inside the current **/*.ts glob. The other 8 are invisible today, including both of those workflow files and tsconfig.json. Two of the 10 need to be ignored rather than formatted, pnpm-lock.yaml above all, since reformatting it would fight pnpm on every install, and there is no .prettierignore in the repo yet.

So that ticket is: fix the 2, add a .prettierignore, widen the glob, then add lint to ci.yml so it stops drifting back.

@latonv latonv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice migration!

@jbuckner
jbuckner merged commit b109f9f into main Aug 4, 2026
4 checks passed
@jbuckner
jbuckner deleted the WEBDEV-8724-pnpm branch August 4, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants