From ab7bf4173af41b77404cccacf408fbac4b426fe8 Mon Sep 17 00:00:00 2001 From: Alexey Frolovskiy Date: Wed, 4 Dec 2024 14:45:41 +0300 Subject: [PATCH 1/2] docs: add runtime add remotes with virtual:__federation__ --- README.md | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0830b399..e2eb97b8 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,20 @@ English | [简体中文](./README-zh.md) A Vite/Rollup plugin which support Module Federation. Inspired by Webpack and compatible with [Webpack Module Federation](https://webpack.js.org/concepts/module-federation/). +## Navigation + +- [Running results](#running-results) +- [Install](#install) +- [Usage](#usage) +- [Example projects](#example-projects) +- [Features](#features) +- [Configuration](#configuration) +- [Add other example projects?](#add-other-example-projects) +- [Runtime add remotes with `virtual:__federation__`](#runtime-add-remotes-with-virtual__federation__) +- [FAQ](#faq) +- [Star History](#star-history) +- [Wiki](#wiki) + ## Running results ![Preview](./README-Preview.gif) @@ -218,7 +232,7 @@ import myButton from 'remote/myButton' ⚠️ **Note:** * Static imports may rely on the browser `Top-level await` feature, so you will need to set build.target in the configuration file to `next` or use the plugin [`vite-plugin-top-level-await`](https://github.com/Menci/vite-plugin-top-level-await). You can see the [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#browser_) of top-level await here compatibility) -* + ## Configuration ### `name: string` @@ -386,6 +400,189 @@ shared: { } ``` +## Runtime add remotes with `virtual:__federation__` +It is not always possible to define the list of remote applications in advance in `vite.config`. Some applications may load the list of these remotes asynchronously when the user visits the website. In such cases, you can use the `virtual:__federation__` API. + +### API `virtual:__federation__` + +Using methods from the `virtual:__federation__` module, you can implement dynamic loading of a remote application. + +```ts +import { + __federation_method_getRemote as getRemote, + __federation_method_setRemote as setRemote, + __federation_method_unwrapDefault as unwrapModule, + type IRemoteConfig, +} from "virtual:__federation__"; + +const renderComponent = () => { + throw Error("Not implemented"); +} + +const loadCrmPlugins = async () => { + try { + const pluginsResponse = await fetch("some-backed.com/plugins"); + const pluginsJson = await pluginsResponse.json(); + + const unresolvedPlugins = pluginsJson.map(async (plugin) => { + setRemote(plugin.name, { + ...commonRemoteConfig, + url: plugin.entry, + }); + + const remoteModule = await getRemote(plugin.name, plugin.component); + const remoteComponent = await unwrapModule(remoteModule); + renderComponent(plugin.name, remoteComponent); + }); + + await Promise.all(unresolvedPlugins); + } catch (e) { + console.error(e); + } +}; +``` + +Available methods: + +
+__federation_method_setRemote + +**Syntax** + +```ts +/** + * Adds a new remote to the shared map of all remotes on the page. + * @param {string} name - The name of the remote. + * @param {IRemoteConfig} config - The configuration of the remote. + */ +function __federation_method_setRemote(name: string, config: IRemoteConfig): void; +``` + +**Types** + +```ts +interface IRemoteConfig { + url: (() => Promise) | string; + format: "esm" | "systemjs" | "var"; + from: "vite" | "webpack; +} +``` + +
+ +
+__federation_method_getRemote + +**Syntax** + +```ts +/** + * Returns a component from a remote. + * @param {string} remoteName - The name of the remote. + * @param {string} componentName - The name of the component to retrieve. + * @returns {Promise} - The retrieved component. + */ +function __federation_method_getRemote(remoteName: string, componentName: string): Promise; +``` + +
+ +
+__federation_method_unwrapDefault + +**Syntax** + +```ts +/** + * Unwraps a module and returns its default export or the module itself. + * @param {unknown} module - The module to unwrap. + * @returns {unknown} - The default export or the module itself. + */ +function __federation_method_unwrapDefault(module: unknown): unknown; +``` + +
+ +
+__federation_method_wrapDefault + +**Syntax** + +```ts +/** + * Checks for a default export and creates a wrapper if necessary. + * @param {unknown} module - The module to process. + * @param {boolean} need - A flag indicating whether to create a wrapper. + * @returns {Promise} - The wrapped module or the original. + */ +function __federation_method_wrapDefault(module: unknown, need: boolean): Promise; +``` + +
+ +
+__federation_method_ensure + +**Syntax** + +```ts +/** + * Checks if a module is initialized and initializes it if necessary. + * @param {string} remoteName - The name of the remote. + * @returns {Promise} - The initialized remote. + */ +async function __federation_method_ensure(remoteName: string): Promise; +``` +
+ +--- + +### Using `virtual:__federation__` with TypeScript + +If you are using TypeScript, define the module types using `declare module`. + +
+declare module example + +To ensure correct functionality in the TypeScript environment, describe the module in a `*.d.ts` file: + +```ts +declare module "virtual:__federation__" { + interface IRemoteConfig { + url: (() => Promise) | string; + format: "esm" | "systemjs" | "var"; + from: "vite" | "webpack"; + } + + export function __federation_method_setRemote( + name: string, + config: IRemoteConfig, + ): void; + + export function __federation_method_getRemote( + name: string, + exposedPath: string, + ): Promise; + + export function __federation_method_unwrapDefault( + unwrappedModule: unknown, + ): Promise; + + export function __federation_method_ensure( + remoteName: string, + ): Promise; + + export function __federation_method_wrapDefault( + module: unknown, + need: boolean, + ): Promise; +} +``` +
+ +Now you can load remote applications without predefining them in `vite.config`. +- [Example vue3-demo-esm](https://github.com/originjs/vite-plugin-federation/blob/main/packages/examples/vue3-demo-esm/layout/src/Layout.vue) + ## Add other example projects? @@ -459,8 +656,6 @@ This also means that there are at least three instructions in the `package.json` ``` - - ## FAQ ### ERROR: `Top-level` await is not available in the configured target environment From 20f5a8ac480eddde7994cba0432a7d9deb196b41 Mon Sep 17 00:00:00 2001 From: Alexey Frolovsky Date: Fri, 3 Jan 2025 14:58:41 +0300 Subject: [PATCH 2/2] docs: add note for virtual modules in Vite Co-authored-by: Toby Scott --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e2eb97b8..54a535be 100644 --- a/README.md +++ b/README.md @@ -403,6 +403,8 @@ shared: { ## Runtime add remotes with `virtual:__federation__` It is not always possible to define the list of remote applications in advance in `vite.config`. Some applications may load the list of these remotes asynchronously when the user visits the website. In such cases, you can use the `virtual:__federation__` API. +> Note: This is a virtual module, for a deeper understanding of virtual modules in Vite, see: https://vite.dev/guide/api-plugin#virtual-modules-convention + ### API `virtual:__federation__` Using methods from the `virtual:__federation__` module, you can implement dynamic loading of a remote application.