nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.47k stars 2.34k forks source link

Next.js 12 Output File Tracing Fails to Build #9017

Closed Miikis closed 1 year ago

Miikis commented 2 years ago

Current Behavior

Expected Behavior

The standalone folder should contain a server.js file.

Steps to Reproduce

I've created a repro repo here. Clone & run yarn nx build --prod to see error message.

Failure Logs

ENOENT: no such file or directory, open '/nx_next_standalone_repro/dist/apps/haha/.next/standalone/apps/haha/server.js'

Environment

Output of next info

    Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 21.2.0: Sun Nov 28 20:29:10 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T8101
    Binaries:
      Node: 16.13.1
      npm: 8.1.2
      Yarn: 1.22.17
      pnpm: N/A
    Relevant packages:
      next: 12.1.0
      react: 17.0.2
      react-dom: 17.0.2

Output of nx report

   Node : 16.13.1
   OS   : darwin arm64
   yarn : 1.22.17

   nx : 13.8.2
   @nrwl/angular : undefined
   @nrwl/cli : 13.8.2
   @nrwl/cypress : 13.8.2
   @nrwl/detox : undefined
   @nrwl/devkit : 13.8.2
   @nrwl/eslint-plugin-nx : 13.8.2
   @nrwl/express : undefined
   @nrwl/jest : 13.8.2
   @nrwl/js : 13.8.2
   @nrwl/linter : 13.8.2
   @nrwl/nest : undefined
   @nrwl/next : 13.8.2
   @nrwl/node : undefined
   @nrwl/nx-cloud : undefined
   @nrwl/react : 13.8.2
   @nrwl/react-native : undefined
   @nrwl/schematics : undefined
   @nrwl/storybook : 13.8.2
   @nrwl/tao : 13.8.2
   @nrwl/web : 13.8.2
   @nrwl/workspace : 13.8.2
   typescript : 4.5.5
   rxjs : 6.6.7
   ---------------------------------------
   Community plugins:
traianrusu1 commented 1 year ago

Thanks to the comments above here's what worked for me on "nx": "14.8.3" and "next": "12.3.1".

As Yizhachok said I had to add the following, although I had to add 'output' outside of experimental. Also needed to add const path = require('path'); to the top of the file.

  output: 'standalone',
  experimental: {
    // this includes files from the monorepo base two directories up
    outputFileTracingRoot: path.join(__dirname, '../../'),
  },

then in pages/index I added

import path from 'path';
path.resolve('./next.config.js');

This wasn't working for me initially so the final thing I had to do was delete my dist folder that I had generated previously and then run the build command with the --skip-nx-cache argument so: nx build shop-next --skip-nx-cache. This finally worked and I was able to run the server file in dist/apps/my-app/.next/standalone/apps/my-app/server.js

I think a more proper solution by NX is really necessary as without these workarounds the docker image is 1.5 GB for me.

juliolugo96 commented 1 year ago

Thanks to the comments above here's what worked for me on "nx": "14.8.3" and "next": "12.3.1".

As Yizhachok said I had to add the following, although I had to add 'output' outside of experimental. Also needed to add const path = require('path'); to the top of the file.

  output: 'standalone',
  experimental: {
    // this includes files from the monorepo base two directories up
    outputFileTracingRoot: path.join(__dirname, '../../'),
  },

then in pages/index I added

import path from 'path';
path.resolve('./next.config.js');

This wasn't working for me initially so the final thing I had to do was delete my dist folder that I had generated previously and then run the build command with the --skip-nx-cache argument so: nx build shop-next --skip-nx-cache. This finally worked and I was able to run the server file in dist/apps/my-app/.next/standalone/apps/my-app/server.js

I think a more proper solution by NX is really necessary as without these workarounds the docker image is 1.5 GB for me.

This worked perfectly for me! Thank you very much, man!

virus2016 commented 1 year ago

Hey all,

My standalone build outputs this server.js file

const NextServer = require('next/dist/server/next-server').default
const http = require('http')
const path = require('path')
process.env.NODE_ENV = 'production'
process.chdir(__dirname)
...

However it only outputs this next-server.js file in the node_modules folder


"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.default = void 0;
require("./initialize-require-hook");
require("./node-polyfill-fetch");
require("./node-polyfill-web-streams");
....

image

The server fails to start because required("./initialize-require-hook"); doesn't exist in the node_modules.

Here is my next config file

//@ts-check

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { withNx } = require('@nrwl/next/plugins/with-nx');
const path = require('path');

/**
 * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
 **/
const nextConfig = {

  // outputStandalone: true,
  output: 'standalone',
  experimental: {
    // this includes files from the monorepo base two directories up
    // outputFileTracingRoot: path.join(__dirname, '../../'),
  },

  nx: {

    // Set this to true if you would like to to use SVGR
    // See: https://github.com/gregberge/svgr
    svgr: false,
  },
};

module.exports = withNx(nextConfig);

Am I missing something?

electriccode commented 1 year ago

Looks like the server folder is not copied properly.

After running nx build myapp --skip-nx-cache, the apps/myapp/.next/standalone/node_modules/next/dist/server folder has significantly less number of files as compared to the a non nx nextjs build created via npx create-next-app --example with-docker nextjs-docker.

Here are the contents for apps/myapp/.next/standalone/node_modules/next/dist/server

get-page-files.js next-server.js
htmlescape.js     utils.js

Contents for with-docker for .next/standalone/node_modules/next/dist/server

accept-header.js
api-utils
app-render.js
base-http
base-server.js
body-streams.js
config-shared.js
config-utils.js
config.js
crypto-utils.js
font-utils.js
get-page-files.js
google-font-metrics.json
htmlescape.js
image-optimizer.js
initialize-require-hook.js
internal-utils.js
lib
load-components.js
next-server.js
node-polyfill-fetch.js
node-polyfill-web-streams.js
node-web-streams-helper.js
optimize-amp.js
post-process.js
render-result.js
render.js
request-meta.js
require.js
response-cache
router.js
send-payload
serve-static.js
server-route-utils.js
utils.js
web
electriccode commented 1 year ago

Upgrading to latest nx fixed the issue

nx migrate latest npm install npx nx migrate --run-migrations

bntzio commented 1 year ago

Upgrading to latest nx fixed the issue

nx migrate latest npm install npx nx migrate --run-migrations

Still not working for me with "nx": "15.2.4"

damianesteban commented 1 year ago

Just don't ask how I investigated this and how much time I spend to find out how to fix this. ⚠️ WARNING ⚠️ Next text for people who believe in magic, skeptics, please stop read this text right now. I warned you.

Steps to get working repo for standalone Next.js server with build using NX CLI.

Init example workspace (or use your own)

  1. Run npx create-nx-workspace@latest --preset=next (tested on v14.1.9 workspace)
  2. Answers to questions: ✔ Workspace name (e.g., org name): next-test ✔ Application name: site ✔ Default stylesheet format: scss
  3. Go to next.config.js file and add to config:
experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, "../../")
}

Preparation finished, now let's make real magic

Go to pages/index.tsx (or any page file) and add this lines (don't ask why, this is MAGIC):

import path from 'path';
path.resolve('./next.config.js');

Really can be any js file that can be resolved from current location (even empty). This code enough to insert only once.

Show magic to the world

Run npm run build. That's it! No errors!

Bonus

This is a VERY SIMPLE Docker config that show how to work with this build.

FROM node:16-alpine AS builder
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /build
COPY . .
RUN npm i && npm run build

FROM node:16-alpine AS runner
ARG APP=site
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/static ./dist/apps/${APP}/.next/static
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/public ./apps/${APP}/public

USER nextjs

EXPOSE 3000

ENV NODE_ENV=production
ENV APP_PATH=apps/${APP}/server.js
ENV PORT 3000

CMD node $APP_PATH

Thanks to @bboyz269 for working example, from his repository I found the issue why build didn't work on clean project. His repo works because of next-i18next lib code.

I was unable to get standalone builds working until I followed this. There is no reason why it should be this difficult.

moatorres commented 1 year ago

Just a heads up to let you know that the workaround shared here (and therefore the bug) is still needed in Next 13, except that we should add the below into app/page.tsx (or whichever is the root page.tsx of your project). In my case it sits on app/(landing)/page.tsx.

import path from 'path'
path.resolve('./next.config.js')

Works like a charm.

package.json for reference:

"nx": "15.7.2", 
"next": "13.1.1", 
"react": "18.2.0"
meightythree commented 1 year ago

Just don't ask how I investigated this and how much time I spend to find out how to fix this. ⚠️ WARNING ⚠️ Next text for people who believe in magic, skeptics, please stop read this text right now. I warned you. Steps to get working repo for standalone Next.js server with build using NX CLI.

Init example workspace (or use your own)

  1. Run npx create-nx-workspace@latest --preset=next (tested on v14.1.9 workspace)
  2. Answers to questions: ✔ Workspace name (e.g., org name): next-test ✔ Application name: site ✔ Default stylesheet format: scss
  3. Go to next.config.js file and add to config:
experimental: {
    outputStandalone: true,
    outputFileTracingRoot: path.join(__dirname, "../../")
}

Preparation finished, now let's make real magic

Go to pages/index.tsx (or any page file) and add this lines (don't ask why, this is MAGIC):

import path from 'path';
path.resolve('./next.config.js');

Really can be any js file that can be resolved from current location (even empty). This code enough to insert only once.

Show magic to the world

Run npm run build. That's it! No errors!

Bonus

This is a VERY SIMPLE Docker config that show how to work with this build.

FROM node:16-alpine AS builder
RUN apk add --no-cache libc6-compat python3 make g++
WORKDIR /build
COPY . .
RUN npm i && npm run build

FROM node:16-alpine AS runner
ARG APP=site
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/.next/static ./dist/apps/${APP}/.next/static
COPY --from=builder --chown=nextjs:nodejs /build/dist/apps/${APP}/public ./apps/${APP}/public

USER nextjs

EXPOSE 3000

ENV NODE_ENV=production
ENV APP_PATH=apps/${APP}/server.js
ENV PORT 3000

CMD node $APP_PATH

Thanks to @bboyz269 for working example, from his repository I found the issue why build didn't work on clean project. His repo works because of next-i18next lib code.

I was unable to get standalone builds working until I followed this. There is no reason why it should be this difficult.

Thanks @damianesteban your solution worked for me!

        "next": "13.1.1",
        "nx": "15.8.5",
        "react": "18.2.0",
fatihdogmus commented 1 year ago

This is a huge problem for us. We don't use any deployment platform like vercel or netlify. We deploy our front-end to a kubernetes cluster, so we need to be able to dockerize our next.js applications. Any comment from nx team on the status of the issue? This has been open for more than a year.

ianldgs commented 1 year ago

@fatihdogmus have you tried next@v13.1.2+? It should have the fix. If you're on 13.1.2+, which error do you get exactly?

salihsagdilek commented 1 year ago

same here.

    "next": "13.2.4",
    "@nrwl/next": "15.8.9",
    "nx": "15.8.9",

[ERROR]: !!! CustomerError: The standalone directory /codebuild/output/src945274832/src/XXX/dist/apps/XXX/.next/standalone is missing a server.js file. This file shoud have been created automatically by NextJS. Make sure you enable output standalone on your next.config.js file or set NEXT_PRIVATE_STANDALONE=true. https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files

const nextConfig = {
  output: "standalone",
  typescript: {
    ignoreBuildErrors: true,
  },
  experimental: {
    outputFileTracingRoot: path.join(__dirname, '../../'),
  },
  nx: {
    svgr: true,
  },
};
Screenshot 2023-04-03 at 15 53 38
moatorres commented 1 year ago

@nowlena Have you tried the workarounds suggested on this thread?

moatorres commented 1 year ago

@nowlena Have you tried the workarounds suggested on this thread?

Yes, I'm not being lazy here, I'm unable to find a working solution.

I'm not implying that either, but since I did share workarounds above for both Next 12 and 13, I find it fairly difficult to help you without any inputs other than "I've been trying to resolve this all day without luck. Is there any way to get eyes on this?" 😕

What have you tried? What is the structure of your workspace? Are you using the default 'dist' path? What error messages are you catching? Are you using Next 12 or 13? Have you added the suggested "import path from 'path'"? On which file? I understand your frustration. Hence why I'm here replying to you.

dnyn92 commented 1 year ago

I have the same issue as @salihsagdilek

    "next": "13.3.0",
    "@nrwl/next": "15.9.1",
    "nx": "15.9.1",
breningham commented 1 year ago

Just want to add my penny's-worth, using the above workarounds does create the server.js, but it does not work, manually updating the server.js to replace the NextServer's configuration conf.distDir value from "./../../../dist/apps/[app-path]/.next" just ".next" allows it to work

i'm just wondering if this is possible what's blocking it from working initially without the workarounds

yusijs commented 1 year ago

I'm seeing the same as @breningham, but I need to change conf.distDir from ./../../dist/apps/frontend/.next to "./../../.."

This is kind of making the nx plugin a pain to use, instead of doing what it's meant to do (simplify development for mono-repos). We have multiple applications being built, and previously (using angular) we've been able to create minimal docker-images by building everything outside and only copying what is required. The only feasible "solution" I'm seeing here is doing a full build within the dockerfile for the client, which both slows down the build and bloats the dockerfile. :/

ild0tt0re commented 1 year ago

Probably Nx v16.3.0 will fix this issue thanks to the direct use of nextjs cli and API instead of nx wrappers

ndcunningham commented 1 year ago

This is now fixed with Nx version 16.3.0

github-actions[bot] commented 1 year ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.