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

feat(module-federation/bridge): Enhance Bridge capabilities and fix some issues #2792

Merged
merged 52 commits into from
Aug 16, 2024

Conversation

danpeen
Copy link
Contributor

@danpeen danpeen commented Jul 24, 2024

Description

In this pr i have supported:

  • support provider.render can be provided by user, so that user can customize their render login. the usage is like this:
import { createBridgeComponent } from '@module-federation/bridge-react';

export const provider = createBridgeComponent({
  rootComponent: App,
  render: () => { user render logic}
});

  • support pass ref and other props, thus user can get the render root dom . The usage is like this:
const Remote1App = createRemoteComponent({
  loader: () => loadRemote('remote1/export-app'),
  fallback: FallbackErrorComp,
  loading: FallbackComp,
});

const App = () => { 
    const ref = useRef<HTMLElement>(null);
    useEffect(() => {
      const refTimeout = setTimeout(() => {
        if (ref && ref.current) {
          ref.current.style.backgroundColor = 'salmon';
        }
      }, 2000)
      return () => {
          clearTimeout(refTimeout);
      }
    }, [])
    return (<Remote1App msg={'hello remote1'} ref={ref} />)
}
  • some fixing about types hit and react-router-dom proxy

Related Issue

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

Copy link

changeset-bot bot commented Jul 24, 2024

🦋 Changeset detected

Latest commit: 87b0110

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 36 packages
Name Type
@module-federation/bridge-react-webpack-plugin Patch
@module-federation/bridge-shared Patch
@module-federation/bridge-react Patch
@module-federation/bridge-vue3 Patch
@module-federation/enhanced Patch
@module-federation/rspack Patch
host Patch
host-v5 Patch
remote1 Patch
remote2 Patch
remote4 Patch
host-vue3 Patch
remote3 Patch
@module-federation/modern-js Patch
@module-federation/nextjs-mf Patch
@module-federation/node Patch
3008-runtime-remote Patch
@module-federation/modernjs Patch
modernjs-ssr-dynamic-nested-remote Patch
modernjs-ssr-dynamic-remote-new-version Patch
modernjs-ssr-dynamic-remote Patch
modernjs-ssr-host Patch
modernjs-ssr-nested-remote Patch
modernjs-ssr-remote-new-version Patch
modernjs-ssr-remote Patch
@module-federation/runtime Patch
@module-federation/webpack-bundler-runtime Patch
@module-federation/sdk Patch
@module-federation/runtime-tools Patch
@module-federation/managers Patch
@module-federation/manifest Patch
@module-federation/dts-plugin Patch
@module-federation/third-party-dts-extractor Patch
@module-federation/devtools Patch
@module-federation/esbuild Patch
@module-federation/utilities Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

netlify bot commented Jul 24, 2024

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit 87b0110
🔍 Latest deploy log https://app.netlify.com/sites/module-federation-docs/deploys/66be168d1476e40008cefa45
😎 Deploy Preview https://deploy-preview-2792--module-federation-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@danpeen danpeen changed the title fix: fix some issue in module-federation/bridge feat(module-federation/bridge): Enhance Bridge capabilities and fix some issues Jul 24, 2024
@zhoushaw zhoushaw self-requested a review July 24, 2024 08:31
Copy link
Contributor

@squadronai squadronai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This pull request enhances the capabilities of the Module Federation Bridge and fixes some issues. The key changes are:

  1. Introduced support for a custom provider.render function, allowing users to customize the rendering logic of the bridge component.
  2. Added support for passing ref and other props to remote components, enabling users to access the rendered DOM elements.
  3. Fixed issues related to types and React Router DOM proxy functionality.

These changes aim to improve the flexibility and usability of the Module Federation Bridge, providing users with more control over the rendering and integration of remote components within their applications.

File Summaries
File Summary
packages/bridge/bridge-react-webpack-plugin/src/index.ts The code updates the webpack configuration by modifying the resolve alias. It adds a new alias entry for the target file path while preserving existing aliases, ensuring that the new alias can be overridden by user-defined aliases.
packages/bridge/bridge-shared/src/type.ts The code defines two interfaces: ProviderParams and RenderFnParams. ProviderParams includes optional properties for module configuration, while RenderFnParams extends ProviderParams and adds a required 'dom' property of type HTMLElement.
packages/bridge/bridge-react/src/create.tsx The code enhances the bridge component's functionality by adding support for custom rendering, ref passing, and prop forwarding. It introduces a new interface for render function parameters and improves type definitions for remote components.
packages/bridge/bridge-react/src/provider.tsx The code enhances the bridge component's functionality by adding support for custom rendering, ref passing, and improved error handling. It introduces a flexible rendering mechanism that adapts to different React versions and allows users to provide their own render logic.
packages/bridge/bridge-react/src/remote/index.tsx The code enhances a React bridge component for module federation. It introduces support for custom render logic, passing refs and props to remote components, and improves type handling and React Router DOM proxy functionality.
packages/bridge/bridge-react/src/router-v5.tsx The code enhances router functionality by introducing WrapperRouter and WrapperRouterProvider components. These components adapt routing behavior based on context, supporting both memory and browser routing scenarios with improved flexibility for module federation.
packages/bridge/bridge-react/src/router-v6.tsx The code enhances router functionality by introducing WrapperRouter and WrapperRouterProvider components. These components dynamically choose between memory and browser routers based on context, allowing for flexible routing configurations in different environments.
packages/dts-plugin/src/core/configurations/remotePlugin.ts The code configures TypeScript compiler options by merging default options with user-defined options from tsconfig.json. It also prepares a list of files to compile, including exposed components, declaration files, and additional specified files.
packages/manifest/src/StatsManager.ts The code filters files from a remote entry chunk, excluding hot update and CSS files. It then asserts that at least one file is found and that there is exactly one file for the remote entry chunk.
packages/node/src/index.ts The code exports several plugins related to module federation and chunk management in a webpack environment. These plugins include ChunkCorrelationPlugin, RemotePublicPathPlugin, EntryChunkTrackerPlugin, and UniverseEntryChunkTrackerPlugin.
packages/node/src/plugins/UniverseEntryChunkTrackerPlugin.ts The UniverseEntryChunkTrackerPlugin is implemented to track entry chunks in a webpack compilation. It injects a script that adds module filenames to a global cache set and uses webpack's EntryPlugin to include this script in the build process.
packages/runtime/src/utils/preload.ts The code enhances remote module loading functionality by adding hooks for script creation and handling different scenarios for loading remote entries. It also improves error handling and type checking for script element creation.
packages/enhanced/src/wrapper/ModuleFederationPlugin.ts The code defines a ModuleFederationPlugin class with methods for applying the plugin to a webpack compiler and retrieving stats resource information. It imports necessary types and utilities for module federation and webpack integration.
packages/runtime/src/type/config.ts The code defines types for configuration options in a module federation system. It includes properties for scope, dependencies, loading strategy, and a loaded flag. Additionally, it introduces a SharedGetter type for module loading functions.
packages/runtime/src/utils/share.ts The code enhances the configuration of shared modules by merging user-provided settings with default values. It handles various properties including 'loaded', 'version', and 'scope', providing flexibility in how these are defined and processed.
packages/bridge/bridge-react/src/router.tsx The code enhances router functionality by introducing wrapper components for BrowserRouter and RouterProvider. These wrappers add support for memory routing and custom basenames, improving flexibility in routing configurations.

Commits reviewed:

67fa05bad65536ba0315051b0c868901a97187f1...5b1bbc47a7d016fb7f1da3b153474214a56944ba

0de1c8365c73245995c5d5b5e68cbda00046b6de...042da07a33bab70d0a4db7688664d6ef68b8bf94
67fa05bad65536ba0315051b0c868901a97187f1...04480a024ceb093025746847a741ac892aeed2f4
67fa05bad65536ba0315051b0c868901a97187f1...04480a024ceb093025746847a741ac892aeed2f4
67fa05bad65536ba0315051b0c868901a97187f1...04480a024ceb093025746847a741ac892aeed2f4
67fa05bad65536ba0315051b0c868901a97187f1...5b1bbc47a7d016fb7f1da3b153474214a56944ba
67fa05bad65536ba0315051b0c868901a97187f1...5b1bbc47a7d016fb7f1da3b153474214a56944ba
67fa05bad65536ba0315051b0c868901a97187f1...7fdae671f2077fa066a27ee1a9e432cf9c9b61fa
67fa05bad65536ba0315051b0c868901a97187f1...7fdae671f2077fa066a27ee1a9e432cf9c9b61fa
67fa05bad65536ba0315051b0c868901a97187f1...7fdae671f2077fa066a27ee1a9e432cf9c9b61fa
67fa05bad65536ba0315051b0c868901a97187f1...ba17cea4ce06456e4387f609f499137e0687845d
67fa05bad65536ba0315051b0c868901a97187f1...ba17cea4ce06456e4387f609f499137e0687845d
67fa05bad65536ba0315051b0c868901a97187f1...ba17cea4ce06456e4387f609f499137e0687845d
67fa05bad65536ba0315051b0c868901a97187f1...dec12d98c53c9a988ff2f900c13907ef8a6073d3
67fa05bad65536ba0315051b0c868901a97187f1...dec12d98c53c9a988ff2f900c13907ef8a6073d3
67fa05bad65536ba0315051b0c868901a97187f1...dec12d98c53c9a988ff2f900c13907ef8a6073d3
67fa05bad65536ba0315051b0c868901a97187f1...950e83e4480220573f5b672eba6539a2c970b892
67fa05bad65536ba0315051b0c868901a97187f1...950e83e4480220573f5b672eba6539a2c970b892
67fa05bad65536ba0315051b0c868901a97187f1...950e83e4480220573f5b672eba6539a2c970b892
67fa05bad65536ba0315051b0c868901a97187f1...fa6aba4b7190960dcafd5b6c120d02db3bb56dab
67fa05bad65536ba0315051b0c868901a97187f1...fa6aba4b7190960dcafd5b6c120d02db3bb56dab
67fa05bad65536ba0315051b0c868901a97187f1...fa6aba4b7190960dcafd5b6c120d02db3bb56dab
67fa05bad65536ba0315051b0c868901a97187f1...38d001cb346c3145e30ea874a3305af37e044e48
67fa05bad65536ba0315051b0c868901a97187f1...38d001cb346c3145e30ea874a3305af37e044e48
67fa05bad65536ba0315051b0c868901a97187f1...38d001cb346c3145e30ea874a3305af37e044e48
67fa05bad65536ba0315051b0c868901a97187f1...7c7ab867cb24dca4ac47524f20296dca67e26e9b
67fa05bad65536ba0315051b0c868901a97187f1...7c7ab867cb24dca4ac47524f20296dca67e26e9b
67fa05bad65536ba0315051b0c868901a97187f1...6bc3a47109aa2b732c99e87a620d544756214db8
67fa05bad65536ba0315051b0c868901a97187f1...7c7ab867cb24dca4ac47524f20296dca67e26e9b
67fa05bad65536ba0315051b0c868901a97187f1...6bc3a47109aa2b732c99e87a620d544756214db8
67fa05bad65536ba0315051b0c868901a97187f1...6bc3a47109aa2b732c99e87a620d544756214db8
67fa05bad65536ba0315051b0c868901a97187f1...d5f3392c5482bd4853f9656ed066623478d9d06c
67fa05bad65536ba0315051b0c868901a97187f1...d5f3392c5482bd4853f9656ed066623478d9d06c
67fa05bad65536ba0315051b0c868901a97187f1...d5f3392c5482bd4853f9656ed066623478d9d06c
67fa05bad65536ba0315051b0c868901a97187f1...955e6b26bb43a87cd8f6547eec9107164bbda0a3
67fa05bad65536ba0315051b0c868901a97187f1...955e6b26bb43a87cd8f6547eec9107164bbda0a3
67fa05bad65536ba0315051b0c868901a97187f1...955e6b26bb43a87cd8f6547eec9107164bbda0a3
67fa05bad65536ba0315051b0c868901a97187f1...0196c57ad9d04cd810cdac67c74bd2ed5760192c
67fa05bad65536ba0315051b0c868901a97187f1...0196c57ad9d04cd810cdac67c74bd2ed5760192c
67fa05bad65536ba0315051b0c868901a97187f1...0196c57ad9d04cd810cdac67c74bd2ed5760192c
67fa05bad65536ba0315051b0c868901a97187f1...eb42c4abd1ddd32421d40b40974e8786083d45ed
67fa05bad65536ba0315051b0c868901a97187f1...a52efbf17f4ee914f870eb5058d2b839f0227c3c
67fa05bad65536ba0315051b0c868901a97187f1...a52efbf17f4ee914f870eb5058d2b839f0227c3c
67fa05bad65536ba0315051b0c868901a97187f1...a52efbf17f4ee914f870eb5058d2b839f0227c3c
67fa05bad65536ba0315051b0c868901a97187f1...35aba94c843b8308f35cae4c91d6df75c74f49da
67fa05bad65536ba0315051b0c868901a97187f1...35aba94c843b8308f35cae4c91d6df75c74f49da
67fa05bad65536ba0315051b0c868901a97187f1...35aba94c843b8308f35cae4c91d6df75c74f49da
67fa05bad65536ba0315051b0c868901a97187f1...dfa66662d9e725875474ca9cf3007521598a0d35
67fa05bad65536ba0315051b0c868901a97187f1...a69c4ec510225b3cbc19a10204ce3cc180d77291
67fa05bad65536ba0315051b0c868901a97187f1...dfa66662d9e725875474ca9cf3007521598a0d35
67fa05bad65536ba0315051b0c868901a97187f1...2b8aab4a80efe08ab78d2f174610e0c659bbb966
67fa05bad65536ba0315051b0c868901a97187f1...a69c4ec510225b3cbc19a10204ce3cc180d77291
67fa05bad65536ba0315051b0c868901a97187f1...a69c4ec510225b3cbc19a10204ce3cc180d77291

Comment on lines 43 to 47
const updatedAlias = {
// allow this config to be override
[this.alias]: targetFilePath,
...originalAlias,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation might unintentionally override user-defined aliases. Reverse the order of the spread operator and the new alias to prioritize user configurations:

Suggested change
const updatedAlias = {
// allow this config to be override
[this.alias]: targetFilePath,
...originalAlias,
};
const updatedAlias = {
...originalAlias,
// allow this config to be override
[this.alias]: targetFilePath,
};

This change ensures that user-defined aliases take precedence over the plugin's default alias.

Comment on lines 11 to 20
export interface RenderFnParams extends ProviderParams {
dom?: any;
}

interface RemoteModule {
provider: () => {
render: (
info: ProviderParams & {
dom: any;
},
info: RenderFnParams,
) => void;
destroy: (info: { dom: any }) => void;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'RemoteModule' interface defines 'provider' as a function returning an object with 'render' and 'destroy' methods. Consider making these methods optional to allow for more flexible implementations:

Suggested change
export interface RenderFnParams extends ProviderParams {
dom?: any;
}
interface RemoteModule {
provider: () => {
render: (
info: ProviderParams & {
dom: any;
},
info: RenderFnParams,
) => void;
destroy: (info: { dom: any }) => void;
};
interface RemoteModule {
provider: () => {
render?: (
info: RenderFnParams,
) => void;
destroy?: (info: { dom: any }) => void;
};
}

packages/bridge/bridge-react/src/create.tsx Outdated Show resolved Hide resolved
Comment on lines 38 to 49
if (bridgeInfo?.render) {
Promise.resolve(bridgeInfo?.render(
<RawComponent
propsInfo={propsInfo}
appInfo={{
name,
basename,
memoryRoute,
}}
/>,
info.dom
)).then((root: RootType) => rootMap.set(info.dom, root));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom render function is called with a Promise.resolve, which may not be necessary. Consider simplifying the code:

Suggested change
if (bridgeInfo?.render) {
Promise.resolve(bridgeInfo?.render(
<RawComponent
propsInfo={propsInfo}
appInfo={{
name,
basename,
memoryRoute,
}}
/>,
info.dom
)).then((root: RootType) => rootMap.set(info.dom, root));
if (bridgeInfo?.render) {
const root = await bridgeInfo.render(
<RawComponent
propsInfo={propsInfo}
appInfo={{
name,
basename,
memoryRoute,
}}
/>,
info.dom
);
rootMap.set(info.dom, root);
}

This change removes the unnecessary Promise.resolve and simplifies the code structure.

Comment on lines 79 to 88
async destroy(info: { dom: HTMLElement }) {
LoggerInstance.log(`createBridgeComponent destroy Info`, {
dom: info.dom,
});
if (atLeastReact18(React)) {
const root = rootMap.get(info.dom);
root?.unmount();
(root as ReactDOMClient.Root)?.unmount();
} else {
ReactDOM.unmountComponentAtNode(info.dom);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destroy method doesn't remove the root from the rootMap after unmounting. Add cleanup to prevent memory leaks:

Suggested change
async destroy(info: { dom: HTMLElement }) {
LoggerInstance.log(`createBridgeComponent destroy Info`, {
dom: info.dom,
});
if (atLeastReact18(React)) {
const root = rootMap.get(info.dom);
root?.unmount();
(root as ReactDOMClient.Root)?.unmount();
} else {
ReactDOM.unmountComponentAtNode(info.dom);
}
async destroy(info: { dom: HTMLElement }) {
LoggerInstance.log(`createBridgeComponent destroy Info`, {
dom: info.dom,
});
if (atLeastReact18(React)) {
const root = rootMap.get(info.dom);
(root as ReactDOMClient.Root)?.unmount();
rootMap.delete(info.dom);
} else {
ReactDOM.unmountComponentAtNode(info.dom);
}
}

This ensures that the rootMap is properly cleaned up when components are destroyed.

Comment on lines 51 to 55
const RouterProvider = (ReactRouterDom as any)['Router' + 'Provider'];
const createMemoryRouter = (ReactRouterDom as any)['create' + 'MemoryRouter'];
const createBrowserRouter = (ReactRouterDom as any)[
'create' + 'BrowserRouter'
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace dynamic property access with direct imports to improve code readability and maintainability.

Suggested change
const RouterProvider = (ReactRouterDom as any)['Router' + 'Provider'];
const createMemoryRouter = (ReactRouterDom as any)['create' + 'MemoryRouter'];
const createBrowserRouter = (ReactRouterDom as any)[
'create' + 'BrowserRouter'
];
import { RouterProvider, createMemoryRouter, createBrowserRouter } from 'react-router-dom/dist/index.js';
// Then use these imported functions directly

This change will make the code more explicit and easier to understand, while also potentially improving performance by allowing better static analysis and tree-shaking.

routerContextProps,
WraperRouterProps: props,
});
if (!routerContextProps) return <ReactRouterDom.BrowserRouter {...props} />;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition if (!routerContextProps) is always false because of the fallback to an empty object on line 14. This renders the check ineffective. Consider removing this condition or adjusting the logic if there's a specific scenario you're trying to handle.

if (!routerContextProps) return <RouterProvider {...props} />;

if (routerContextProps.memoryRoute) {
const MemeoryRouterInstance = createMemoryRouter(routers, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct the typo in 'MemeoryRouterInstance' to 'MemoryRouterInstance' for consistency and clarity.

Suggested change
const MemeoryRouterInstance = createMemoryRouter(routers, {
const MemoryRouterInstance = createMemoryRouter(routers, {

Comment on lines 53 to 55
const createBrowserRouter = (ReactRouterDom as any)[
'create' + 'BrowserRouter'
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace the string concatenation method used to access router creation functions with direct property access. This improves code readability and reduces the risk of runtime errors.

Suggested change
const createBrowserRouter = (ReactRouterDom as any)[
'create' + 'BrowserRouter'
];
const createBrowserRouter = (ReactRouterDom as any).createBrowserRouter;

Apply this change to the 'createMemoryRouter' assignment as well.

@@ -52,6 +53,7 @@ function WraperRouterProvider(
const createBrowserRouter = (ReactRouterDom as any)[
'create' + 'BrowserRouter'
];

if (!routerContextProps) return <RouterProvider {...props} />;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a type check for 'props' before spreading it into RouterProvider. This ensures type safety and prevents potential runtime errors if 'props' is undefined or null.

Suggested change
if (!routerContextProps) return <RouterProvider {...props} />;
if (!routerContextProps) return <RouterProvider {...(props || {})} />;


- name: Run condition check script
id: check-ci
run: node tools/scripts/ci-is-affected.mjs --appName=3005-runtime-host
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to include the subapp in the app name as well

@@ -1,6 +1,6 @@
# React Bridge

`@module-federation/bridge-react` 提供了用于 React 应用的 `bridge` 工具函数,其提供的 `createBridgeComponent` 可用于导出应用级别模块,`createRemoteComponent` 用于加载应用级别模块。[Demo](https://github.com/module-federation/core/core/tree/main/apps/router-demo)
`@module-federation/bridge-react` 提供了用于 React 应用的 `bridge` 工具函数,其提供的 `createBridgeComponent` 可用于导出应用级别模块,`createRemoteComponent` 用于加载应用级别模块。[Demo](https://github.com/module-federation/core/tree/main/apps/router-demo)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Increases which parameters can be passed to the created component

@@ -0,0 +1,113 @@
# remote2

## 1.0.15
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file can be deleted

}
},
destroy(info: { dom: HTMLElement }) {
async destroy(info: { dom: HTMLElement }) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to eliminate asynchrony

@@ -22,6 +22,8 @@ export default defineConfig({
entry: {
index: path.resolve(__dirname, 'src/index.ts'),
router: path.resolve(__dirname, 'src/router.tsx'),
'router-v5': path.resolve(__dirname, 'src/router-v5.tsx'),
'router-v6': path.resolve(__dirname, 'src/router-v6.tsx'),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rollup plugin can be used to optimize the two router code issues

@zhoushaw zhoushaw merged commit 49d6135 into main Aug 16, 2024
16 checks passed
@zhoushaw zhoushaw deleted the fix/mf-bridge branch August 16, 2024 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants