msw: Error `Class extends value undefined is not a constructor or null` occurs after the version upgrade from 0.49.2 to the latest version

Prerequisites

Environment check

  • I’m using the latest msw version
  • I’m using Node.js version 14 or higher

Browsers

Chromium (Chrome, Brave, etc.)

Reproduction repository

private code i am not allowed to share

Reproduction steps

I run into the error during the runtime after the MSW.js version upgrade. It happens to the version between v0.49.3 to v1.0.1.

All the version 0.49.3 works fine before v0.49.3 for me.

image

AsyncEventEmitter.js:11 Uncaught TypeError: Class extends value undefined is not a constructor or null
    at __webpack_modules__../node_modules/@mswjs/interceptors/lib/utils/AsyncEventEmitter.js.__extends (AsyncEventEmitter.js:11:1)
    at AsyncEventEmitter.js:85:1
    at ./node_modules/@mswjs/interceptors/lib/utils/AsyncEventEmitter.js (AsyncEventEmitter.js:248:2)
    at __webpack_require__ (bootstrap:24:1)
    at fn (hot module replacement:62:1)
    at ./node_modules/@mswjs/interceptors/lib/Interceptor.js (Interceptor.js:16:27)
    at __webpack_require__ (bootstrap:24:1)
    at fn (hot module replacement:62:1)
    at ./node_modules/@mswjs/interceptors/lib/index.js (index.js:15:14)
    at __webpack_require__ (bootstrap:24:1)

Current behavior

package.json contains: “msw”: “1.0.1”,

The entry point of the development mode. index.dev.tsx.

...
const worker = require('@mock-api/browser').default

worker.start().then(() => {
  renderApp()
})

The file handles the worker.

import { setupWorker } from 'msw'
import handlers from './handlers'

// This configures a Service Worker with the given request handlers.
const worker = setupWorker(...handlers())

export default worker

The file handles the handlers

...
import {
  rest,
  RestRequest,
  PathParams,
  ResponseResolver,
  ResponseComposition,
  RestContext,
} from 'msw'

export const handler = (responder = rest) => [
  responder.get(
    '....',
    withAuth((req, res, ctx) =>
      res(
        ctx.status(200),
        ctx.json({ ... })
      )
    )
  ),
...

Expected behavior

Working properly.

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 12
  • Comments: 16 (3 by maintainers)

Most upvoted comments

Thanks for the insights, everyone. The root cause seems to be for the @mswjs/interceptors code ending up in your bundle when using msw import. May be a matter of a broken import somewhere in the source. Need investigation.

How to solve this

I’ve opened a work-in-progress fix at https://github.com/mswjs/interceptors/pull/382. It requires quite a lot of changes to adopt the old version of Interceptors (0.17.9) to the newer strict-event-emitter. To add insult to injury, I’ve already spent a lot of time migrating to those changes as a part of #1404.

I’m sorry to everyone blocked by this but I don’t have the capacity to dive into a week of refactoring to support a backport version. MSW 1.x is stale as I wrote countless times on Twitter, and I highly encourage you to update to msw@next. I’m certain it has this issue fixed there, just as dozens of other issues. Please read #1464 to make your migration easier (there’s a migration guideline inside).

I wish I had more time to handle these projects with proper care, providing backport fixes and such. I simply don’t. I know it will make people sad but that’s the reality. You can change it, instead of being sad, by Sponsoring MSW.

If you are blocked by this, this is the best time to migrate to msw@next. Or you can pick up https://github.com/mswjs/interceptors/pull/382, look at what I did regarding Emitter in #1404 and push those changes to the backport. Thanks for understanding.

@JimLin94 I ran into this as well.

Going through the call stack, it looks like AsyncEventEmitter.js is an iife, and it’s called with (strict_event_emitter_1.StrictEventEmitter)). However, with the latest dependency upgrades, strict-event-emitter@0.4.6 no longer has a module called StrictEventEmitter.

npm ls strict-event-emitter gives this:

npm ls strict-event-emitter             
portex-web-app@5.83.0 /Users/dustinmyers/Development/projects/portex/portex-web-app
└─┬ msw@1.1.0
  ├─┬ @mswjs/interceptors@0.17.9
  │ └── strict-event-emitter@0.2.8
  └── strict-event-emitter@0.4.6

Installing the older version - npm install strict-event-emitter@0.2.8 - get me past that error, but then I run into this error from SetupApi.ts due to the old version of strict-event-emitter not having the module Emitter any longer.

Screenshot 2023-03-14 at 11 02 57 AM

I experienced this when transitively importing msw/node in a bundle that was loaded on the client. I resolved this by removing the msw/node import from the client bundle. I ended up with mocks.client.ts and mocks.server.ts, where only mocks.server.ts imported msw/node.

I’m writing this now mostly for learning purposes since I found the problem really interesting and want to understand more how dependency resolution in this case works - I hope that is ok!

To sum up first: As others already pointed out, the problem is that @mswjs/interceptors has it’s own dependency on strict-event-emitter@0.2.8 while msw depends on strict-event-emitter@0.4.6. There is a breaking change between version 0.2.8 and 0.4.6 (which I understand according to Semantic Versioning is ok).

What puzzles me is that when I create a fresh project with CRA and install the latest version of msw my yarn.lock file and my node_modules looks exactly the same as in a project which crashes with Class extends value undefined is not a constructor or null.

The node_modules folder in the root of the project in both cases looks like this:

/strict-event-emitter                                  <- 0.4.6 in root of node_modules
/@mswjs/interceptors/node_modules/strict-event-emitter <- 0.2.8 another version inside @mswjs/interceptors

So in both cases it looks fine to me!

In the non-working app it seems now that node, when resolving the dependency in @mswjs/interceptors prioritizes the higher version of strict-event-emitter from the root node_modules folder, despite having the correct version of strict-event-emitter in the local node_modules folder.

I would expect that node searches for strict-event-emitter in all node_modules up the directory chain. But this is not the case. When I print module.paths, I can see that it only contains one node_modules (the root one):

[ '/Users/jakob/projects/not-working-app/node_modules' ]

As compared to in the working, fresh CRA app:

 [
        '/Users/jakob/projects/working-app/node_modules/@mswjs/interceptors/lib/utils/node_modules',
        '/Users/jakob/projects/working-app/node_modules/@mswjs/interceptors/lib/node_modules',
        '/Users/jakob/projects/working-app/node_modules/@mswjs/interceptors/node_modules',
        //...
        '/Users/jakob/node_modules',
        '/Users/node_modules',
        '/node_modules'
      ]

Consequently, when I change the line where strict-event-emitter is imported inside @mswjs/interceptors from

require('strict-event-emitter')

to the relative path

require('../../node_modules/strict-event-emitter')

it is importing the correct version and works as expected.

Does anybody understand why node (or any other involved tooling) is changing module.paths from one app to another? Or point me to any learning material that could help to further understand the problem?

Just did a fresh install of msw and encountered this. The latest versions are broken this way out of the box, at least for some systems.

I can confirm, same issue as @dustinmyers msw@1.1.0

HI, any updates regarding this? thank you!

Same happens here. Upgrade from 0.49.2 to 1.2.1 and the error appears. Do not know what to do. Using node version 18.15.0. Using Yarn v3.5.0 in pnp. I was forced to add strict-event-emmiter to resolutions (v0.2.8 or v0.4.6) With 0.2.8 -> TypeError: import_strict_event_emitter.Emitter is not a constructor error appears. With 0.4.6 -> TypeError: Class extends value undefined is not a constructor or null error appears

A bit of a mess.

I will rollback to 0.49.2 and wait 😃

same issue in msw@1.1.1, downgrading to 49.2 fixes the problem but msw cant be updated to typescript 5.0

I came across this same issue in Remix. I fixed it by doing this:

if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
  const { server } = require('./mocks/server');

  server.listen();
}

The typeof check is important. Otherwise, it’ll try to run this in the browser and even though process is not available in the browser, it will still be truthy and attempt to execute.