Skip to content

Commit 61a0ff1

Browse files
fix: clean up polyfill info
1 parent eff5234 commit 61a0ff1

2 files changed

Lines changed: 1223 additions & 1742 deletions

File tree

‎docs/recipes/MockServiceWorker.md‎

Lines changed: 6 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -32,82 +32,17 @@ npx expo install msw fast-text-encoding react-native-url-polyfill
3232

3333
## Step 2: Polyfill Hermes for MSW Compatibility
3434

35-
**Why this is needed:** MSW assumes a browser-like environment. On Hermes, `globalThis.fetch` is non-writable and `BroadcastChannel`, `EventTarget`, `Event`, `MessageEvent`, and `XMLHttpRequestUpload` are all missing, causing MSW to crash at runtime. See the [MSW #2367 discussion](https://github.com/mswjs/msw/issues/2367#issuecomment-4311536712) for background.
35+
**Why this is needed:** MSW assumes a browser-like environment. On Hermes, `BroadcastChannel`, `EventTarget`, `Event`, `MessageEvent`, and `XMLHttpRequestUpload` are all missing, causing MSW to crash at runtime. See the [MSW #2367 discussion](https://github.com/mswjs/msw/issues/2367#issuecomment-4311536712) for background.
3636

37-
The polyfill file below handles six things:
37+
The polyfill file below handles these things:
3838

39-
1. Imports `fast-text-encoding` and `react-native-url-polyfill` for `TextEncoder`/`URL` support
40-
2. Deletes and re-defines `globalThis.fetch` as writable so MSW can replace it
41-
3. Stubs `Event` with enough of the interface for MSW's internal event dispatching
42-
4. Stubs `EventTarget` with `addEventListener`/`removeEventListener`/`dispatchEvent`
43-
5. Stubs `MessageEvent`, `XMLHttpRequestUpload`, and `BroadcastChannel`
44-
6. Wraps `XMLHttpRequest.getAllResponseHeaders()` to never return `null` (Hermes quirk that causes MSW to crash on `.split()`)
39+
1. Imports `fast-text-encoding`, `react-native-url-polyfill` and `web-streams-polyfill` to handle missing polyfills
40+
2. Stubs `MessageEvent`, and `BroadcastChannel`
4541

4642
```js title="msw.polyfills.js"
4743
import "fast-text-encoding"
4844
import "react-native-url-polyfill/auto"
49-
50-
// Issue 1: globalThis.fetch is non-writable in Hermes.
51-
// MSW's FetchInterceptor does `globalThis.fetch = interceptedFetch`, but Hermes
52-
// exposes fetch as a non-configurable native binding. The assignment silently
53-
// fails — no error thrown, MSW reports it's listening, but no requests are
54-
// intercepted. We must delete and re-define the property as writable/configurable
55-
// before MSW is imported.
56-
const nativeFetch = globalThis.fetch
57-
try {
58-
delete globalThis.fetch
59-
globalThis.fetch = nativeFetch
60-
Object.defineProperty(globalThis, "fetch", {
61-
value: nativeFetch,
62-
writable: true,
63-
configurable: true,
64-
})
65-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
66-
} catch (error) {
67-
if (typeof globalThis.fetch === "undefined") {
68-
globalThis.fetch = nativeFetch
69-
}
70-
}
71-
72-
// Issue 2: Missing Web APIs at import time.
73-
// MSW's core module (ws.js, sse.js) references BroadcastChannel, EventTarget,
74-
// Event, and MessageEvent at module evaluation time (not inside functions).
75-
// Hermes doesn't provide these. They must be polyfilled before any MSW import.
76-
77-
if (typeof globalThis.Event === "undefined") {
78-
globalThis.Event = class Event {
79-
constructor(type, init) {
80-
this.type = type
81-
this.bubbles = init?.bubbles ?? false
82-
this.cancelable = init?.cancelable ?? false
83-
this.defaultPrevented = false
84-
}
85-
preventDefault() {
86-
this.defaultPrevented = true
87-
}
88-
stopPropagation() {}
89-
stopImmediatePropagation() {}
90-
}
91-
}
92-
93-
if (typeof globalThis.EventTarget === "undefined") {
94-
globalThis.EventTarget = class EventTarget {
95-
constructor() {
96-
this._listeners = {}
97-
}
98-
addEventListener(type, listener) {
99-
;(this._listeners[type] ??= []).push(listener)
100-
}
101-
removeEventListener(type, listener) {
102-
const list = this._listeners[type]
103-
if (list) this._listeners[type] = list.filter((l) => l !== listener)
104-
}
105-
dispatchEvent(event) {
106-
for (const listener of this._listeners[event.type] ?? []) listener(event)
107-
return true
108-
}
109-
}
110-
}
45+
import "web-streams-polyfill/dist/polyfill"
11146

11247
if (typeof globalThis.MessageEvent === "undefined") {
11348
globalThis.MessageEvent = class MessageEvent {
@@ -122,27 +57,6 @@ if (typeof globalThis.MessageEvent === "undefined") {
12257
}
12358
}
12459

125-
// MSW's XMLHttpRequest interceptor uses `target instanceof XMLHttpRequestUpload`
126-
// at runtime. Hermes doesn't expose XMLHttpRequestUpload as a global, causing a
127-
// ReferenceError. Provide a stub class so the instanceof check resolves to false
128-
// instead of throwing.
129-
if (typeof globalThis.XMLHttpRequestUpload === "undefined") {
130-
globalThis.XMLHttpRequestUpload = class XMLHttpRequestUpload {}
131-
}
132-
133-
// Hermes's XMLHttpRequest.getAllResponseHeaders() can return null instead of an
134-
// empty string when no headers are available. MSW's interceptor calls .split()
135-
// on the result, which throws "Cannot read property 'split' of null".
136-
// Wrap the method so it always returns a string.
137-
if (typeof globalThis.XMLHttpRequest !== "undefined") {
138-
const origGetAllResponseHeaders = globalThis.XMLHttpRequest.prototype.getAllResponseHeaders
139-
if (origGetAllResponseHeaders) {
140-
globalThis.XMLHttpRequest.prototype.getAllResponseHeaders = function () {
141-
return origGetAllResponseHeaders.call(this) ?? ""
142-
}
143-
}
144-
}
145-
14660
if (typeof globalThis.BroadcastChannel === "undefined") {
14761
globalThis.BroadcastChannel = class BroadcastChannel {
14862
constructor(_name) {}
@@ -155,6 +69,7 @@ if (typeof globalThis.BroadcastChannel === "undefined") {
15569
}
15670
}
15771
}
72+
15873
```
15974
16075
## Step 3: Patch `@mswjs/interceptors` for `whatwg-fetch` Compatibility

0 commit comments

Comments
 (0)