next.js: Missing dependencies when using standalone output with pnpm 8
Verify canary release
- I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000
Binaries:
Node: 16.19.1
npm: 8.19.3
Yarn: 1.22.19
pnpm: 8.1.0
Relevant packages:
next: 13.2.5-canary.32
eslint-config-next: N/A
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
Standalone mode (output: “standalone”)
Link to the code that reproduces this issue
https://github.com/Bubbleinpit/nextjs13-standalone-with-pnpm8
To Reproduce
You can follow this README of the repo above to reproduce the issue. For convenience, I will also describe it here.
- execute
next build
and copy all necessary files to output dir
pnpm build
[ -d "build" ] && rm -r build
mkdir build
cp -r ./public ./build
cp -r ./.next/standalone/* ./build
cp -r ./.next/standalone/.next ./build
cp -r ./.next/static ./build/.next
- move output dir to another place and execute
node server.js
in it
mv ./build ~/build
cd ~/build
node ./server.js
Then you will see:
node:internal/modules/cjs/loader:1024
throw err;
^
Error: Cannot find module 'styled-jsx'
Require stack:
- /Users/username/build/node_modules/next/dist/build/webpack/require-hook.js
- /Users/username/build/node_modules/next/dist/server/initialize-require-hook.js
- /Users/username/build/node_modules/next/dist/server/next-server.js
- /Users/username/build/server.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1021:15)
at Function.resolve (node:internal/modules/cjs/helpers:114:19)
at Object.loadRequireHook (/Users/username/build/node_modules/next/dist/build/webpack/require-hook.js:38:21)
at Object.<anonymous> (/Users/username/build/node_modules/next/dist/server/initialize-require-hook.js:3:19)
at Module._compile (node:internal/modules/cjs/loader:1191:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1245:10)
at Module.load (node:internal/modules/cjs/loader:1069:32)
at Function.Module._load (node:internal/modules/cjs/loader:904:12)
at Module.require (node:internal/modules/cjs/loader:1093:19)
at require (node:internal/modules/cjs/helpers:108:18) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/Users/username/build/node_modules/next/dist/build/webpack/require-hook.js',
'/Users/username/build/node_modules/next/dist/server/initialize-require-hook.js',
'/Users/username/build/node_modules/next/dist/server/next-server.js',
'/Users/username/build/server.js'
]
}
Describe the Bug
I’m using pnpm 8 and nextjs 13. And I turn on the appDir
. I set the output mode as standalone
to build the code on my dev computer and to deploy it on a prod server.
When I try to execute the server.js
, it always says Error: Cannot find module 'xxx'
.
node:internal/modules/cjs/loader:1024
throw err;
^
Error: Cannot find module 'styled-jsx'
Require stack:
- /Users/username/build/node_modules/next/dist/build/webpack/require-hook.js
- /Users/username/build/node_modules/next/dist/server/initialize-require-hook.js
- /Users/username/build/node_modules/next/dist/server/next-server.js
- /Users/username/build/server.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1021:15)
at Function.resolve (node:internal/modules/cjs/helpers:114:19)
at Object.loadRequireHook (/Users/username/build/node_modules/next/dist/build/webpack/require-hook.js:38:21)
at Object.<anonymous> (/Users/username/build/node_modules/next/dist/server/initialize-require-hook.js:3:19)
at Module._compile (node:internal/modules/cjs/loader:1191:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1245:10)
at Module.load (node:internal/modules/cjs/loader:1069:32)
at Function.Module._load (node:internal/modules/cjs/loader:904:12)
at Module.require (node:internal/modules/cjs/loader:1093:19)
at require (node:internal/modules/cjs/helpers:108:18) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/Users/username/build/node_modules/next/dist/build/webpack/require-hook.js',
'/Users/username/build/node_modules/next/dist/server/initialize-require-hook.js',
'/Users/username/build/node_modules/next/dist/server/next-server.js',
'/Users/username/build/server.js'
]
}
Now I have to reinstall the dependencies in the output dir on my prod server to solve this issue. This is too much of a waste of time.
And I am sure that if I switch to use npm as the package manager, the issue will go away. I verified that.
Expected Behavior
I can start the server successfully with the steps to reproduce.
Which browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
No response
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 1
- Comments: 15 (5 by maintainers)
I think this is related to the architecture design of pnpm, which may use some symbolic links and hard links to reuse npm packages. When I searched for past issues of Next.js, I found that Next.js had also done some handling for this situation, such as copying the original files instead of just copying symbolic links. However, it seems that this handling is not comprehensive enough. I am not sure if this issue exists in Next.js 12, maybe we can try Next.js 12 again to figure out if the problem was introduced in version 13.
It seems that symlink problems can be avoided by installing the package with
node-linker=hoisted
in the pnpm configuration before standalone output.ref. https://pnpm.io/ja/npmrc#node-linker
We hit this as well, when trying to build with
pnpm
but also when trying to build using Bazel andrules_js
(which emulates the sort of directory structurepnpm
creates, with symlinks everywhere, but doesn’t actually directly usepnpm
). The issue is that thenode_modules
dir instandalone/node_modules
just contains symlinks to the actual module files elsewhere on the filesystem, so if you copy just thestandalone
folder (as I would expect to work and does withyarn
) you end up with broken symlinks that don’t go anywhere. If you copy the entire source.pnpm
folder that the symlinks point to then you end up with a load of other dependencies for unrelated things also being worked on on that machine.Ideally IMO all directories and ‘leaf’ files that are being copied into
standalone
by Next.js as part of the build process should be checked, and if they’re symlinks they should be dereferenced (potentially multiple times) and the target ‘real’ file copied.As it is at the moment I’m just bypassing all of our
pnpm
andbazel
code by loading the source tree into a build container and usingyarn
in there, then copying the resultingstandalone
folder into the output container. But that contaminates the rest of our build pipeline. I can’t have anything else use this component except as a black-box container, and I have to manually build within the container several dependencies that have already been built bybazel
, and lose all caching of output artifacts etc. My CTO isn’t very happy about it, but it works…I figured it out. Since the way i was doing Dockerfile was build outside of container, then copy the results from my local machine to container. I was having the impression that the “standalone” build is a bundled version of the node server with all the dependencies like the way i would bundle a express server for instance. And all i have to do is copy over the bundled server files.
But since it does depend on node_modules, this would not work. It seems like the only way to do this is to install dependencies and build all inside containers like the example Dockerfile.
@DuCanhGH Thank you for your help. Perhaps I should deploy using Docker, which may solve the problem. Maybe the behavior of the COPY command in the Dockerfile is different from the cp command in Linux. Thanks again, I will give it a try.
@Bubbleinpit Looking back, there’s this part in my Dockerfile, which originated from Next’s
examples/with-docker
:And I’m using pnpm, which makes this problem look somewhat weird - I think Next is handling this correctly. I will try to look into it further later, but perhaps you will need help from the maintainers…