Skip to content

amaralc/vite-nx-module-federation

Repository files navigation

Vite and Module Federation - Notes

Adding vite plugin federation

  • yarn add @originjs/vite-plugin-federation -D

Usage with Nx

  • While exposing a component using a monorepo structure (with Nx) it is necessary to reference the full path and file extension of the component:
...
federation({
  name: 'service-vite-remote',
  filename: 'remoteEntry.js',
  exposes: {
    './Button': 'apps/service-vite-remote/src/components/button.jsx',  // <--- Here, using "./src/components/button" is not enough
  },
  shared: ['react', 'react-dom'],
}),

Differences from module federation

  • Differently than module federation, with vite-plugin-federation you need to build the remote application in order to generate the manifest file;
  • Also, it is necessary to serve the build using "preview"; In nx case:
yarn nx run service-vite-remote:build && yarn nx run service-vite-remote:preview:production
  • (browser) Visit http://localhost:5001 and verify that you see a manifest file such as:
const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']);
let moduleMap = {
  './Button': () => {
    dynamicLoadingCss(['button-e9e5ede8.css']);
    return __federation_import('./__federation_expose_Button-ad9f608a.js').then((module) =>
      Object.keys(module).every((item) => exportSet.has(item)) ? () => module.default : () => module
    );
  },
};
const seen = {};
const dynamicLoadingCss = (cssFilePaths) => {
  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;
    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) => {
  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 };
  • The manifest tells the consuming application: "Here is what I've got";

  • In our case, it has got the shared libraries (react, react-dom), and our "Button", that has our button implementation in it;

  • If you don't see this file, than nothing is going to work at this point.

  • If you just run the "yarn nx run service-vite-remote:serve", it won't generate the manifest, so that's why it's important to build the application and serve the files generated by the production build.

  • It's also important to emphasize the location of this file. It is in the "assets" directory, and that's because this remoteEntry file and all of the bundles and references are just JavaScript files and should be treated and deployed like assets.

  • One common question about module federation:

    • What happens to the sharing between two applications if the application that is sharing the component that I rely on goes down?

    "Nothing, because what you should do is you should go and deploy your assets (in this case your css, your js, your images, all of it), they should be deployed to a static assets file store. And that includes all of the federate modules. So yes, your 'application' could go down but that is just the server side of your application. The static assets should never go down because they should be deployed into something like S3, which never (joking) is going to go down."

Consuming your remote

  • So far I was not able to consume the remote manifest from a host in development mode; I need to build and "preview" in production to see it.

(...continue) https://youtu.be/t-nchkL9yIg?t=816

Share store code

  • Why to use something like "jotai" instead of "redux"?
  • Jotai lets you deal with your state in a way that reduces coupling, as compared to jotai.

Webpack Compatibility

  • Vite with rollup is compatible with webpack 5;