Skip to content

[vitest-pool-workers] Stop externalizing devDependencies from the published bundle#13933

Open
petebacondarwin wants to merge 1 commit into
mainfrom
fix/vitest-pool-workers-undici-external
Open

[vitest-pool-workers] Stop externalizing devDependencies from the published bundle#13933
petebacondarwin wants to merge 1 commit into
mainfrom
fix/vitest-pool-workers-undici-external

Conversation

@petebacondarwin
Copy link
Copy Markdown
Contributor

@petebacondarwin petebacondarwin commented May 15, 2026

Fixes #13807

Summary

The bundler's external list for @cloudflare/vitest-pool-workers was hand-maintained and out of sync with package.json. undici and semver were both listed as external despite being only devDependencies, leaving dist/pool/index.mjs with a top-level import { fetch } from "undici" that only resolved at runtime because pnpm happened to hoist undici from other packages' devDependencies during local development. This PR fixes the bug at its source and adds a CI check to prevent it recurring.

Changes

@cloudflare/vitest-pool-workers

  • tsdown.config.ts now derives its external list from dependencies + peerDependencies in package.json. A devDependency can never silently end up externalized.

@cloudflare/workers-utils

  • Declares "sideEffects": false so consumers can tree-shake unused exports. With this in place, vitest-pool-workers drops the unused cloudflared / tunnel exports (and their transitive undici import) entirely.
  • Moves undici from devDependencies to dependencies — it was always a runtime dep, the manifest just didn't say so. Keeping it external (rather than bundling) avoids two-undici-instance issues for consumers like wrangler — instanceof Request/Response/Headers checks and setGlobalDispatcher / proxy configuration depend on there being a single shared undici instance.
  • Adds vitest as an optional peerDependency because ./test-helpers uses vitest's vi/beforeEach/afterEach at runtime.
  • Drops the stale "msw" entry from the tsup external list — never used.

Validator (tools/deployments/validate-package-dependencies.ts)

  • Extended to scan each public package's runtime entry points (main/module/exports/bin) for bare-specifier imports and flag any that aren't declared in dependencies or peerDependencies.
  • New IGNORED_DIST_IMPORTS allowlist convention in scripts/deps.ts for legitimate try/catch'd optional imports inside bundled libraries (e.g. @netlify/build-info probing for installed frameworks, recast's babylon fallback).
  • 53 new unit tests; 132/132 passing.

Results

Before After
vitest-pool-workers/dist/pool/index.mjs size 489 KB 125 KB
vitest-pool-workers/dist/pool/index.mjs undici references 1 (unresolvable import) 0
workers-utils/dist/index.mjs undici references 1 (unresolvable import) 1 (resolvable via runtime dep)
wrangler-dist/cli.js undici copies 1 1 (single shared instance)
Validator catches devDep externalization no yes

Fixes a latent runtime resolution bug and prevents regressions.

  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: internal build/packaging fix with no user-facing API or behavior changes.

Open in Devin Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 15, 2026

🦋 Changeset detected

Latest commit: ab1852c

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

This PR includes changesets to release 3 packages
Name Type
@cloudflare/vitest-pool-workers Patch
@cloudflare/workers-utils Patch
@cloudflare/cli-shared-helpers 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 15, 2026
@workers-devprod workers-devprod requested review from a team and emily-shen and removed request for a team May 15, 2026 13:41
@workers-devprod
Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/fix-vitest-pool-workers-external-list.md: [@cloudflare/wrangler]
  • .changeset/fix-workers-utils-sideeffects-undici.md: [@cloudflare/wrangler]
  • packages/create-cloudflare/scripts/deps.ts: [@cloudflare/wrangler]
  • packages/vitest-pool-workers/tsdown.config.ts: [@cloudflare/wrangler]
  • packages/workers-utils/package.json: [@cloudflare/wrangler]
  • packages/workers-utils/scripts/deps.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tsup.config.ts: [@cloudflare/wrangler]
  • packages/wrangler/scripts/deps.ts: [@cloudflare/wrangler]
  • pnpm-lock.yaml: [@cloudflare/wrangler]
  • tools/deployments/tests/validate-package-dependencies.test.ts: [@cloudflare/wrangler]
  • tools/deployments/validate-package-dependencies.ts: [@cloudflare/wrangler]

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

✅ All changesets look good

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 15, 2026

Confirmed — msw is not imported in workers-utils source, so removing it from the external list is correct.

Now let me think through the overall changes for any logic issues:

  1. vitest-pool-workers/tsdown.config.ts — Derives externals from dependencies + peerDependencies. This looks correct. The runtimeDepPatterns generates both exact matches and subpath regex patterns.

  2. workers-utils/package.json — Moves undici to dependencies, adds vitest as optional peer dep. Makes sense given the ./test-helpers export uses vitest APIs.

  3. workers-utils/tsup.config.ts — Removes stale msw from externals. Correct.

  4. Validator changes — The extractBareImports regex-based scanner, isBareSpecifier, validateDistImports, scanDistForExternalImports, and getEntryPointPaths are all well-structured with thorough tests.

  5. create-cloudflare/scripts/deps.ts and wrangler/scripts/deps.ts — Add IGNORED_DIST_IMPORTS allowlists for legitimate try/catch optional imports. Well-documented.

Let me check one edge case in the comment-stripping logic — the regex \/\/[^\n]* could strip // inside string literals, which could affect the subsequent import extraction. Let me think about whether this matters in practice for bundled output... In minified/bundled output, // inside strings would be rare as an import statement, and false negatives would be more concerning than false positives. The comment in the code acknowledges this is a deliberate approximation. This is acceptable for the use case.

Let me also verify one more subtle thing — the loadDepsModule function calls require() twice (once from loadExternalDependencies and once from loadIgnoredDistImports). Since require caches, this is fine — it will just return the cached module.

Let me check one more thing — the changeset bot mentions @cloudflare/cli-shared-helpers as a released package, but I don't see any changes to that package. This is likely because it depends on @cloudflare/workers-utils which is getting bumped. That's automatic changeset behavior, not something in this PR.

I've completed my review. The changes are well-structured, thoroughly tested, and address a real bug. The approach of deriving bundler externals from package.json is the right fix. The validator extension will prevent regressions. The code is clean and well-documented.

LGTM

github run

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 15, 2026

create-cloudflare

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

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@13933

miniflare

npm i https://pkg.pr.new/miniflare@13933

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@13933

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@13933

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@13933

@cloudflare/vitest-pool-workers

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

@cloudflare/workers-editor-shared

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

@cloudflare/workers-utils

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

wrangler

npm i https://pkg.pr.new/wrangler@13933

commit: ab1852c

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: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 6 additional findings.

Open in Devin Review

… published bundle

The bundler's external list for vitest-pool-workers was hand-maintained
and out of sync with package.json. undici and semver were both listed
as external despite being only devDependencies, leaving dist/pool/index.mjs
with a top-level import { fetch } from "undici" that only resolved at
runtime because pnpm happened to hoist undici from other packages'
devDependencies during local development.

The bundler now derives its external list from dependencies +
peerDependencies in package.json, so a devDependency can never silently
end up externalized.

Combined with the new "sideEffects": false declaration in
@cloudflare/workers-utils, the unused cloudflared / tunnel exports
(and their transitive undici import) are tree-shaken out of the pool
entirely. dist/pool/index.mjs no longer references undici at all and
shrinks from ~489 KB to ~125 KB.

@cloudflare/workers-utils also moves undici from devDependencies to
dependencies (it was always a runtime dep — the manifest just didn't
say so) and keeps it external so consumers don't end up with two
undici instances in their bundle, which would break instanceof checks
and setGlobalDispatcher / proxy configuration.

The validator (tools/deployments/validate-package-dependencies.ts) is
extended to scan each public package's runtime entry points
(main/module/exports/bin) for bare-specifier imports and flag any that
aren't declared in dependencies or peerDependencies. An
IGNORED_DIST_IMPORTS allowlist is added to scripts/deps.ts to handle
legitimate try/catch optional imports from bundled libraries
(e.g. @netlify/build-info probing for frameworks).
@petebacondarwin petebacondarwin force-pushed the fix/vitest-pool-workers-undici-external branch from b110bd2 to ab1852c Compare May 15, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

Missing dependency in @cloudflare/vitest-pool-workers@0.15.2

2 participants