Code Monkey home page Code Monkey logo

module-federation is working using npm run dev but not with npm run build npm run start , the application is excuted but with a bad remoteEntry.js (Container initialization failed as it has already been initialized with a different share scope) about universe HOT 11 CLOSED

AmineAyachi avatar AmineAyachi commented on July 17, 2024
module-federation is working using npm run dev but not with npm run build npm run start , the application is excuted but with a bad remoteEntry.js (Container initialization failed as it has already been initialized with a different share scope)

from universe.

Comments (11)

ScriptedAlchemy avatar ScriptedAlchemy commented on July 17, 2024 2

We are overhauling nextjs-mf in v8 you will have a real lifecycle hook set to use, we will also look at improved HMR (real hmr) capabilities in production as ByteDance has figured it out already

from universe.

ScriptedAlchemy avatar ScriptedAlchemy commented on July 17, 2024 1

You need to have something like App.getInitialProps or getServerProps, otherwise it static generation and theres no server, so threads restart the application to static generate it

from universe.

benmarch avatar benmarch commented on July 17, 2024 1

@AmineAyachi I had a similar issue with my setup as well. I have 5 NextJS apps, 1 is the host, the others are all remotes. They are all built as standalone NextJS apps in Docker images and deployed through Fargate (ECS). Whenever I updated a remote, the host would error out on the server until I redeployed the host.

The only way I could reproduce the error locally was to do this:

  1. Build the host and a remote (npm run build)
  2. Boot up the remote, then the host
  3. Navigate to the host in a browser (at this point, the host works as expected)
  4. Change the filename of one of the remote JS chunks under .next/static/ssr/ and reboot the remote
  5. Refresh the page ---> Error

What happens locally is that revalidate() sees the same remote remoteEntry.js and doesn't invalidate it (this is the correct behavior). However, because the chunk is different, when Webpack tries to load the chunk that is referenced in remoteEntry.js, the fetch returns 404 and throws the fetch error (which is what I see in the logs as Error [Object object] -- yeah, super helpful error message).

Obviously this doesn't happen exactly the same way in Production, but i've tried so many different ways to reproduce this locally, and this was the only way it worked. I think what's going on in Production for me is that we boot up 2 legs of each app, and one of them updates before the other. So one version of remoteEntry.js is loaded and cached, and then it attempts to fetch a chunk but gets redirected to the other leg where the chunk doesn't exist. This is just a hypothesis, though.

In any case, i was able to mostly fix this issue by modifying my delegate module a bit:

Updated Delegate Module (lots of code, expand to view)
const { importDelegatedModule } = require('@module-federation/utilities')

// eslint-disable-next-line no-async-promise-executor
module.exports = new Promise(async (resolve, reject) => {

  // <some custom remote string parsing logic here>

  importDelegatedModule({
    global: remoteID,
    url: `${url}?${Date.now()}`,
    globalThis: true, // this is because of a quirk here: https://github.com/module-federation/universe/blob/main/packages/utilities/src/utils/importDelegatedModule.ts#L16
  }).then((remote) => {
    // shim the async container to catch remote module loading errors
    const container = {
      get: async (arg) => {
        try {
          // call the async container's `get` function and catch any errors it might throw
          return await remote?.get?.(arg)
        } catch (err) {
          if (typeof window === 'undefined' && globalThis.__remote_scope__[remoteID]) {
            // if there is an error, mark the container as fake to invalidate the cache
            globalThis.__remote_scope__[remoteID].fake = true

            // ensure this chunk is flushed to prevent caching
            if (globalThis.usedChunks) {
              globalThis.usedChunks.add(`${remoteID}->${arg}`)
            }
          }
        }

        // if it's broken, return an empty module
        return {
          __esModule: true,
          default: () => {
            return null
          },
        }
      },
      init: remote?.init,
    }

    // store the shimmed container in the remote scope
    if (typeof window === 'undefined') {
      globalThis.__remote_scope__[remoteID] = container
    }

    resolve(container)
  }, reject)
})

Basically, this wraps the remote container with some error handling that forcibly invalidates itself from the cache if there is a chunk-load error. I hijacked the "fake" container property so that the built-in revalidate() function always invalidates it right away. Additionally, I wrapped the revalidate() function when i want to forcibly invalidate the cache elsewhere in the host like this:

const invalidate = () => {
  const globalScope = globalThis.__remote_scope__

  if (globalScope) {
    Object.keys(globalScope).forEach((remoteID) => {
      if (!remoteID.startsWith('_') && Object.prototype.hasOwnProperty.call(globalScope, remoteID)) {
        globalScope[remoteID].fake = true
      }
    })
  }

  return revalidate()
}

It's not perfect, it recently occurred randomly a couple weeks ago. But instead of it happening most of the time, its down to probably about 2% of the time. Let me know if you try it and if it helps or not.

from universe.

ScriptedAlchemy avatar ScriptedAlchemy commented on July 17, 2024 1

Try with latest, ive rewritten most of this now.

from universe.

AmineAyachi avatar AmineAyachi commented on July 17, 2024

Thanks , i did and its working in production here is my setup :

pages/_app.js:

import App from 'next/app';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <style jsx>{`
        .hero {
          width: 100%;
          color: #333;
        }

        .title {
          margin: 0;
          width: 100%;
          padding-top: 80px;
          line-height: 1.15;
          font-size: 48px;
        }

        .title,
        .description {
          text-align: center;
        }

        .row {
          max-width: 880px;
          margin: 80px auto 40px;
          display: flex;
          flex-direction: row;
          justify-content: space-around;
        }

        .card {
          padding: 18px 18px 24px;
          width: 220px;
          text-align: left;
          text-decoration: none;
          color: #434343;
          border: 1px solid #9b9b9b;
        }

        .card:hover {
          border-color: #067df7;
        }

        .card h3 {
          margin: 0;
          color: #067df7;
          font-size: 18px;
        }

        .card p {
          margin: 0;
          padding: 12px 0 0;
          font-size: 13px;
          color: #333;
        }
      `}</style>
    </>
  );
}

MyApp.getInitialProps = async ctx => {
  const appProps = await App.getInitialProps(ctx);
  return appProps;
};

export default MyApp;

pages/_document.js:
import Document, { Html, Head, Main, NextScript } from "next/document";
import React from "react";
import { revalidate, FlushedChunks, flushChunks } from "@module-federation/nextjs-mf/utils";

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx);

    // can be any lifecycle or implementation you want
    ctx?.res?.on('finish', () => {
      revalidate().then((shouldUpdate) => {
        console.log('finished sending response', shouldUpdate);
      });
    });

    return initialProps;
  }
  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

index.js:

import Head from 'next/head';
import styles from '../styles/Home.module.css';


export default function Home() {


const handleclick = () => {

}

  return (
    <div className={styles.container}>
      Auth App 6
      
    
    </div>
  );
}

Home.getServerProps = async ctx => {
  return {};
};

But i need to restart the host when i make changes and redeploy the remote :/ can you help me on that please

from universe.

ScriptedAlchemy avatar ScriptedAlchemy commented on July 17, 2024

try debugging revalidate, check if it returns a boolean "true" revalidate in your case is lazy, so it will send stale replay for first new render after update, you can make that await instead of res.on.send so it clears require cache before rendering again after update, otherwise its stale while revalidate.

Do you use express or docker?

from universe.

AmineAyachi avatar AmineAyachi commented on July 17, 2024

Yes, I am using Docker and I am experiencing an issue with the remote which requires me to clear the cache using Ctrl + F5 in order to refresh the remote
note that i have the same issue with npm run build npm start without docker

from universe.

AmineAyachi avatar AmineAyachi commented on July 17, 2024

In my Next.js application, whenever I make changes to the remote and redeploy, it's necessary to refresh the page using CTRL+F5. For replication purposes, I've created repositories and would greatly appreciate if you could analyze the code. Any feedback would be extremely helpful as I'm currently facing some issues.

My solution consists of 3 projects:

Shared Package: https://github.com/AmineAyachi/template-auth-core-package
Host: https://github.com/AmineAyachi/template-host-app
Remote: https://github.com/AmineAyachi/template-auth-app
The core package is deployed on GitHub. The host application includes a simple button to switch languages. However, if you make changes in the remote and then refresh the page, the latest updates from the remote are not reflected in the host

from universe.

AmineAyachi avatar AmineAyachi commented on July 17, 2024

Okay, thank you sir. I need to get this work done as soon as possible 👍

from universe.

AmineAyachi avatar AmineAyachi commented on July 17, 2024

@AmineAyachi I had a similar issue with my setup as well. I have 5 NextJS apps, 1 is the host, the others are all remotes. They are all built as standalone NextJS apps in Docker images and deployed through Fargate (ECS). Whenever I updated a remote, the host would error out on the server until I redeployed the host.

The only way I could reproduce the error locally was to do this:

  1. Build the host and a remote (npm run build)
  2. Boot up the remote, then the host
  3. Navigate to the host in a browser (at this point, the host works as expected)
  4. Change the filename of one of the remote JS chunks under .next/static/ssr/ and reboot the remote
  5. Refresh the page ---> Error

What happens locally is that revalidate() sees the same remote remoteEntry.js and doesn't invalidate it (this is the correct behavior). However, because the chunk is different, when Webpack tries to load the chunk that is referenced in remoteEntry.js, the fetch returns 404 and throws the fetch error (which is what I see in the logs as Error [Object object] -- yeah, super helpful error message).

Obviously this doesn't happen exactly the same way in Production, but i've tried so many different ways to reproduce this locally, and this was the only way it worked. I think what's going on in Production for me is that we boot up 2 legs of each app, and one of them updates before the other. So one version of remoteEntry.js is loaded and cached, and then it attempts to fetch a chunk but gets redirected to the other leg where the chunk doesn't exist. This is just a hypothesis, though.

In any case, i was able to mostly fix this issue by modifying my delegate module a bit:

Updated Delegate Module (lots of code, expand to view)
Basically, this wraps the remote container with some error handling that forcibly invalidates itself from the cache if there is a chunk-load error. I hijacked the "fake" container property so that the built-in revalidate() function always invalidates it right away. Additionally, I wrapped the revalidate() function when i want to forcibly invalidate the cache elsewhere in the host like this:

const invalidate = () => {
  const globalScope = globalThis.__remote_scope__

  if (globalScope) {
    Object.keys(globalScope).forEach((remoteID) => {
      if (!remoteID.startsWith('_') && Object.prototype.hasOwnProperty.call(globalScope, remoteID)) {
        globalScope[remoteID].fake = true
      }
    })
  }

  return revalidate()
}

It's not perfect, it recently occurred randomly a couple weeks ago. But instead of it happening most of the time, its down to probably about 2% of the time. Let me know if you try it and if it helps or not.

Thanks , I will try it

from universe.

github-actions avatar github-actions commented on July 17, 2024

Stale issue message

from universe.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.