Skip to content

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

Merged
petebacondarwin merged 1 commit into
mainfrom
fix/vitest-pool-workers-undici-external
May 21, 2026
Merged

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

Conversation

@petebacondarwin

@petebacondarwin petebacondarwin commented May 15, 2026

Copy link
Copy Markdown
Contributor

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

changeset-bot Bot commented May 15, 2026

Copy link
Copy Markdown

🦋 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

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

✅ All changesets look good

@ask-bonk

ask-bonk Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

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

pkg-pr-new Bot commented May 15, 2026

Copy link
Copy Markdown
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

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

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.

✅ 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
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • ✅ @cloudflare/wrangler
Show detailed file reviewers

@workers-devprod workers-devprod left a comment

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.

Codeowners reviews satisfied

@github-project-automation github-project-automation Bot moved this from Untriaged to Approved in workers-sdk May 21, 2026
@petebacondarwin petebacondarwin merged commit 90092c0 into main May 21, 2026
56 checks passed
@petebacondarwin petebacondarwin deleted the fix/vitest-pool-workers-undici-external branch May 21, 2026 13:07
@github-project-automation github-project-automation Bot moved this from Approved to Done in workers-sdk May 21, 2026
penalosa pushed a commit that referenced this pull request May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

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

3 participants