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

9.5.0 introduced compile error / breaks when using the pnpm package manager #15616

Closed
bitfrost opened this issue Jul 29, 2020 · 19 comments
Closed
Assignees
Milestone

Comments

@bitfrost
Copy link

bitfrost commented Jul 29, 2020

Bug report

Upgrading a hello world project from 9.4.4 to 9.5.0 when using pnpm introduces a compile error for next build (see below)

To Reproduce

install pnpm on your system.

clone example repo https://github.com/bitfrost/example_pnpm_next_issue
cd into repo

run:

pnpm install
pnpm next build

Get

next "build"

Creating an optimized production build

Compiled successfully.

Automatically optimizing pages ..
Error occurred prerendering page "/404". Read more: https://err.sh/next.js/prerender-error
Error: Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
at Z (/Users/kobrien/dev/nx-5/.next/server/pages/_document.js:1028:404)
at module.exports.mbhs.exports.useContext (/Users/kobrien/dev/nx-5/.next/server/pages/_document.js:1033:261)
at Html (/Users/kobrien/dev/nx-5/.next/server/pages/_document.js:257:29)
at d (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:498)
at $a (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:39:16)
at a.b.render (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:44:476)
at a.b.read (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:44:18)
at renderToStaticMarkup (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:54:462)
at renderDocument (/Users/kobrien/dev/nx-5/node_modules/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/next/dist/next-server/server/render.js:3:594)
at renderToHTML (/Users/kobrien/dev/nx-5/node_modules/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/next/dist/next-server/server/render.js:47:72)

Error occurred prerendering page "/". Read more: https://err.sh/next.js/prerender-error
Error: Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
at Z (/Users/kobrien/dev/nx-5/.next/server/pages/_document.js:1028:404)
at Object.module.exports.mbhs.exports.useState (/Users/kobrien/dev/nx-5/.next/server/pages/_document.js:1034:277)
at Link (/Users/kobrien/dev/nx-5/.next/server/pages/index.js:1975:50)
at d (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:498)
at $a (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:39:16)
at a.b.render (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:44:476)
at a.b.read (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:44:18)
at renderToString (/Users/kobrien/dev/nx-5/node_modules/.pnpm/react-dom@16.13.1_react@16.13.1/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:54:364)
at Object.renderPage (/Users/kobrien/dev/nx-5/node_modules/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/next/dist/next-server/server/render.js:45:686)
at Function.getInitialProps (/Users/kobrien/dev/nx-5/.next/server/pages/_document.js:222:19)

Build error occurred
Error: Export encountered errors on following paths:
/
/404
at exportApp (/Users/kobrien/dev/nx-5/node_modules/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/next/dist/export/index.js:24:1103)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async build (/Users/kobrien/dev/nx-5/node_modules/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/next/dist/build/index.js:37:218)
Automatically optimizing pages . ERROR  Command failed with exit code 1.

Move dependency to "next": "9.4.4" in package.json

repeat:

pnpm install
pnpm next build

project works and builds

Expected behavior

pnpm and be used with a minimal next.js app

System information

  • OS: macos

  • Version of Next.js: 9.5.0

  • Version of Node.js: 12.6.3

@bitfrost bitfrost changed the title 9.5.0 introduce compile error when using the pnpm package manager 9.5.0 introduced compile error / breaks when using the pnpm package manager Jul 29, 2020
@IanMitchell
Copy link
Contributor

IanMitchell commented Jul 29, 2020

Seeing the same thing, but on regular npm - nvm

@bitfrost
Copy link
Author

Interesting @IanMitchell . With the above example repo, I don't see this using npm (6.14.4).

Can you confirm my example repo doesn't work with you for npm (and if so what version of npm / node?) (Look out for stray node_modules/ in parent directories, i have seen those cause similar webpack errors around duplicate 'react/package versions'). Locally I can use the example repo and npm to build and run via next without any issues. Tested on 2 machines.

@IanMitchell
Copy link
Contributor

Whoops, you're right - I wasn't seeing the same thing, I was getting a different bug covered by an existing report / PR. My bad for not reading the stack trace carefully enough, sorry!

@bitfrost
Copy link
Author

I can repo with 9.4.5-canary.8 (9.4.5-canary.7 issue is not present)
I will dig through these v9.4.5-canary.7...v9.4.5-canary.8

@bitfrost
Copy link
Author

Via some bi-secting This appears to be introduced by
e125d90
@timneutkens . I am not grokking why yet, but will dig a bit more tomorrow.

@bitfrost
Copy link
Author

removing the use of this hook gets rid of the 'you may be violating the rules of hooks' error. I tweaked (_document.tsx)
// const { inAmpMode } = useContext(DocumentComponentContext)._documentProps
const inAmpMode = true; <-- not a real fix, e125d90 did introduce using the hook versus a class component

The above hack lead me to this apparently bad import

https://github.com/vercel/next.js/blob/canary/packages/next/next-server/server/lib/path-match.ts#L1
notice the direct import from compiled/dist/

import * as pathToRegexp from 'next/dist/compiled/path-to-regexp'

This package is missing here:

https://github.com/vercel/next.js/tree/canary/packages/next/compiled

changing path-match.ts and removing the use of a hook in _document.tsx
fixes things when using pnpm

Change:
import * as pathToRegexp from 'path-to-regexp' + package.json update adding 'path-to-regexp'

Not sure yet why I am getting the hook error only from pnpm.

I will dig more tomorrow potentially raise a PR

@Timer Timer added this to the iteration 6 milestone Jul 29, 2020
@ijjk ijjk self-assigned this Jul 29, 2020
@Timer
Copy link
Member

Timer commented Jul 29, 2020

I've unmarked this as p0 because this appears to be a bug with pnpm. pnpm is duplicating copies of react and react-dom which causes three instances to be loaded at build-time:

/example_pnpm_next_issue/node_modules/react/index.js
/example_pnpm_next_issue/node_modules/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/react/index.js
/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/react/index.js

@Timer
Copy link
Member

Timer commented Jul 29, 2020

Marking this as a duplicate of #9022

@Timer Timer closed this as completed Jul 29, 2020
@Timer Timer assigned Timer and unassigned ijjk Jul 29, 2020
@bitfrost
Copy link
Author

@Timer I followed the following procedure and found the same instance of react. pnpm points things via symlinks links to single shared versions of these packages. Can you share how you produced the above snippet?
Using the facebook suggested method, I got a 'true' using the below:

https://reactjs.org/warnings/invalid-hook-call-warning.html
// Add this in node_modules/react-dom/index.js
window.React1 = require('react');

// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);

Same version of pnpm, this works fine previous to this change.

@bitfrost
Copy link
Author

This does feel like a pnpm bug , the work around here fixes my example repo
pnpm/pnpm#2695

@zkochan
Copy link
Contributor

zkochan commented Aug 1, 2020

pnpm is duplicating copies of react and react-dom which causes three instances to be loaded at build-time

I don't see duplicated.

This
/.pnpm/next@9.5.0_react-dom@16.13.1+react@16.13.1/node_modules/react/

and this
/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/react/

are just symlinks to the same directory:
/.pnpm/react@16.13.1/node_modules/react

Seems like a bug in React. React should resolve these symlinks to their real locations. That's how node's resolution algorithm works.

@bitfrost
Copy link
Author

bitfrost commented Aug 4, 2020

I agree @zkochan not pnpm bug, all same copies of React. While the workaround 'worked', it should not be needed at all.

@elliottsj
Copy link
Contributor

tl;dr resolveRequest should be updated to follow symlinks, which I believe should fix this issue.


Did some debugging; here's what I found.

The issue is in this handleExternals function where Next.js determines which modules webpack should include in the server bundle (written to .next/server/pages/_document.js) and which should be treated as "external", i.e. require()'d by Node.js at run time. We end up with two instances of the 'react' module because one is included in the bundle and one is require()'d by Node.js.

I added some log statements to this function and ran next dev:

with npm

> my-app@0.1.0 dev /Users/spencerelliott/Dev/elliottsj/my-app
> next dev

ready - started server on http://localhost:3000
context /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/next/dist/pages
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
externalizing module
context /Users/spencerelliott/Dev/elliottsj/my-app/pages
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
externalizing module
context /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/next/dist/pages
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
externalizing module

with pnpm

> my-app@0.1.0 dev /Users/spencerelliott/Dev/elliottsj/my-app
> next dev

ready - started server on http://localhost:3000
context /Users/spencerelliott/Dev/elliottsj/my-app/pages
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
externalizing module
context /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/next/dist/pages
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes !== res. bundling module.
context /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/next/dist/pages
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes !== res. bundling module.
context /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/styled-jsx/dist
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes !== res. bundling module.
context /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/styled-jsx/dist
res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js
baseRes !== res. bundling module.

Using npm, we get our desired result: the 'react' module is always externalized.

Using pnpm, 'react' is only externalized when the importer is in the /Users/spencerelliott/Dev/elliottsj/my-app/pages directory (aka the context). From these other contexts, React is bundled instead:

  • node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/next/dist/pages
  • node_modules/.pnpm/styled-jsx@3.3.0_react@16.13.1/node_modules/styled-jsx/dist

This happens because handleExternals performs a check to make sure a module is resolved to the same location from both the context and the project directory dir before externalizing it:

res = resolveRequest(request, `${context}/`)

baseRes = resolveRequest(request, `${dir}/`)

If baseRes !== res, then the module is forced to be bundled.

If we take this example:

res     /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/react/index.js
baseRes /Users/spencerelliott/Dev/elliottsj/my-app/node_modules/react/index.js

Both of these paths actually resolve to the same location on disk:

$ cd node_modules/.pnpm/next@9.5.2_ac1925d3b18bab26b8f85315f9db8478/node_modules/react
$ pwd -P
/Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/react@16.13.1/node_modules/react
$ cd -
$ cd node_modules/react
$ pwd -P
/Users/spencerelliott/Dev/elliottsj/my-app/node_modules/.pnpm/react@16.13.1/node_modules/react

So, I think the root cause of this issue is that the resolveRequest function does not follow symlinks. If it followed symlinks, then baseRes would always equal res, so React would be externalized as we expect.

@elliottsj
Copy link
Contributor

elliottsj commented Aug 24, 2020

I made a pull request which updates the behaviour of resolveRequest so it follows symlinks: #16369

This fixes the problem described in this issue.

However it does not resolve the issue which this is marked as a duplicate of: #9022. Importing React components from outside of the project folder is a slightly different issue due to the fact that you can have different copies of the React package on disk.

Appreciate if a maintainer can re-open this issue.

@keyworks
Copy link

Would love to see #16369 merged as well. We are currently on a pnpm monorepo and this issue is preventing us from upgrading to 9.5 from 9.4. #9022 seems to be a slew of issues where duplicate react is a symptom and not necessarily related to this specific issue that @elliottsj has root caused so I too would appreciate if the maintainers can perhaps re-open this and consider #16369.

@bitfrost
Copy link
Author

@elliottsj I missed this PR and created a similar one (but made it pnpm specific) and attached it to
#16471 as well. I would be thrilled if one of them is merged. In the mean time I postinstall the patched files into next.js (ick)

@theprobugmaker
Copy link

@bitfrost What you mean with postinstall?

@bitfrost
Copy link
Author

bitfrost commented Aug 30, 2020

In my own projects, I setup something like:
"scripts": {
"postinstall": "a script to copy files into node_modues/next/dist with my patched PR files"
}
so post pnpm install , next.js gets 'fixed' and I can continue to work using pnpm + next.

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants