vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.21k stars 26.76k forks source link

Multiple assets emit different content to the same filename ../main.js.nft.json #31692

Open adamrneary opened 2 years ago

adamrneary commented 2 years ago

We are trying to migrate from a CRA-based React app to NextJS by importing the root component directly in the Next build. Our plan is to get Next building our bundles, and then we can migrate the pages out one by one without having to do everything at once.

The project uses npm workspaces.

// new-app/pages/index.tsx
import RootComponent from '@watershed/app/src/admin/AdminRoot';

function HomePage() {
  return <RootComponent />;
}

export default HomePage;

When we run next build we get an unexpected error without enough context to resolve:


> @watershed/app-admin@0.1.0 build
> next build

info  - Checking validity of types
info  - Creating an optimized production build
Failed to compile.

Conflict: Multiple assets emit different content to the same filename ../main.js.nft.json

> Build failed because of webpack errors
npm ERR! Lifecycle script `build` failed with error:
npm ERR! Error: command failed
npm ERR!   in workspace: @watershed/app-admin@0.1.0
npm ERR!   at location: /Users/adamrneary/github/ghg/client/workspaces/app-admin

What would be the best way to diagnose this? Is this something folks have seen before? Happy to collaborate on a fix, but I am not sure where to start. Thanks in advance for your time!

ijjk commented 2 years ago

Hi, you should be able to add outputFileTracing: false to your next.config.js to get around this for now. Could you add a repo with a minimal reproduction so that we can ensure this is fixed?

adamrneary commented 2 years ago

@ijjk thanks so much! that's what we are doing for now. still trying to get to a minimal repro repo. will follow up as soon as we can narrow down the complexity enough to isolate the problem.

adamrneary commented 2 years ago

@ijjk I found it. I believe it relates to

   "monaco-editor": "^0.25.2",
    "monaco-editor-webpack-plugin": "^4.0.0",

https://github.com/adamrneary/nextjs-repro-repo

If I leave out the webpack plugin that compiles monaco, it's fine, but leave the code in, and it fails. Does this help? Is this a Monaco problem more than a Next.js problem?

cc @spikebrehm @aitskovi

ivantm commented 2 years ago

I've come across the same error. Some quick notes:

With the repro repo above, const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); is missing from the top of next.config.js

Downgrading the stack to next.js 11 results in npm run build succeeding. Make the following changes in package.json:

    "next": "^11.0.0",
    "next-transpile-modules": "^8.0.0",
adamrneary commented 2 years ago

@ivantm I appreciate that (in earnest), but we're trying to get this working with Next 12.

LukasBombach commented 2 years ago

It might have something to do with the monaco-editor-webpack-plugin using a Webpack ChildCompiler. We are using one as well and run into the same issue

markgomez commented 2 years ago

The webpack function in next.config.js executes twice (for server and client) and the monaco-editor-webpack-plugin doesn't seem to get along with the next build command when it's the server's turn (next dev is unaffected). Limiting the MonacoWebpackPlugin instantiation to the client by using the isServer option fixed the problem for me.

// next.config.js
const { patchWebpackConfig } = require("next-global-css");
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  trailingSlash: true,
  // options: { buildId, dev, isServer, defaultLoaders, webpack }
  webpack: (config, options) => {
    // allow global CSS to be imported from within node_modules
    // see also:
    //   - https://nextjs.org/docs/messages/css-npm
    //   - https://github.com/vercel/next.js/discussions/27953
    patchWebpackConfig(config, options);

    config.module.rules.push({ test: /\.ttf$/, type: "asset/resource" });

    // when `isServer` is `true`, building (`next build`) fails with the following error:
    // "Conflict: Multiple assets emit different content to the same filename ../main.js.nft.json"
    if (!options.isServer) {
      config.plugins.push(
        new MonacoWebpackPlugin({
          languages: ["javascript", "typescript"],
          filename: "static/[name].worker.js",
        })
      );
    }

    return config;
  },
};

module.exports = nextConfig;

My setup:

"next": "12.1.0",
"next-global-css": "1.3.0",
"monaco-editor": "0.32.1",
"monaco-editor-webpack-plugin": "7.0.1",
balazsorban44 commented 2 years ago

First, monaco-editor-webpack-plugin is customizing the default Next.js Webpack config, which in itself warrants a warning. The plugin should take into consideration the existing config, making sure it does not override defaults that could cause a break.

You should reach out to the plugin author to verify/test their plugin against Next.js.

Second, monaco-editor is likely not meant for SSR as pointed out in https://github.com/vercel/next.js/issues/31692#issuecomment-1061260424 so you should make sure it's not executed in Next.js's SSR pass either.

Create a component file for your editor, and use dynamic import with {ssr: false} as an option. Please refer to: https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr

shifengdiy commented 2 years ago

hi, i have a same error in next12, do you find solution in this question

medihack commented 2 years ago

@shifengdiy I have it working with an up-to-date NextJS 12. Below is the relevant code. It uses some custom language service, but it'll hopefully give you the idea...

// next.config.js
/** @type {import('next').NextConfig} */
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
const path = require("path");

const nextConfig = {
  reactStrictMode: true,
  webpack: (config, options) => {
    // Allows to load CSS in monaco-editor as Next.js does not handle CSS in
    // node modules folder.
    // Workaround until RFC is through: https://github.com/vercel/next.js/discussions/27953
    config.module.rules
      .find((rule) => rule.oneOf)
      .oneOf.forEach((r) => {
        if (r.issuer?.and?.[0]?.toString().includes("_app")) {
          r.issuer = [
            { and: r.issuer.and },
            /[\\/]node_modules[\\/]monaco-editor[\\/]/,
          ];
        }
      });

    // See https://github.com/vercel/next.js/issues/31692#issuecomment-1061260424
    if (!options.isServer) {
      config.plugins.push(
        new MonacoWebpackPlugin({
          languages: [],
          filename: "static/[name].js",
          customLanguages: [
            {
              label: "medtl",
              worker: {
                id: "vs/language/medtl/medtlWorker",
                entry: path.resolve(
                  "./node_modules/@medtl/monaco-plugin/dist/medtl.worker.js"
                ),
              },
            },
          ],
        })
      );
    }
    return config;
  },
};

module.exports = nextConfig;
// pages/editor.tsx
import { NextPage } from "next";
import dynamic from "next/dynamic";

const MedtlEditor = dynamic(() => import("../src/components/MedtlEditor"), {
  ssr: false,
});

const Editor: NextPage = () => {
  return (
    <div>
      <MedtlEditor />
    </div>
  );
};

export default Editor;
// src/components/MedtlEditor.tsx
import MonacoEditor from "react-monaco-editor";
import { setDiagnosticsOptions } from "@medtl/monaco-plugin";

const MedtlEditor = () => {
  return (
    <div>
      <MonacoEditor
        width={800}
        height={600}
        language="medtl"
        editorDidMount={() => {
          setDiagnosticsOptions();
          console.log("Editor ready");
        }}
      />
    </div>
  );
};

export default MedtlEditor;

Hope this helps.

LukasBombach commented 2 years ago

My suspicion is that this comes from webpack child compilers. I have one in my code base and i know that Monaco editor uses one as well. I think that because it inherits its parent compilers settings it might write to the same file name.

shifengdiy commented 2 years ago

i add outputFileTracing: false to the next.config.js,it's worked,this error not occored

LukasBombach commented 2 years ago

I can confirm that this works as a workaround, but this does not solve the problem

harrynikolov commented 1 year ago

Quoting the best explanation of the fix for visibility! https://github.com/vercel/next.js/issues/31692#issuecomment-1061260424

The webpack function in next.config.js executes twice (for server and client) and the monaco-editor-webpack-plugin doesn't seem to get along with the next build command when it's the server's turn (next dev is unaffected). Limiting the MonacoWebpackPlugin instantiation to the client by using the isServer option fixed the problem for me.

// next.config.js
const { patchWebpackConfig } = require("next-global-css");
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  trailingSlash: true,
  // options: { buildId, dev, isServer, defaultLoaders, webpack }
  webpack: (config, options) => {
    // allow global CSS to be imported from within node_modules
    // see also:
    //   - https://nextjs.org/docs/messages/css-npm
    //   - https://github.com/vercel/next.js/discussions/27953
    patchWebpackConfig(config, options);

    config.module.rules.push({ test: /\.ttf$/, type: "asset/resource" });

    // when `isServer` is `true`, building (`next build`) fails with the following error:
    // "Conflict: Multiple assets emit different content to the same filename ../main.js.nft.json"
    if (!options.isServer) {
      config.plugins.push(
        new MonacoWebpackPlugin({
          languages: ["javascript", "typescript"],
          filename: "static/[name].worker.js",
        })
      );
    }

    return config;
  },
};

module.exports = nextConfig;

My setup:

"next": "12.1.0",
"next-global-css": "1.3.0",
"monaco-editor": "0.32.1",
"monaco-editor-webpack-plugin": "7.0.1",

Quoting the best explanation of the fix for visibility! https://github.com/vercel/next.js/issues/31692#issuecomment-1061260424

LukasBombach commented 7 months ago

✅ I found the issue in my case and could solve it:

I wrote a webpack loader that creates a childCompiler with a new entry point.

  const childCompiler = this._compilation.createChildCompiler(COMPILER_NAME, outputOptions, [
    new this._compiler.webpack.EntryPlugin(this._compiler.context, this.resourcePath, { name: "main" }),
  ]);

Next js will create a main.js.nft.json file, based on the entry name option { name: "main" }. Since the childCompiler is used multiple times, next will try and create a file with the same name multiple times. To fix this, i created a dynamic name based on the relative filepath in the project:

function generateUniqeEntryName(rootContext, resourcePath) {
  const relativePath = path.relative(rootContext, resourcePath);
  const relativeName = relativePath.replace(/\//g, '_');
  const withoutExtension = relativeName.replace(/\..+?$/, '');
  return 'inline_js_' + withoutExtension;
}

const childCompiler = this._compilation.createChildCompiler(COMPILER_NAME, outputOptions, [
  new this._compiler.webpack.EntryPlugin(this._compiler.context, this.resourcePath, {
    name: generateUniqeEntryName(this.rootContext, this.resourcePath)
  }),
]);

Surely Monaco has a similar issue

LukasBombach commented 7 months ago

In other words: This needs to be adressed at https://github.com/microsoft/monaco-editor/issues