Skip to content

fix(create-cloudflare): generate valid pnpm-workspace.yaml for pnpm 9/10#13967

Draft
matingathani wants to merge 4 commits into
cloudflare:mainfrom
matingathani:fix/create-cloudflare-pnpm-ignored-builds
Draft

fix(create-cloudflare): generate valid pnpm-workspace.yaml for pnpm 9/10#13967
matingathani wants to merge 4 commits into
cloudflare:mainfrom
matingathani:fix/create-cloudflare-pnpm-ignored-builds

Conversation

@matingathani
Copy link
Copy Markdown
Contributor

@matingathani matingathani commented May 19, 2026

Fixes #13928.

create-cloudflare ran pnpm install without a pnpm-workspace.yaml. pnpm 10+ blocks build scripts by default, so installs failed with ERR_PNPM_IGNORED_BUILDS for packages like esbuild, workerd, and sharp that require build scripts.

Changes

  • writePnpmWorkspaceYaml(projectPath) is called before pnpm install in npmInstall()
  • For pnpm 10+: writes allowBuilds with proper YAML boolean values (not quoted strings)
  • For pnpm 9.x: writes onlyBuiltDependencies list
  • Does nothing if the file already exists, the PM is not pnpm, or pnpm < 9

  • Tests
    • Tests included/updated
  • Public documentation
    • Documentation not necessary because: this is a fix for a scaffolding bug, no public API or user-facing CLI flags changed

pnpm 10+ blocks build scripts by default; without an explicit allow-list,
`pnpm install` exits with ERR_PNPM_IGNORED_BUILDS. Before running install,
write a pnpm-workspace.yaml that opts in the packages that need build
scripts (esbuild, workerd, sharp):
  - pnpm ≥ 10: allowBuilds map with boolean values
  - pnpm 9.x: onlyBuiltDependencies list

Does nothing if the file already exists or the PM is not pnpm.

Fixes cloudflare#13928
Copilot AI review requested due to automatic review settings May 19, 2026 01:03
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

🦋 Changeset detected

Latest commit: f0851cd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
create-cloudflare Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk May 19, 2026
@workers-devprod workers-devprod requested review from a team and emily-shen and removed request for a team May 19, 2026 01:04
@workers-devprod
Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • packages/create-cloudflare/src/helpers/tests/packages.test.ts: [@cloudflare/wrangler]
  • packages/create-cloudflare/src/helpers/packages.ts: [@cloudflare/wrangler]

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a writePnpmWorkspaceYaml helper that is invoked before pnpm install in create-cloudflare's npmInstall() flow, intending to fix #13928 (pnpm 10+ aborting scaffolds with ERR_PNPM_IGNORED_BUILDS). Depending on the detected pnpm major version, it writes either an onlyBuiltDependencies list (pnpm 9) or an allowBuilds map of booleans (pnpm 10+) for esbuild, workerd, and sharp. A new unit-test file covers each branch.

Changes:

  • Add writePnpmWorkspaceYaml(projectPath) that detects the package manager and pnpm version (via semver) and writes a pnpm-workspace.yaml if one doesn't exist.
  • Call the helper from npmInstall() before running pnpm install.
  • Add packages.test.ts with branch coverage for non-pnpm, pnpm <9, pnpm 9.x, pnpm 10+, and the "file exists" short-circuit.

Reviewed changes

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

File Description
packages/create-cloudflare/src/helpers/packages.ts Adds writePnpmWorkspaceYaml and wires it into npmInstall.
packages/create-cloudflare/src/helpers/tests/packages.test.ts New unit tests for the helper's branches.
Comments suppressed due to low confidence (2)

packages/create-cloudflare/src/helpers/packages.ts:42

  • Using allowBuilds as the pnpm 10+ setting key is very likely incorrect. pnpm did not rename onlyBuiltDependencies in 10.x — that key is still the documented mechanism for approving install scripts in both pnpm-workspace.yaml and package.json, and its value is still a list of package names (not a map of booleans). The pnpm docs URL referenced in the comment (https://pnpm.io/settings#allowbuilds) does not correspond to a real setting. If allowBuilds is not a recognized key, pnpm 10 will silently ignore it and pnpm install will still fail with ERR_PNPM_IGNORED_BUILDS, meaning this PR does not actually fix #13928 on pnpm 10/11. Please verify against the pnpm 10 release notes / settings docs and, unless there is concrete evidence such a key exists, write onlyBuiltDependencies as a list for both the pnpm 9 and pnpm 10+ branches (the format is the same across versions).
	if (semver.gte(version, "10.0.0")) {
		// pnpm 10+: allowBuilds uses a map of package -> boolean
		const entries = PNPM_BUILT_DEPENDENCIES.map((pkg) => `  ${pkg}: true`).join(
			"\n"
		);
		content = [
			"# Approve build scripts for packages that require them.",
			"# See: https://pnpm.io/settings#allowbuilds",
			"allowBuilds:",
			entries,
			"",
		].join("\n");

packages/create-cloudflare/src/helpers/packages.ts:43

  • semver.gte(version, "10.0.0") will throw TypeError: Invalid Version if which-pm-runs returns a non-semver-coercible version (or undefined, which can happen when pnpm cannot be parsed from the user agent). Existing code in this package generally treats version as opaque and doesn't compare it. Consider using semver.coerce(version) or semver.gte(version, "10.0.0", { loose: true }) and guarding for a missing/invalid version so a malformed PM version string does not crash npmInstall for all users.
	if (semver.gte(version, "10.0.0")) {
		// pnpm 10+: allowBuilds uses a map of package -> boolean
		const entries = PNPM_BUILT_DEPENDENCIES.map((pkg) => `  ${pkg}: true`).join(
			"\n"
		);
		content = [
			"# Approve build scripts for packages that require them.",
			"# See: https://pnpm.io/settings#allowbuilds",
			"allowBuilds:",
			entries,
			"",
		].join("\n");
	} else if (semver.gte(version, "9.0.0")) {

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


const { npm } = detectPackageManager();

writePnpmWorkspaceYaml(ctx.project.path);
Comment on lines +7 to +14
vi.mock("node:fs");
vi.mock("which-pm-runs");

describe("writePnpmWorkspaceYaml", () => {
beforeEach(() => {
mockPackageManager("npm");
vi.mocked(existsSync).mockReturnValue(false);
vi.mocked(writeFileSync).mockImplementation(() => undefined);
import { existsSync, writeFileSync } from "node:fs";
import { writePnpmWorkspaceYaml } from "helpers/packages";
import { beforeEach, describe, expect, test, vi } from "vitest";
import whichPMRuns from "which-pm-runs";
Comment on lines +25 to +28
const workspaceYamlPath = nodePath.join(projectPath, "pnpm-workspace.yaml");
if (existsSync(workspaceYamlPath)) {
return;
}
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

@@ -0,0 +1,72 @@
import { existsSync, writeFileSync } from "node:fs";
import { writePnpmWorkspaceYaml } from "helpers/packages";
import { beforeEach, describe, expect, test, vi } from "vitest";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Test imports expect from vitest instead of using test context destructuring

The AGENTS.md rule explicitly states: "expect must come from test context — never import { expect } from "vitest". Use destructured test context: it("name", ({ expect }) => { ... })". This test file imports expect from vitest at line 3 and all six test callbacks use bare () => instead of ({ expect }) =>. Every other test file in the same directory (e.g., packages/create-cloudflare/src/helpers/__tests__/args.test.ts, packageManagers.test.ts, json.test.ts) correctly uses the ({ expect }) pattern.

Prompt for agents
The test file at packages/create-cloudflare/src/helpers/__tests__/packages.test.ts violates the mandatory AGENTS.md rule that expect must come from the test context, not from a vitest import.

What needs to change:
1. Remove `expect` from the vitest import on line 3: change to `import { beforeEach, describe, test, vi } from "vitest";`
2. Update every `test("...", () => {` callback to `test("...", ({ expect }) => {` — this affects lines 17, 23, 29, 36, 49, and 64.

See the existing test files in the same directory (args.test.ts, packageManagers.test.ts, json.test.ts) for the correct pattern.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — removed expect from the vitest import and switched all test callbacks to use ({ expect }) destructuring pattern.


const { npm } = detectPackageManager();

writePnpmWorkspaceYaml(ctx.project.path);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 rectifyPmMismatch runs pnpm install without creating pnpm-workspace.yaml

The PR adds writePnpmWorkspaceYaml(ctx.project.path) to npmInstall (packages/create-cloudflare/src/helpers/packages.ts:103) but not to rectifyPmMismatch (packages/create-cloudflare/src/helpers/packageManagers.ts:130). Both functions run pnpm install. When a framework template scaffolds and installs deps with npm (creating node_modules), npmInstall at packages/create-cloudflare/src/cli.ts:155 returns early without creating the workspace yaml, then rectifyPmMismatch at packages/create-cloudflare/src/cli.ts:156 detects the lockfile mismatch, deletes node_modules, and runs pnpm install without a pnpm-workspace.yaml. On pnpm 10+ this causes the exact ERR_PNPM_IGNORED_BUILDS error this PR aims to fix, as build scripts for esbuild/workerd/sharp are silently skipped.

Prompt for agents
The writePnpmWorkspaceYaml function is called in npmInstall (packages/create-cloudflare/src/helpers/packages.ts:103) but not in rectifyPmMismatch (packages/create-cloudflare/src/helpers/packageManagers.ts:99-136). Both code paths run pnpm install and are called sequentially from cli.ts:155-156.

When a framework template creates node_modules during scaffolding, npmInstall returns early (line 97-98) without creating pnpm-workspace.yaml. Then rectifyPmMismatch detects the npm-vs-pnpm lockfile mismatch, deletes node_modules, and runs pnpm install without the workspace yaml.

To fix: add a writePnpmWorkspaceYaml(ctx.project.path) call inside rectifyPmMismatch in packageManagers.ts, before the runCommand call at line 130. You'll need to import writePnpmWorkspaceYaml from helpers/packages. Alternatively, move the writePnpmWorkspaceYaml call earlier in the cli.ts flow (e.g. before npmInstall) so it's always executed regardless of which install path is taken.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in f0851cd — added writePnpmWorkspaceYaml(ctx.project.path) to rectifyPmMismatch before the pnpm install call when npm === "pnpm".

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 19, 2026

create-cloudflare

npm i https://pkg.pr.new/cloudflare/workers-sdk/create-cloudflare@13967

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/deploy-helpers@13967

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/kv-asset-handler@13967

miniflare

npm i https://pkg.pr.new/cloudflare/workers-sdk/miniflare@13967

@cloudflare/pages-shared

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/pages-shared@13967

@cloudflare/unenv-preset

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/unenv-preset@13967

@cloudflare/vite-plugin

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/vite-plugin@13967

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/vitest-pool-workers@13967

@cloudflare/workers-auth

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/workers-auth@13967

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/workers-editor-shared@13967

@cloudflare/workers-utils

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/workers-utils@13967

wrangler

npm i https://pkg.pr.new/cloudflare/workers-sdk/wrangler@13967

@cloudflare/wrangler-bundler

npm i https://pkg.pr.new/cloudflare/workers-sdk/@cloudflare/wrangler-bundler@13967

commit: f0851cd

Copy link
Copy Markdown
Contributor

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

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

I don't think this is the correct approach for that issue.

I am still to see a proper reproduction of the problem. The issue description talks about C3 generating a bad pnpm workspace file but as demonstrated by this PR we do not generate any such file. So that seems invalid.
The subsequent comments there also don't seem related to the original issue.

This PR is generating a file to "solve" the problem but hardcoding it like this would be fragile. If we were to try to resolve the original problem, I think it would be more likely to just run something like pnpm approve-builds as part of the setup. But I am yet to be convinced that is needed.

@github-project-automation github-project-automation Bot moved this from Untriaged to In Review in workers-sdk May 19, 2026
@matingathani
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback, @petebacondarwin! I agree hardcoding package names is fragile.

A few clarifying questions before revising:

  1. pnpm approve-builds approach — appears interactive. Is there a non-interactive form create-cloudflare could call, or a --yes flag?
  2. Scope — should the fix live in create-cloudflare (pre-install step), or in the scaffolded template's package.json?
  3. Repro — reporter confirmed pnpm 10 fresh scaffolds break. Happy to provide a minimal repro if helpful before investing in a new approach.

Happy to revise once we align on direction.

@petebacondarwin
Copy link
Copy Markdown
Contributor

@matingathani - if you are able to create a reproduction that would help. I was not able to.

@petebacondarwin petebacondarwin marked this pull request as draft May 20, 2026 10:06
@petebacondarwin petebacondarwin removed the request for review from emily-shen May 20, 2026 10:06
@matingathani
Copy link
Copy Markdown
Contributor Author

Here is a minimal reproduction for @petebacondarwin.

Prerequisite: pnpm 10 (which blocks build scripts by default)

# Install pnpm 10 globally
npm install -g pnpm@10
pnpm --version  # confirm: 10.x.x

# Simulate what create-cloudflare does: install a package that needs build scripts
mkdir /tmp/c3-repro && cd /tmp/c3-repro
cat > package.json <<'PKGJSON'
{
  "name": "test-worker",
  "private": true,
  "dependencies": {
    "esbuild": "^0.24.0"
  }
}
PKGJSON

pnpm install
# Expected: ERR_PNPM_IGNORED_BUILDS — esbuild requires a build script
# which pnpm 10 blocks unless pnpm-workspace.yaml lists it under `allowBuilds`

Why create-cloudflare triggers this:
create-cloudflare runs pnpm install after scaffolding. Workers templates pull in esbuild and workerd (which both need build scripts). Without a pnpm-workspace.yaml with allowBuilds: { esbuild: true, workerd: true, sharp: true }, pnpm 10 blocks them.

Fix in this PR:
writePnpmWorkspaceYaml(projectPath) is called before pnpm install in npmInstall(). For pnpm 10+, it writes allowBuilds; for pnpm 9.x, it writes onlyBuiltDependencies.

You can also verify the fix works end-to-end using the preview build from this PR:

npm i https://pkg.pr.new/create-cloudflare@13967

@matingathani matingathani marked this pull request as ready for review May 23, 2026 18:44
@workers-devprod
Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • packages/create-cloudflare/src/helpers/tests/packages.test.ts: [@cloudflare/wrangler]
  • packages/create-cloudflare/src/helpers/packages.ts: [@cloudflare/wrangler]

@petebacondarwin
Copy link
Copy Markdown
Contributor

Here is a minimal reproduction for @petebacondarwin.

Prerequisite: pnpm 10 (which blocks build scripts by default)

# Install pnpm 10 globally
npm install -g pnpm@10
pnpm --version  # confirm: 10.x.x

# Simulate what create-cloudflare does: install a package that needs build scripts
mkdir /tmp/c3-repro && cd /tmp/c3-repro
cat > package.json <<'PKGJSON'
{
  "name": "test-worker",
  "private": true,
  "dependencies": {
    "esbuild": "^0.24.0"
  }
}
PKGJSON

pnpm install
# Expected: ERR_PNPM_IGNORED_BUILDS — esbuild requires a build script
# which pnpm 10 blocks unless pnpm-workspace.yaml lists it under `allowBuilds`

Why create-cloudflare triggers this: create-cloudflare runs pnpm install after scaffolding. Workers templates pull in esbuild and workerd (which both need build scripts). Without a pnpm-workspace.yaml with allowBuilds: { esbuild: true, workerd: true, sharp: true }, pnpm 10 blocks them.

Fix in this PR: writePnpmWorkspaceYaml(projectPath) is called before pnpm install in npmInstall(). For pnpm 10+, it writes allowBuilds; for pnpm 9.x, it writes onlyBuiltDependencies.

You can also verify the fix works end-to-end using the preview build from this PR:

npm i https://pkg.pr.new/create-cloudflare@13967

Thanks for offering this reproduction, but it doesn't actually run C3. Can you actually reproduce the error by running C3? When I do so (with pnpm 10) it doesn't error.

@petebacondarwin petebacondarwin marked this pull request as draft May 29, 2026 09:25
@petebacondarwin petebacondarwin marked this pull request as draft May 29, 2026 09:25
@matingathani
Copy link
Copy Markdown
Contributor Author

Thanks for testing. If running C3 with pnpm 10 doesn't trigger the error, the fix may not be needed — the issue could already be handled elsewhere. Happy to close this PR if you can't reproduce it. Let me know.

@matingathani matingathani force-pushed the fix/create-cloudflare-pnpm-ignored-builds branch from 3629eb4 to d366d44 Compare June 3, 2026 09:15
Also fix unused import and test-context expect per AGENTS.md rules.
Addresses Devin review comments.
@petebacondarwin
Copy link
Copy Markdown
Contributor

Hi @matingathani — thanks for filing this first and for the persistence on the repro. You had the right shape of the fix two weeks before I did.

For context: I dug into #13928 from another angle (#14193) and found that the reason this kept slipping through was a combination of two things — a pnpm 10→11 default flip (strictDepBuilds flipped to true, turning the warning into a fatal error), and a C3 e2e test gap where the matrix's pnpm 11.5.1 label was cosmetic (the install actually ran on the monorepo's pinned pnpm 10.x, where it's only a warning). That second part is why the original "can't reproduce" verdict happened on my side — sorry for the runaround.

#14193 supersedes this PR. It takes the same core approach (write pnpm-workspace.yaml with build approvals before install) but:

  • always emits allowBuilds instead of a pnpm-9/10/11 version branch. pnpm 9 doesn't actually have this bug (per the pnpm 10 docs, dependency lifecycle scripts were unblocked in v9 and only started being blocked in v10), and allowBuilds was only added in pnpm v10.26 — so a fixed "always allowBuilds" rule covers the actually-affected population (10.26+ / 11+) correctly without the version branching;
  • adds error-detection defence in depth (detect ERR_PNPM_IGNORED_BUILDS and print actionable guidance) for environments with a pre-existing pnpm-workspace.yaml;
  • closes the C3 e2e CI gap so a future pnpm-version regression can't silently slip through again.

The PR description and issue comment both credit you. Once #14193 lands you can close this one. Apologies again for the delay, and thanks for sticking with it.

@matingathani
Copy link
Copy Markdown
Contributor Author

Thank you Pete , i will have a look at it , but will take me 2-3 days to work on it because i just got my wisdom teeth removal surgery done today .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

create-cloudflare fails with ERR_PNPM_IGNORED_BUILDS on pnpm 10/11

4 participants