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

Issue with Remix and Vite apps using Federation #609

Open
anajnas66 opened this issue Jul 4, 2024 · 9 comments
Open

Issue with Remix and Vite apps using Federation #609

anajnas66 opened this issue Jul 4, 2024 · 9 comments

Comments

@anajnas66
Copy link

anajnas66 commented Jul 4, 2024

Versions

  • vite-plugin-federation: v1.3.5
  • vite: v5.1.0

Reproduction

[vite] Error when evaluating SSR module virtual:remix/server-build:
|- TypeError [ERR_INVALID_ARG_VALUE]: The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received '\x00virtual:<path_to_my_app>/__federation__'

Additional Details

Host vite config file -


import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import federation from "@originjs/vite-plugin-federation";

export default defineConfig({
  plugins: [
    remix({
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true,
      },
    }),
    federation({
      name: 'hostApp',
      remotes: {
        remoteApp: "http://localhost:3000/assets/remoteEntry.js",
      }
  }),
    tsconfigPaths(),
  ],
  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false
  }
});

Remote's vite.config file -


import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import federation from '@originjs/vite-plugin-federation'

export default defineConfig({
  plugins: [
    remix({
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true,
      },
    }),
    federation({
      name: 'remoteApp',     
      filename: 'remoteEntry.js',
      // Modules to expose
      exposes: {
          './Button': './app/components/button',
      }
    }),
    tsconfigPaths(),
  ],
  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false
  }
});

NOTE: The bundled file is accessible at http://localhost:3000/assets/remoteEntry.js


const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']);
      let moduleMap = {
"./Button":()=>{
      dynamicLoadingCss(["style-BMp_cBgO.css"], false, './Button');
      return __federation_import('./__federation_expose_Button-BxGtt5ih.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},};
    const seen = {};
    const dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => {
      const metaUrl = import.meta.url;
      if (typeof metaUrl == 'undefined') {
        console.warn('The remote style takes effect only when the build.target option in the vite.config.ts file is higher than that of "es2020".');
        return
      }
      const curUrl = metaUrl.substring(0, metaUrl.lastIndexOf('remoteEntry.js'));

      cssFilePaths.forEach(cssFilePath => {
        const href = curUrl + cssFilePath;
        if (href in seen) return
        seen[href] = true;
        if (dontAppendStylesToHead) {
          const key = 'css__remoteApp__' + exposeItemName;
          if (window[key] == null) window[key] = [];
          window[key].push(href);
        } else {
          const element = document.head.appendChild(document.createElement('link'));
          element.href = href;
          element.rel = 'stylesheet';
        }
      });
    };
    async function __federation_import(name) {
        return import(name);
    }    const get =(module) => {
      if(!moduleMap[module]) throw new Error('Can not find remote module ' + module)
      return moduleMap[module]();
    };
    const init =(shareScope) => {
      globalThis.__federation_shared__= globalThis.__federation_shared__|| {};
      Object.entries(shareScope).forEach(([key, value]) => {
        const versionKey = Object.keys(value)[0];
        const versionValue = Object.values(value)[0];
        const scope = versionValue.scope || 'default';
        globalThis.__federation_shared__[scope] = globalThis.__federation_shared__[scope] || {};
        const shared= globalThis.__federation_shared__[scope];
        (shared[key] = shared[key]||{})[versionKey] = versionValue;
      });
    };

export { dynamicLoadingCss, get, init };

Steps to reproduce

What is Expected?

The host app should be able to access the Button component exposed from the remote app and render properly.

What is actually happening?

The remote app builds successfully while the host app throws the error while running "npm run build"
[vite] Error when evaluating SSR module virtual:remix/server-build:
|- TypeError [ERR_INVALID_ARG_VALUE]: The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received '\x00virtual:<path_to_my_app>/federation'

@anajnas66
Copy link
Author

Turns out I was running my app in dev mode. On running in prod mode using the serve command, I now see a different error while building my host app.

Error: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. Received protocol 'http:'

@infantito
Copy link

@anajnas66, Sorry. Have you already resolved this? I'm thinking of setting up a project with remix-spa and module federation.

@ajmeraaxesh
Copy link

@anajnas66 @infantito did this got resolved for you?

@infantito
Copy link

@anajnas66 @infantito did this got resolved for you?

I'm using the React Router v6 API because remix-run was ruled out by my team. React Router V6 works well with module federation; it probably would work with Remix SPA.

@marono
Copy link

marono commented Aug 15, 2024

Hello, good I came across this thread, I'm in the same boat - started building a fleet of frontends (not going to call them micro as they're really only sections) thinking module federation can't be that hard with Remix (given it's one of the recommendations in the React docs and the newer shinier option). Unfortunately it seems to me it's an unresolved problem so far. Any pointers are welcome. Thanks!

@veeramarni
Copy link

@marono/all anyone tried on remix with SSR successfully?

@lucax88x
Copy link

Same.

https://github.com/lucax88x/microfrontend-poc/tree/main/shells/shell-remix

but I have a different error

Error: require is not defined

@MagedEWilliam
Copy link

@HarishkumarSP
Copy link

HarishkumarSP commented Nov 12, 2024

Hey!

I also had same issue saying Error: Only URLs with a scheme in: file and data are supported by the default ESM loader. Received protocol 'http:' while doing a build and serve as remix-serve ./build/server/index.js

But this only happened on host app and in remote it works well but unfortunately css styling didn't applied while build and serve

Any comments are appreciated.

Update: I found that the above error occurs because of the federation plugin we use and for the remote and it points out that http which we used on below

remotes: {
 remoteApp: "http://localhost:3000/assets/remoteEntry.js",
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants