Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Package path ./browser is not exported from package (Next.js) #1877

Closed
4 tasks done
xereda opened this issue Nov 21, 2023 · 18 comments
Closed
4 tasks done

Package path ./browser is not exported from package (Next.js) #1877

xereda opened this issue Nov 21, 2023 · 18 comments
Labels
bug Something isn't working needs:triage Issues that have not been investigated yet. scope:browser Related to MSW running in a browser

Comments

@xereda
Copy link

xereda commented Nov 21, 2023

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

https://github.com/xereda/nextjs-msw-example

Reproduction steps

  • clone the above repository;
  • install the dependencies (yarn install);
  • run the server (yarn dev);

Current behavior

yarn dev                             yarn dev                                                                                                                 17.8m  ter 21 nov 2023 14:52:22
yarn run v1.22.19
warning ../package.json: No license field
$ next dev
   ▲ Next.js 14.0.3
   - Local:        http://localhost:3000

warning ../package.json: No license field
 ✓ Ready in 2.4s
 ○ Compiling / ...
 ⨯ ./src/mocks/browser.js:2:0
Module not found: Package path ./browser is not exported from package /home/xereda/repositories/nextjs-msw-example/node_modules/msw (see exports field in /home/xereda/repositories/nextjs-msw-example/node_modules/msw/package.json)
  1 | // mocks/browser.js
> 2 | import { setupWorker } from "msw/browser";
  3 | import { handlers } from "./handlers";
  4 |
  5 | if (process.env.NODE_ENV === "development" && typeof window !== "undefined") {

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./src/pages/_app.js

Expected behavior

Service mock enabled.

@xereda xereda added bug Something isn't working needs:triage Issues that have not been investigated yet. scope:browser Related to MSW running in a browser labels Nov 21, 2023
@Alex-apostolo
Copy link

im having the same issue and cannot migrate because of it

@Alex-apostolo
Copy link

Alex-apostolo commented Nov 23, 2023

fixed by changing the import from ES Module to CommonJS
const { setupWorker } = require("msw/browser");. Im still having problems on my dev server since im using Vite which directly injects ESM without polyfills so it would be much appreciated if the default export for msw/browser is the mjs file

@kdn0325
Copy link

kdn0325 commented Dec 3, 2023

i'm having the same issue

Step
yarn add -msw --dev

//broswer.ts

"use client";

import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";

export const worker = setupWorker(...handlers);

image

@lgenzelis
Copy link

I'm having the same issue. It's easy to reproduce:

  1. Create a new next application
$ npx create-next-app@latest
  1. Reply "no" to everything (to reduce e.g. typescript as a possible culprit). So:
✔ What is your project named? … my-app
✔ Would you like to use TypeScript? … No
✔ Would you like to use ESLint? … No
✔ Would you like to use Tailwind CSS? … No
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … No
✔ Would you like to customize the default import alias (@/*)? … No
  1. Run
$ cd my-app
$ npm i -D msw
  1. Edit ./pages/_app.js and add import { setupWorker } from 'msw/browser' on top.
  2. Run npm run build and you'll see the error
Module not found: Package path ./browser is not exported from package /Users/my.user/my-app/node_modules/msw (see exports field in /Users/my.user/my-app/node_modules/msw/package.json)

It makes no sense, because the "exports field in /Users/my.user/my-app/node_modules/msw/package.json" clearly shows:

    "./browser": {
      "node": null,
      "types": "./lib/browser/index.d.ts",
      "require": "./lib/browser/index.js",
      "import": "./lib/browser/index.mjs",
      "default": "./lib/browser/index.js"
    },

😖😖

@skopz356
Copy link

skopz356 commented Dec 5, 2023

Same thing happens when you run dependency-cruiser:
error not-to-unresolvable: src/app/mock/worker.mock.ts → msw/browser

@tstewart-klaudhaus
Copy link

tstewart-klaudhaus commented Dec 9, 2023

Same issue here, when trying to access setupWorker from within a dynamically imported module. It works when the import is static but my build process involves some dynamic module loading like:

const myModule = await import(jsModulePath)

And in turn the module at jsModulePath has a transitive dependency on another module that imports setupWorker. Exact same error message. I can see that the export is indeed defined in package.json.

@tstewart-klaudhaus
Copy link

My workaround is to create the api mocks as a separate stand-alone module which is all statically linked and then load it in a separate script tag at runtime with the async attribute so mocking is set up before my main app script loads. This way I can drop it into a page without any impact on my main app when I need mocking, so I'm happier with this than having it linked with the main app anyway.

There do seem to be a few issues around package exports with dynamic imports, maybe related to the problems other people are having.

webpack/webpack#13865
highlightjs/highlight.js#3384

@deminoth
Copy link

This issue appears to be due to NextJS attempting to import /browser from the Node environment for SSR. I resolved this issue by using dynamic import only in the browser environment.

if (typeof window !== 'undefined') {
  const { setupWorker } = await import('msw/browser');
  

@RaflyLesmana3003
Copy link

I found duplicate issue here

#1801

try this in your next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
   webpack: (config, { isServer }) => {
      if (isServer) {
         // next server build => ignore msw/browser
         if (Array.isArray(config.resolve.alias)) { // in Next the type is always object, so this branch isn't necessary. But to keep TS happy, avoid @ts-ignore and prevent possible future breaking changes it's good to have it
            config.resolve.alias.push({ name: "msw/browser", alias: false })
            } else {
            config.resolve.alias["msw/browser"] = false
            }
         } else {
            // browser => ignore msw/node
            if (Array.isArray(config.resolve.alias)) {
            config.resolve.alias.push({ name: "msw/node", alias: false })
            } else {
            config.resolve.alias["msw/node"] = false
            }
         }
      return config;
   }};

module.exports = nextConfig

@filiphazardous
Copy link

filiphazardous commented Jan 19, 2024

I can add that the same problem is present in Nuxt as well. Will try the workarounds in this thread.
Edit: The simpler workarounds don't seem to work.
To be more specific, I'm working with Nuxt in combination with Histoire. The setup is run server side in a thread with Jsdom. For some reason require doesn't solve the problem - I still get: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './browser' is not defined by "exports" in /app/frontend/node_modules/msw/package.json

@kettanaito
Copy link
Member

I suspect this error is happening because Next.js runs client-only components in Node.js for SSR. This effectively means that a JavaScript module is evaluated in a Node.js environment. This causes webpack to treat the export conditions as those of "node", which fails "msw/browser" imports, which are completely legal imports in the client.

I'm a bit perplexed by this because even if I put a dynamic import into useEffect, Next.js still moves that import statement to the top of the module. Afaik, that's not how imports behave in ESM. This is the root cause of this issue.

The suggestion from @RaflyLesmana3003 does help but it's rather a hack and I don't recommend it. What it does it tells webpack to ignore some of MSW's exports based on the environment so webpack wouldn't error. Suppressing the error, though, doesn't mean things are working properly.

I will close this issue because it cannot be addressed on MSW's side.

@kettanaito
Copy link
Member

Also keep track of the official Next.js + MSW example (mswjs/examples#101). It's not ready yet but once it is, you will have a reference point.

@filiphazardous
Copy link

For the record, this also happens with Nuxt.js and Vite, in conjunction with Histoire (a Storybook replacement).

SSR is disabled in our configuration. This is the only package displaying this problem, so I still figure it may be connected to MSW. I'll post a comment/PR if I can pinpoint exactly how.

@kettanaito
Copy link
Member

@filiphazardous, it's a combination of MSW using export conditions and whichever compiler you have not respecting those export conditions correctly. In hindsight, export conditions seem to be extremely new and their support differs vastly across tooling.

Can you please share a reproduction repository? I would love to have one to look into the problem.

@anusreesubash
Copy link

This worked for me on Next13

import { handlers } from './handlers'

export const setUpMocks = async () => {
  if (typeof window !== 'undefined') {
    const { setupWorker } = require("msw/browser");

    const worker = setupWorker(...handlers)
  }
}

@filiphazardous
Copy link

I'm sorry, but we moved on to another solution - this was a show stopper for us.

My platform is Vue + Nuxt 3 + Vite 5. (Vite may have been at version 4 when I encountered the problem.)

@NateWilliams2
Copy link

I had this same issue in a Docusaurus project (ridiculous, I know, I promise I had a real use case). I was able to solve it with a hack similar to the one listed above (and at #1801), creating a custom Docusaurus plugin in order to modify the webpack config with the following strategy:

plugins/webpack-loader/index.js

module.exports = function (context, options) {
  return {
    name: 'webpack-loader',
    configureWebpack(config, isServer) {
      if (isServer) {
        return {
          resolve: {alias: {'msw/browser': false}},
        };
      }
    },
  };
};

This hacky solution differs from the one above because I found that adding a !isServer clause to alias 'msw/node' removed the actual msw functionality from the client. Note that I am using msw in production, not just development.

Leaving this comment in case it helps anyone in a similar situation.

@Spacylion
Copy link

Works for me with this declaration:

import {http} from 'msw';
import {generatePayloadHandler} from "./handlers/generate-payload-handler";

let setupWorker: any;
if (typeof window !== 'undefined') {
    setupWorker = require('msw/browser').setupWorker;
}

const baseUrl = document.baseURI.replace(/\/$/, '');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs:triage Issues that have not been investigated yet. scope:browser Related to MSW running in a browser
Projects
None yet
Development

No branches or pull requests