Skip to content

fix(jsdom): support Blob on jsdom 28+ and 30.1 [backport to v4] - #11384

Closed
rifflock wants to merge 1 commit into
vitest-dev:v4from
rifflock:fix/v4-jsdom-30.1-blob
Closed

rifflock wants to merge 1 commit into
vitest-dev:v4from
rifflock:fix/v4-jsdom-30.1-blob

Conversation

@rifflock

@rifflock rifflock commented Sep 28, 2026 •

Copy link
Copy Markdown

Description

Written by an AI agent operated by @rifflock, who has reviewed it.

Refs #11336

Backports #11295 and #11379 to v4.

On Vitest 4 with jsdom 28+, any Blob or File that goes through URL.createObjectURL, a Request body or FormData reads back as the string "undefined", because jsdom 28 renamed _buffer to _bytes. Tests that check upload contents still pass, because they compare against the wrong value. jsdom 30.1 also stopped storing the impl under an own Symbol(impl), so the lookup throws Cannot read properties of undefined (reading '_buffer') (#11336).

One change beyond the backport: the bindings are resolved inside the jsdom copy the environment imported (dirname(require.resolve('jsdom/package.json'))). A bare jsdom/lib/... subpath is resolved from Vitest's own location and through NODE_PATH, which pnpm points at its store, so it can land in a different jsdom whose implForWrapper returns null for these wrappers. This repo hits it: v4 tests run on jsdom 27.4 while 29.1.1 is hoisted at the root.

main has the same bug. The reproduction is in the comments below, and the same change on top of main is on rifflock:fix/jsdom-bindings-resolution.

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. If the feature is substantial or introduces breaking changes without a discussion, PR might be closed.
  • Ideally, include a test that fails without this PR but passes with it.
  • Please, don't make changes to pnpm-lock.yaml unless you introduce a new test example.
  • Please check Allow edits by maintainers to make review process faster. Note that this option is not available for repositories that are owned by Github organizations.

Tests

  • Run the tests with pnpm test:ci.

test/core/test/environments/jsdom.spec.ts was run against jsdom 27.4.0, 28.1.0, 30.0.1 and 30.1.1. With the patch all 72 tests pass on every version. Without it, 28.1.0 and 30.0.1 fail the two new byte-level tests (6 failures) and 30.1.1 fails 12. pnpm lint and pnpm typecheck pass. The full pnpm test:ci was not run.

Documentation

  • If you introduce new functionality, document it. You can run documentation with pnpm run docs command.

No new functionality.

Changesets

  • Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with feat:, fix:, perf:, docs:, or chore:.

🤖 Generated with Claude Code

@netlify

netlify Bot commented Sep 28, 2026

Copy link
Copy Markdown

✅ Deploy Preview for vitest-dev ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit ef5ef7b
🔍 Latest deploy log https://app.netlify.com/projects/vitest-dev/deploys/6abae8a59664c70008c69f02
😎 Deploy Preview https://deploy-preview-11384--vitest-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@hi-ogawa hi-ogawa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One change beyond the backport: the bindings are resolved inside the jsdom copy the environment imported (dirname(require.resolve('jsdom/package.json'))). A bare jsdom/lib/generated/idl/utils.js that doesn't exist in that copy (jsdom < 28.1) makes Node fall through to a different jsdom higher in node_modules, and that copy's implForWrapper returns null for these wrappers. This repo hits it: v4 tests run on jsdom 27.4 while 29.1.1 is hoisted at the root. main uses the same bare-subpath lookup; I haven't reproduced it there yet, but can send that fix separately.

Can you try reproduce that on main? main and v4 should have the same fix if we'd backport.

Backports vitest-dev#11295 and vitest-dev#11379. On jsdom 28+ the Blob impl stores its
bytes as `_bytes`, so every Blob passed through `URL.createObjectURL`,
a `Request` body or `FormData` became the string "undefined". jsdom
30.1 also stopped exposing the impl as an own Symbol, so the lookup
itself threw.

It also resolves jsdom's bindings inside the copy the environment
imported. A bare `jsdom/lib/...` subpath is resolved from Vitest's own
location and through NODE_PATH, which pnpm points at its store, so it
can land in a different jsdom whose `implForWrapper` returns null for
these wrappers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@rifflock
rifflock force-pushed the fix/v4-jsdom-30.1-blob branch from ef5ef7b to f19f9b4 Compare October 2, 2026 00:35
@rifflock

rifflock commented Oct 2, 2026

Copy link
Copy Markdown
Author

Written by an AI agent operated by @rifflock, who has reviewed it.

Yes — main has the same bug, though not with the versions it currently installs.

createBlobImplGetter requires the bindings by bare specifier, so Node resolves them independently of the copy import('jsdom') returned. In a pnpm workspace that search also covers NODE_PATH, which pnpm points at node_modules/.pnpm/node_modules. When the two land on different copies, implForWrapper doesn't recognise the wrapper and returns undefined.

Repro on main (ad208cc29): point packages/vitest/node_modules/jsdom at the version under test, leave the hoisted node_modules/.pnpm/node_modules/jsdom at 30.1.1, then run test/unit/test/environments/jsdom.spec.ts:

env's jsdom main with the fix
27.4.0 4 failed / 22 passed 26 passed
28.0.0 4 failed / 22 passed 26 passed
28.1.0 26 passed 26 passed
30.0.1 26 passed 26 passed
30.1.1 26 passed 26 passed

The four are Fetch API accepts other APIs, FormData > keeps Blob bytes when used as a Request body, URL.createObjectUrl works properly and URL.createObjectURL keeps the Blob bytes, each TypeError: Vitest cannot read the bytes of a jsdom Blob.

One caveat on which installs hit it: pnpm --filter vitest add -D jsdom@27.4.0 on main hoists 27.4.0 into .pnpm/node_modules too, so both lookups agree and the suite stays green. It only shows when the hoisted copy differs from the one the environment imported.

The same change on top of main is on rifflock:fix/jsdom-bindings-resolution (a0a25f1d0) — lint and typecheck pass, full pnpm test:ci not run. @rifflock can open it as a PR against main whenever you'd like; it isn't open yet because the policy allows one pull request at a time and this is it.

@rifflock

rifflock commented Oct 2, 2026

Copy link
Copy Markdown
Author

I'm re-running tests to make sure we're all good. Let me know if you want that PR to main, or if there's anything else I can do to be helpful.

@hi-ogawa

hi-ogawa commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Let me know if you want that PR to main

pls

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Hello @rifflock. Thank you for taking the time to contribute!

Unfortunately, the team accepts pull requests only from maintainers and approved contributors, so this pull request was closed automatically. We are sorry about that, it is not a judgement of your work. The number of pull requests grew beyond what the team can review, and this policy gives maintainers the space to triage and prioritize issues at their own pace.

If there is no issue for this change yet, please open one to discuss it with the team first. Your changes are not lost: a maintainer can reopen this pull request if the team decides to go forward with it. See our pull request policy for more context.

@github-actions github-actions Bot closed this Oct 5, 2026
@rifflock

rifflock commented Oct 5, 2026

Copy link
Copy Markdown
Author

Written by an AI agent operated by @rifflock, who has reviewed it.

The same change on top of main is ready on rifflock:fix/jsdom-bindings-resolution (3ea312e91). My earlier comment cited a0a25f1d0, which was amended away before the push — the branch is the reliable reference.

I haven't opened it as a PR because this one was closed automatically today, and a new PR from me would be closed the same way: I'm not on APPROVED_CONTRIBUTORS. Either /approve-user rifflock or reopening this PR unblocks it, whenever it suits you. If you'd rather not, the branch can stay as it is and a maintainer is welcome to take the commit.

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.

2 participants