Closed xereda closed 10 months ago
im having the same issue and cannot migrate because of it
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
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);
I'm having the same issue. It's easy to reproduce:
Create a new next application
$ npx create-next-app@latest
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
Run
$ cd my-app
$ npm i -D msw
Edit ./pages/_app.js
and add import { setupWorker } from 'msw/browser'
on top.
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"
},
😖😖
Same thing happens when you run dependency-cruiser:
error not-to-unresolvable: src/app/mock/worker.mock.ts → msw/browser
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.
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.
https://github.com/webpack/webpack/issues/13865 https://github.com/highlightjs/highlight.js/issues/3384
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');
…
I found duplicate issue here
https://github.com/mswjs/msw/issues/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
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
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.
Also keep track of the official Next.js + MSW example (https://github.com/mswjs/examples/pull/101). It's not ready yet but once it is, you will have a reference point.
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.
@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.
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)
}
}
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.)
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 https://github.com/mswjs/msw/issues/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.
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(/\/$/, '');
This is how I got it in my Next.js app.
// helper_functions/setup-msw-browser.js
let mswWorker;
const mockHandlers = [];
export const setupMswWorker = async () => {
if (typeof window !== 'undefined' && !mswWorker) {
const { setupWorker } = await import('msw/browser');
mswWorker = setupWorker(...mockHandlers);
return mswWorker;
}
};
Then in the _app.js
file:
import { setupMswWorker } from "helper_functions/setup-msw-browser";
import { useEffect } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
export default function App({ Component, pageProps }) {
const getLayout = Component.getLayout || (page => page);
useEffect(() => {
let mswWorker;
const startMswWorker = async () => {
mswWorker = await setupMswWorker();
await mswWorker?.start();
}
if (process.env.NODE_ENV === "development") {
startMswWorker();
}
return () => {
mswWorker?.stop();
}
}, []);
return (
<QueryClientProvider client={queryClient}>
{getLayout(<Component {...pageProps} />)}
</QueryClientProvider>
);
}
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) } }
where does this live? in the Next js app directory structure...
The solutions here to me aren't really solutions though. It's only using the browser version of msw.. My goal is to intercept requests made on the server side (within server components in NextJS) so I can tell Cypress to wait for them to resolve before running any assertions. You can't do this when only starting the browser worker and not the node server in msw. Unless my understanding of this package is not correct? And there is no way to intercept/wait for server-side requests within Cypress. I thought msw might be the answer, but maybe it's not.
### Add below code in next.config.mjs file and then run it's working fine.
/* @type {import('next').NextConfig} / const nextConfig = { reactStrictMode: true, 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; } };
export default nextConfig;
Prerequisites
Environment check
msw
versionBrowsers
Chromium (Chrome, Brave, etc.)
Reproduction repository
https://github.com/xereda/nextjs-msw-example
Reproduction steps
Current behavior
Expected behavior
Service mock enabled.