Replies: 2 comments
-
Hi @Pink-Wolf Short answerI agree this should not fail silently. A circular import can “work” in dev (because modules are loaded incrementally) but fail during next build/static export where evaluation order matters. At minimum, Next.js could warn in dev and fail in build when a cycle is detected in the app’s module graph (excluding node_modules). That keeps dev UX friendly while preventing broken deploys. Why it happensESM/TS allows cycles, but initialization order is not guaranteed. During SSR/static export a module may read a value from another module before it’s initialized, producing undefined, empty routes, or pages not emitted. Some cycles only appear after re-exports (barrel files), so the error is non-obvious. What would help (proposal)
Workarounds you can use today
.eslintrc {
"plugins": ["import"],
"rules": {
"import/no-cycle": ["error", { "ignoreExternal": true, "maxDepth": 1 }]
}
}
# install
npm i -D madge
# script (exits non-zero on cycles)
madge --extensions ts,tsx --circular src package.json { "scripts": { "check:cycles": "madge --extensions ts,tsx --circular src" } }
// next.config.js (CJS)
const CircularDependencyPlugin = require('circular-dependency-plugin');
module.exports = {
webpack: (config) => {
config.plugins.push(
new CircularDependencyPlugin({
exclude: /node_modules/,
failOnError: true,
allowAsyncCycles: false
})
);
return config;
},
}; Refactor tips to break cycles
Conclusion+1 to making cycles actionable in Next: warn in dev, error in build, with clear paths. Until then, enforcing import/no-cycle and/or madge in CI prevents the “works in dev, breaks in build” trap. |
Beta Was this translation helpful? Give feedback.
-
While it is true that you should avoid circular dependencies, I don't think their presence is the only thing that leads to this issue, but a pattern further down within a cycle. I have used the tools in the comment above, and also dependency-cruiser, and I can tell ya most projects have circular dependencies - anyway long story short - I wonder if this was the issue: export default async function Keyword({ children }: { children: string | string[] }) {
if (typeof (children) !== "string") children = children.join('')
return (await GetKeywordRecord())[children]
} Cuz, And the reason why it surfaces in prod build, could be because of minification, which not only minifies, but mangles, and re-arranges code in safe ways. The latter being my interest here... maybe the minifier did something that was not safe? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Goals
Non-Goals
Background
By "circular includes", I mean that there are 2 files that both includes and uses functions from the other. For example:
I was working on a project automatically build and hosted to Github Pages, and during for example this commit*, I saw no mistake when developing (where I was using "npm run dev") nor saw any when the website was automatically build (with "npm run build"). I therefore assumed the project was working correctly.
I only later (when looking at the build hosted through Github Pages) saw that no actual pages where being build, except 404.
Since there was no errors and it worked with "npm run dev", I had to slowly go through different versions of the website, building each to finally find the mistake: I had two files, both including the other.
Once found, the mistake was easy to fix.
No pages being built is clearly an error. An error only happening when building, and especially an error happening while "npm run build" reports none, is obviously horrible (the chance errors are not caught before they reach production is much higher).
I have not checked if it matters that I for example use static exports.
*The circular include, in this repository, was from:
Proposal
While it would be useful for "npm run build" to handle circular includes (just like "npm run dev" seemingly can) I assume getting this to work reliably is actually much more difficult.
I therefore propose that "npm run build" simply checks for circular includes, and ends with an error if any are found.
This would solve goal 1.
I also propose that "npm run dev" checks for circular includes, and reports an error if any are found as well.
Stopping "npm run dev" from working with circular includes may be a too breaking change, but I believe an error that can be ignored is fine.
This would somewhat solve goal 2.
Beta Was this translation helpful? Give feedback.
All reactions