Open redbmk opened 2 months ago
I also tried fixing the webpack config with an overly complex set of rules but it didn't seem to help, even though the end result looked the same. Which makes me think webpack isn't the issue, but some sort of linting rule or swc plugin or something. I saw that both babel and swc have rules to check that you're not using a barrel export in a page file, but that doesn't make sense to me then why adding pages
"fixes" it.
I found a workaround that works! It seems that next-swc-loader
is getting a pagesDir
that matches the rootDir
, so it thinks everything is a page, apparently. But it only seems to get that when running cypress... with next dev
or next build
, it properly gets undefined
for pagesDir
.
Here's a (still complex) working solution, but I'm hoping I can figure out an easier way to tell it that I'm not using pages
:
/** @type {import('next').NextConfig} */
/**
* @param {import('next/dist/server/config').NextConfig} config
* @returns {void}
**/
const fixBuildForCypressComponentTests = (config) => {
for (const rule of config.module.rules) {
for (const use of findUseItems(rule)) {
fixLoaderOptions(use);
}
}
/**
* @param {import('webpack').RuleSetRule} rule
* @returns {import('webpack').RuleSetUseItem[]}
*/
function findUseItems(rule) {
if (Array.isArray(rule.use)) {
return rule.use.flatMap((use) => (use && typeof use === "object" ? use : []));
}
if (rule.use && typeof rule.use === "object") {
return [rule.use];
}
if (Array.isArray(rule.oneOf)) {
return rule.oneOf.flatMap((rule) =>
rule && typeof rule === "object" ? findUseItems(rule) : [],
);
}
return [];
}
/**
* @param {import('webpack').RuleSetUseItem} use
* @returns {void}
*/
function fixLoaderOptions(use) {
if (typeof use === "string") return;
if (typeof use === "function") return;
if (use.loader !== "next-swc-loader") return;
if (typeof use.options !== "object") return;
if (!use.options.nextConfig) return;
use.options.pagesDir = undefined;
}
};
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
webpack(config) {
if (process.env.CYPRESS === "true") {
fixBuildForCypressComponentTests(config);
}
return config;
},
};
export default nextConfig;
A little more digging and it looks like it's not just setting the root as pagesDir
but also not detecting appDir
. I'm guessing if it can't find either then it defaults to the pages
router using the rootDir
. I still haven't tracked down where the change in detection was introduced or where that detection is happening.
regarding the need for pagesDir
, that is a cypress bug https://github.com/cypress-io/cypress/issues/26802 Cypress configures webpack incorrectly
Link to the code that reproduces this issue
https://github.com/redbmk/next-optimize-package-imports-bug
To Reproduce
bun create next-app cypress-mui-bug
bun add -d cypress && bun add @mui/material @emotion/react @emotion/styled
bunx cypress open
Close cypress and create component with a test
Current vs. Expected behavior
Current behavior is you will see an error and the test will fail:
Expected behavior is that the test will pass. Ideally with no extra configuration, but perhaps with something like:
Provide environment information
Which area(s) are affected? (Select all that apply)
Developer Experience, Documentation, Module Resolution, Pages Router, SWC, Webpack
Which stage(s) are affected? (Select all that apply)
Other (Deployed)
Additional context
I've tried a few different things to fix or workaround this.
One thing I found was that if I create a
pages
folder, the errors go away. However, since I'm using the app router, that also introduces all kinds of other side effects, such asuseSearchParams
now showing up as possibly beingnull
in typescript. It's also unclear to me if that means thatoptimizePackageImports
is essentially disabled even when usingnext build
.I tried commenting out this line and that works, but ideally I'm not having to patch anything in
node_modules
. As far as I can tell, there's no way to remove anything fromoptimizePackageImports
- there's a default set and you can only add to it, but can't remove anything.Considering this is marked as "experimental" I would expect to have to opt in to it, but at the very least there should be a way to opt out.
I also found that this same setup works fine in NextJS
14.1.4
, but breaks in14.2.0
(the next stable release). That's a pretty large release so might take a bit to track down exactly when it broke. The diff betweenv14.1.4..v14.2.0
onconfig.ts
is pretty minimal and seems unrelated to this. It could be a change inswc
or some changes related to the Pages router (It seems like Cypress probably creates a "page" when running component tests)