Skip to content

fix: support projects that use pnpm for their formatters - #146

Draft
yuyuyuyuyu-dev wants to merge 8 commits into
mainfrom
fix/support-pnpm-projects
Draft

yuyuyuyuyu-dev wants to merge 8 commits into
mainfrom
fix/support-pnpm-projects

Conversation

@yuyuyuyuyu-dev

@yuyuyuyuyu-dev yuyuyuyuyu-dev commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Projects that install formatters (for README.md, GitHub Actions files, and so on) with pnpm could not run this plugin.

Cause

pnpm init writes devEngines.packageManager: { "name": "pnpm" } into package.json. npm reads it and stops with EBADDEVENGINES for both npm install and npx. The plugin ran both: npmInstall before the build, and npx workbox-cli generateSW after it.

Changes

  • The plugin runs npm exec --prefix <empty dir> -- workbox-cli generateSW <config>. The empty folder is build/composepwa/empty-npm-prefix of the module. With --prefix, npm treats that folder as the project, so it does not read the project's package.json, .npmrc, or node_modules. The command itself still runs in the module folder.
  • The project's .npmrc is ignored on purpose, so a project cannot change how the plugin behaves. The user's ~/.npmrc still applies, because it holds settings such as a proxy or a private registry, without which npm cannot reach the registry at all.
  • --prefix also moves where npm looks for the global npmrc (<global prefix>/etc/npmrc), so that file is not read. This is accepted: it is meant for machines shared by many users, and on a development machine Node.js is usually installed per user (Volta, nvm), where such settings go in ~/.npmrc. Passing it back with --globalconfig would need another npm process to find the file, which is not worth the risk.
  • The plugin no longer depends on npmInstall. workbox-cli is fetched by npm exec, so installing the project's own dependencies was never needed.

Why npm only, and not pnpm for pnpm projects

An earlier version of this PR ran pnpm dlx workbox-cli when package.json named pnpm. It was dropped because:

  • Running it with pnpm brings no benefit. pnpm saves disk space by sharing files across projects, but npx / npm exec also reuses one cached copy of workbox-cli (~/.npm/_npx/<hash>) across all projects.
  • Letting users choose among package managers means testing each one, which raises the maintenance cost far beyond what it gains.

--force was also tried instead of --prefix. It turns the error into a warning, but it disables other npm protections, and npm still looks at the project's node_modules: an old workbox-cli installed there is used instead of the latest one, and one installed only inside another package makes the command fail with "workbox: command not found". --prefix fixes both.

Checks

  • Manual builds of test-projects/web-app with Gradle 9.8.2 and npm 12.2.2, wasm and js, with a package.json that names pnpm in devEngines.packageManager (plain npx fails there with EBADDEVENGINES) and a fake workbox-cli in its node_modules: both service workers were generated with the real workbox-cli, no package-lock.json was created, and the empty folder stayed empty.
  • The same build with a project .npmrc pointing to a fake registry (ignored, build passes).
  • The same build with no package.json, after clean, and again with the configuration cache reused.

Not fixed here

  • If package.json also has the packageManager field (for example "pnpm@12.18.0"), the Kotlin Gradle plugin's own kotlinNpmInstall / kotlinWasmNpmInstall fails, because the Yarn 1 it downloads reads that field. This happens without ComposePWA too.

🤖 Generated with Claude Code

yuyuyuyuyu-dev and others added 8 commits November 10, 2026 15:17
npm stops with EBADDEVENGINES when the nearest package.json names pnpm
in devEngines.packageManager, so both npmInstall and npx failed there.

Look for the nearest package.json up to the root project. If it names
pnpm in devEngines.packageManager, run workbox-cli with pnpm dlx.
Otherwise keep npx. Also drop the unused npmInstall dependency and skip
pnpmSetup unless node-gradle downloads Node.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Look past the Gradle root, stop at a node_modules folder, and follow npm workspaces, so the plugin reads the same package.json that npx reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the pnpm path and the package.json detection. The plugin now runs
`npm exec --prefix <empty dir> -- workbox-cli`, so npm does not read the
project's package.json, .npmrc or node_modules. This avoids
EBADDEVENGINES in pnpm projects without a second package manager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`--prefix` also moves where npm looks for the global npmrc, so settings
such as a proxy or a private registry in it were lost. Ask npm for that
file first and pass it with `--globalconfig`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`--prefix` also moves where npm looks for the global npmrc, so settings
such as a proxy or a private registry in it were lost. Ask npm for that
file first and pass it with `--globalconfig`.

The previous commit has the wrong message: it only reverts the README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Development machines rarely rely on it, so the extra logic is not worth the risk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant