11import type { ConstructorOptions , DOMWindow , VirtualConsole as IVirtualConsole } from 'jsdom'
22import type { Environment } from '../../types/environment'
33import type { JSDOMOptions } from '../../types/jsdom-options'
4+ import { createRequire } from 'node:module'
45import { URL as NodeURL } from 'node:url'
56import { 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+
295319function 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