Skip to content

Commit 6c49b71

Browse files
authored
fix(jsdom): support Blob on jsdom 30.1 (#11379)
1 parent aafc099 commit 6c49b71

7 files changed

Lines changed: 181 additions & 118 deletions

File tree

‎examples/lit/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
},
1919
"devDependencies": {
2020
"@vitest/browser-playwright": "latest",
21-
"jsdom": "^29.1.1",
21+
"jsdom": "^30.1.1",
2222
"playwright": "^1.62.1",
2323
"vite": "latest",
2424
"vitest": "latest"

‎examples/projects/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@vitejs/plugin-react": "^5.2.0",
1717
"@vitest/ui": "latest",
1818
"fastify": "^5.12.1",
19-
"jsdom": "^29.1.1",
19+
"jsdom": "^30.1.1",
2020
"react": "^19.2.8",
2121
"supertest": "^7.2.2",
2222
"tsx": "^4.23.13",

‎packages/vitest/package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
"@jridgewell/trace-mapping": "catalog:",
188188
"@opentelemetry/api": "^1.9.1",
189189
"@sinonjs/fake-timers": "15.4.0",
190-
"@types/jsdom": "^28.0.3",
190+
"@types/jsdom": "^30.0.0",
191191
"@types/node": "^24.13.3",
192192
"@types/picomatch": "^4.0.3",
193193
"@types/prompts": "^2.4.9",
@@ -204,7 +204,7 @@
204204
"cac": "catalog:",
205205
"flatted": "catalog:",
206206
"happy-dom": "^20.12.0",
207-
"jsdom": "^29.1.1",
207+
"jsdom": "^30.1.1",
208208
"local-pkg": "^1.2.1",
209209
"mime": "^4.1.0",
210210
"pathe": "catalog:",

‎packages/vitest/src/integrations/env/jsdom.ts‎

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ConstructorOptions, DOMWindow, VirtualConsole as IVirtualConsole } from 'jsdom'
22
import type { Environment } from '../../types/environment'
33
import type { JSDOMOptions } from '../../types/jsdom-options'
4+
import { createRequire } from 'node:module'
45
import { URL as NodeURL } from 'node:url'
56
import { populateGlobal } from './utils'
67

@@ -292,12 +293,31 @@ interface CompatUtils {
292293
makeCompatFormData: (formData: FormData) => FormData
293294
}
294295

296+
// jsdom keeps Blob bytes on an internal "impl" object and exposes no synchronous
297+
// public way to read them, so this reaches into its generated bindings
298+
function createBlobImplGetter(window: DOMWindow): (blob: Blob) => any {
299+
const _require = createRequire(import.meta.url)
300+
// jsdom 28.1 moved the generated bindings; jsdom has no "exports" map, so both subpaths resolve
301+
for (const id of [
302+
'jsdom/lib/generated/idl/utils.js',
303+
'jsdom/lib/jsdom/living/generated/utils.js',
304+
]) {
305+
try {
306+
const { implForWrapper } = _require(id)
307+
if (typeof implForWrapper === 'function') {
308+
return implForWrapper
309+
}
310+
} catch {}
311+
}
312+
// jsdom < 30.1 also stores the impl under an own Symbol("impl")
313+
const implSymbol = Object.getOwnPropertySymbols(new window.Blob())[0]
314+
return (blob) => (blob as any)[implSymbol]
315+
}
316+
317+
let getBlobImpl: (blob: Blob) => any
318+
295319
function createCompatUtils(window: DOMWindow): CompatUtils {
296-
// this returns a hidden Symbol(impl)
297-
// this is cursed, and jsdom should just implement fetch API itself
298-
const implSymbol = Object.getOwnPropertySymbols(
299-
Object.getOwnPropertyDescriptors(new window.Blob()),
300-
)[0]
320+
getBlobImpl ??= createBlobImplGetter(window)
301321
const utils = {
302322
window,
303323
makeCompatFormData(formData: FormData) {
@@ -312,7 +332,12 @@ function createCompatUtils(window: DOMWindow): CompatUtils {
312332
return nodeFormData
313333
},
314334
makeCompatBlob(blob: Blob) {
315-
const impl = (blob as any)[implSymbol]
335+
const impl = getBlobImpl(blob)
336+
if (!impl) {
337+
throw new TypeError(
338+
'Vitest cannot read the bytes of a jsdom Blob. This is a Vitest bug, please report it with your jsdom version.',
339+
)
340+
}
316341
// jsdom 28 renamed `_buffer` to `_bytes`
317342
return new NodeBlob_([impl._bytes ?? impl._buffer], { type: blob.type })
318343
},

0 commit comments

Comments
 (0)