Skip to content

feat: add bridgeHook plugin system to support lifecycyle in bridge #2992

Merged
zhoushaw merged 49 commits intomainfrom
feat/bridge-lifecycle-hook
Nov 14, 2024
Merged

feat: add bridgeHook plugin system to support lifecycyle in bridge #2992
zhoushaw merged 49 commits intomainfrom
feat/bridge-lifecycle-hook

Conversation

@danpeen
Copy link
Copy Markdown
Contributor

@danpeen danpeen commented Sep 24, 2024

Description

feat: support lifecycyle hooks in module-deferation bridge so that we can support more scene with bridge capability

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.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Sep 24, 2024

🦋 Changeset detected

Latest commit: e0b11d5

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

This PR includes changesets to release 27 packages
Name Type
@module-federation/bridge-react Patch
@module-federation/bridge-vue3 Patch
@module-federation/runtime Patch
@module-federation/devtools Patch
@module-federation/data-prefetch Patch
@module-federation/dts-plugin Patch
@module-federation/nextjs-mf Patch
@module-federation/node Patch
@module-federation/retry-plugin Patch
@module-federation/runtime-tools Patch
@module-federation/webpack-bundler-runtime Patch
@module-federation/enhanced Patch
@module-federation/modern-js Patch
@module-federation/rspack Patch
@module-federation/rsbuild-plugin Patch
@module-federation/storybook-addon Patch
@module-federation/modernjsapp Patch
@module-federation/sdk Patch
@module-federation/managers Patch
@module-federation/manifest Patch
@module-federation/third-party-dts-extractor Patch
@module-federation/bridge-shared Patch
@module-federation/bridge-react-webpack-plugin Patch
@module-federation/error-codes Patch
@module-federation/esbuild Patch
@module-federation/utilities Patch
website-new 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

@netlify
Copy link
Copy Markdown

netlify bot commented Sep 24, 2024

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit e0b11d5
🔍 Latest deploy log https://app.netlify.com/sites/module-federation-docs/deploys/673579332bebac0008fd9622
😎 Deploy Preview https://deploy-preview-2992--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.

Copy link
Copy Markdown
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

The pull request introduces a new "bridgeHook" plugin system to support lifecycle hooks in the module-deferation bridge, enabling better integration and support for more diverse use cases. The key changes include:

  • Addition of lifecycle hooks such as "beforeBridgeRender", "afterBridgeRender", "beforeBridgeDestroy", and "afterBridgeDestroy" to the bridge-react and vue3-bridge packages, allowing for custom actions during the bridge's rendering and destruction processes.
  • Enhancements to the FederationHost class in the runtime package to include the new bridgeHook plugin system, providing a way to extend the bridge's functionality.
  • Improvements to the module handling logic, including a new utility function "processModuleAlias" to ensure consistent module name formatting.
  • Updates to the SnapshotHandler class in the runtime package to include a new "afterLoadSnapshot" hook, enabling additional processing or handling of loaded snapshots.
  • Refinements to the type definitions, such as the introduction of the "RemoteInfoCommon" interface, to better support the new lifecycle hook functionality.

These changes aim to provide more flexibility and extensibility to the module-deferation bridge, allowing for better integration with the overall application and support for a wider range of use cases.

File Summaries
File Summary
packages/bridge/bridge-react/src/create.tsx The code changes introduce a new bridgeHook plugin system to support lifecycle hooks in the module-deferation bridge, allowing for more scene support with bridge capability. The changes include the addition of an ErrorBoundary component and updates to the ProviderParams type to enable this new functionality.
packages/bridge/bridge-react/src/provider.tsx The code changes introduce a new plugin system called "bridgeHook" to support lifecycle hooks in the module-federation bridge. This allows for more flexibility and extensibility in the bridge's capabilities, enabling support for additional use cases and scenarios. The changes include the implementation of several lifecycle hooks, such as "beforeBridgeRender", "afterBridgeRender", "beforeBridgeDestroy", and "afterBridgeDestroy", which can be used to perform custom actions during the bridge's rendering and destruction processes.
packages/bridge/bridge-react/src/remote/index.tsx The code changes introduce a new plugin system called "bridgeHook" to support lifecycle hooks in the module-deferation bridge. This allows for more flexibility and support for different scenarios where the bridge capability is used.
packages/bridge/bridge-react/src/router-v5.tsx The code changes introduce a new plugin system for the bridge-react module, allowing for the addition of lifecycle hooks to support more use cases within the module-deferation bridge. The changes focus on enabling support for lifecycle hooks in the router-v5 component, which is a key part of the bridge-react module.
packages/bridge/bridge-react/src/router.tsx The code changes introduce a new feature to support lifecycle hooks in the module-deferation bridge, allowing for more scene-specific functionality. The key modification is the addition of a new optional basename property to the createBrowserRouter function, which can be derived from the routerContextProps or the router object.
packages/bridge/bridge-react/src/utils.ts The code changes introduce a new utility function getModuleName that extracts the module name from a given module ID, and a new function getRootDomDefaultClassName that generates a default CSS class name for the root component of a module based on its module name. These changes are likely part of a larger effort to add support for lifecycle hooks in the module-deferation bridge, which will enable more flexibility in handling different use cases.
packages/bridge/vue3-bridge/src/create.ts The changes introduce a new plugin system for the bridge module, allowing for the support of lifecycle hooks in the module-deferation bridge. This enables the bridge to handle more complex scenarios and provide better integration with the overall application.
packages/bridge/vue3-bridge/src/provider.ts The code changes introduce a new bridgeHook plugin system to support lifecycle hooks in the module-federation bridge. This allows for more flexibility and customization when using the bridge, enabling developers to hook into the bridge's lifecycle events and add additional functionality as needed.
packages/bridge/vue3-bridge/src/remoteApp.tsx The code changes introduce a new plugin system called "bridgeHook" to support lifecycle hooks in the module-federation bridge. This allows for more flexibility and extensibility in handling different scenarios where the bridge capability is used. The changes include adding new lifecycle hooks such as "beforeBridgeRender", "afterBridgeRender", "beforeBridgeDestroy", and "afterBridgeDestroy" to enable better control and customization of the bridge's behavior.
packages/runtime/src/core.ts The code changes introduce a new bridgeHook plugin system to the FederationHost class, which allows for the addition of lifecycle hooks to support more scenarios with the bridge capability. The new plugin system includes four hooks: beforeBridgeRender, afterBridgeRender, beforeBridgeDestroy, and afterBridgeDestroy, which can be used to extend the functionality of the bridge.
packages/runtime/src/embedded.ts The code changes introduce a new bridgeHook property to the FederationHost class, which allows for the support of lifecycle hooks in the module-deferation bridge. This enhancement enables the handling of more scenarios with bridge capabilities.
packages/runtime/src/module/index.ts The code changes introduce a new feature to support lifecycle hooks in the module-deferation bridge, allowing for more scene support with bridge capability. The key modifications include adding a new utility function processModuleAlias to ensure a consistent module name format, and updating the wrapModuleFactory method to use the processed module name instead of the raw remote information.
packages/runtime/src/plugins/snapshot/SnapshotHandler.ts The code changes introduce a new afterLoadSnapshot hook in the SnapshotHandler class. This hook is triggered after the remote snapshot is loaded, allowing for additional processing or handling of the loaded snapshot. The changes also refactor the existing snapshot loading logic to separate the assignment of the mSnapshot and gSnapshot variables, making the code more readable and maintainable.
packages/runtime/src/type/config.ts The code changes introduce a new RemoteInfoCommon interface to the config.ts file, which defines the common properties for remote information. This change aims to support lifecycle hooks in the module-deferation bridge, enabling more scene-specific functionality to be added to the bridge capability.
packages/runtime/src/type/plugin.ts The code changes introduce a new plugin system for the module-deferation bridge, allowing for the support of lifecycle hooks. This enables the bridge to handle more diverse scenarios by providing a way to extend its capabilities through plugins.
packages/runtime/src/utils/plugin.ts The code changes introduce a new bridgeHook property to the Module['host'] interface, allowing for the registration of lifecycle hooks in the module-deferation bridge. This enhancement enables support for more diverse scenarios where the bridge capability is utilized, providing greater flexibility and extensibility to the application.
packages/runtime/src/utils/tool.ts The code changes introduce a new utility function called processModuleAlias that takes a module name and a sub-path, and returns a processed module name. The function handles cases where the module name ends with a slash and the sub-path starts with a dot, ensuring the final module name is correctly formatted.

Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 23

Configuration

Squadron Mode: essential

Commits Reviewed

44ac346ef4212a17e4b2d611ba0e7948ba804947...4e319bb2d0708da2b1f89bad9f1b2aec4c1029c2

Files Reviewed
  • packages/bridge/bridge-react/src/create.tsx
  • packages/bridge/bridge-react/src/lifecycle.ts
  • packages/bridge/bridge-react/src/provider.tsx
  • packages/bridge/bridge-react/src/remote/index.tsx
  • packages/bridge/bridge-react/src/router-v6.tsx
  • packages/bridge/bridge-react/src/router.tsx
  • packages/bridge/vue3-bridge/src/create.ts
  • packages/bridge/vue3-bridge/src/lifecycle.ts
  • packages/bridge/vue3-bridge/src/provider.ts
  • packages/bridge/vue3-bridge/src/remoteApp.tsx
  • packages/runtime/src/helpers.ts
  • packages/runtime/src/module/index.ts
  • packages/runtime/src/plugins/snapshot/SnapshotHandler.ts
  • packages/runtime/src/type/config.ts
  • packages/runtime/src/type/plugin.ts
  • packages/runtime/src/utils/plugin.ts
  • packages/runtime/src/utils/tool.ts
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

@module-federation module-federation deleted a comment from squadronai bot Sep 26, 2024
@module-federation module-federation deleted a comment from squadronai bot Sep 26, 2024
@module-federation module-federation deleted a comment from squadronai bot Sep 26, 2024
@danpeen danpeen changed the title feat: support lifecycyle hooks in module-deferation bridge feat: add bridgeHook plugin system to support lifecycyle in bridge Nov 7, 2024
Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 1

Configuration

Squadron Mode: essential

Commits Reviewed

8ff7ef654a77f4388ccb8e29dd95df9d3a447925...dfb6d7761330c4fa21e067e8b770d57bc5da9a3f

Files Reviewed
  • packages/bridge/vue3-bridge/src/provider.ts
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines +13 to +16
beforeBridgeRender?: (params: RenderFnParams) => void | Record<string, any>;
afterBridgeRender?: (params: RenderFnParams) => void | Record<string, any>;
beforeBridgeDestroy?: (params: DestroyParams) => void | Record<string, any>;
afterBridgeDestroy?: (params: DestroyParams) => void | Record<string, any>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The hook types should be more consistent in their return types. Currently, all hooks allow returning Record<string, any>, but typically only beforeBridgeRender needs to return extra props. Consider:

Suggested change
beforeBridgeRender?: (params: RenderFnParams) => void | Record<string, any>;
afterBridgeRender?: (params: RenderFnParams) => void | Record<string, any>;
beforeBridgeDestroy?: (params: DestroyParams) => void | Record<string, any>;
afterBridgeDestroy?: (params: DestroyParams) => void | Record<string, any>;
beforeBridgeRender?: (params: RenderFnParams) => void | { extraProps?: Record<string, any> };
afterBridgeRender?: (params: RenderFnParams) => void;
beforeBridgeDestroy?: (params: DestroyParams) => void;
afterBridgeDestroy?: (params: DestroyParams) => void;

This makes the API more explicit about which hooks can meaningfully return data.

@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
@module-federation module-federation deleted a comment from squadronai bot Nov 7, 2024
Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 2

Configuration

Squadron Mode: essential

Commits Reviewed

dfb6d7761330c4fa21e067e8b770d57bc5da9a3f...f2e962492ecaab4795c702beaa18acabeeabd49a

Files Reviewed
  • packages/runtime/src/type/plugin.ts
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines 40 to 47
SnapshotLifeCycleCyclePartial &
SharedLifeCycleCyclePartial &
RemoteLifeCycleCyclePartial &
ModuleLifeCycleCyclePartial & {
ModuleLifeCycleCyclePartial &
ModuleBridgeLifeCycleCyclePartial & {
name: string;
version?: string;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The plugin interface combines multiple lifecycle partials. Consider adding a descriptive type comment to explain the purpose of each lifecycle component and their relationships. Additionally, the optional version field could benefit from a more specific type if there are expected version formats:

Suggested change
SnapshotLifeCycleCyclePartial &
SharedLifeCycleCyclePartial &
RemoteLifeCycleCyclePartial &
ModuleLifeCycleCyclePartial & {
ModuleLifeCycleCyclePartial &
ModuleBridgeLifeCycleCyclePartial & {
name: string;
version?: string;
};
/**
* Plugin configuration interface combining multiple lifecycle hooks:
* - SnapshotLifeCycle: Handles snapshot-related events
* - SharedLifeCycle: Manages shared module events
* - RemoteLifeCycle: Controls remote module loading
* - ModuleLifeCycle: Core module lifecycle events
* - ModuleBridgeLifeCycle: Bridge-specific events
*/
type Plugin =
SnapshotLifeCycleCyclePartial &
SharedLifeCycleCyclePartial &
RemoteLifeCycleCyclePartial &
ModuleLifeCycleCyclePartial &
ModuleBridgeLifeCycleCyclePartial & {
name: string;
version?: `${number}.${number}.${number}`; // Semantic versioning format
};

Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 2

Configuration

Squadron Mode: essential

Commits Reviewed

f2e962492ecaab4795c702beaa18acabeeabd49a...b25a91bd26bc94906c08848d36111af48e2a009d

Files Reviewed
  • packages/bridge/bridge-react/src/provider.tsx
  • packages/bridge/bridge-react/src/remote/index.tsx
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines 147 to 151
providerInfoRef.current?.destroy({
moduleName,
dom: renderDom.current,
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The destroy call should handle errors gracefully to prevent potential crashes during cleanup. Consider wrapping it in a try-catch block:

Suggested change
providerInfoRef.current?.destroy({
moduleName,
dom: renderDom.current,
});
}
try {
providerInfoRef.current?.destroy({
moduleName,
dom: renderDom.current,
});
} catch (error) {
console.error('Error during bridge component destruction:', error);
}

This ensures that any errors during cleanup won't propagate up and potentially break the application's unmounting process.

Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 9

Configuration

Squadron Mode: essential

Commits Reviewed

b25a91bd26bc94906c08848d36111af48e2a009d...7d6846c84022c63d0847a4e66af8bc06acd026da

Files Reviewed
  • packages/bridge/bridge-react/src/provider.tsx
  • packages/bridge/bridge-react/src/remote/index.tsx
  • packages/bridge/bridge-react/src/utils.ts
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines +115 to +122
async destroy(info: DestroyParams) {
LoggerInstance.log(`createBridgeComponent destroy Info`, {
dom: info.dom,
});
const instance = getModuleInstance(info.moduleName);
LoggerInstance.log(`createBridgeComponent remote instance`, instance);
instance?.bridgeHook?.lifecycle?.beforeBridgeDestroy?.emit(info);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The destroy method has duplicate logging and similar lifecycle hook pattern. Consider extracting common logging logic:

Suggested change
async destroy(info: DestroyParams) {
LoggerInstance.log(`createBridgeComponent destroy Info`, {
dom: info.dom,
});
const instance = getModuleInstance(info.moduleName);
LoggerInstance.log(`createBridgeComponent remote instance`, instance);
instance?.bridgeHook?.lifecycle?.beforeBridgeDestroy?.emit(info);
async destroy(info: DestroyParams) {
const logContext = {
action: 'destroy',
dom: info.dom,
moduleName: info.moduleName
};
LoggerInstance.log('createBridgeComponent destroy info', logContext);
const instance = getModuleInstance(info.moduleName);
getLifecycleHook(instance, 'beforeBridgeDestroy')?.(info);

Comment on lines +44 to +59
const getHostInstance = (moduleName: string) => {
let hostInstance: FederationHost | undefined = undefined;
const currentName = getModuleName(moduleName);
const remoteInstance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === currentName,
);

// @ts-ignore
if (remoteInstance && remoteInstance?.hostName) {
// @ts-ignore
hostInstance = window.__VMOK__.__INSTANCES__.find(
(instance) => instance.name === remoteInstance?.hostName,
);
}
return hostInstance;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The getHostInstance function contains multiple unsafe type assertions and optional chaining. Consider adding proper type guards and error handling:

Suggested change
const getHostInstance = (moduleName: string) => {
let hostInstance: FederationHost | undefined = undefined;
const currentName = getModuleName(moduleName);
const remoteInstance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === currentName,
);
// @ts-ignore
if (remoteInstance && remoteInstance?.hostName) {
// @ts-ignore
hostInstance = window.__VMOK__.__INSTANCES__.find(
(instance) => instance.name === remoteInstance?.hostName,
);
}
return hostInstance;
};
const getHostInstance = (moduleName: string): FederationHost | undefined => {
if (!moduleName || !window?.__FEDERATION__?.__INSTANCES__) {
return undefined;
}
const currentName = getModuleName(moduleName);
const remoteInstance = window.__FEDERATION__.__INSTANCES__.find(
(v) => v.name === currentName
);
if (!remoteInstance?.hostName || !window.__VMOK__?.__INSTANCES__) {
return undefined;
}
return window.__VMOK__.__INSTANCES__.find(
(instance) => instance.name === remoteInstance.hostName
);
};

Comment on lines +132 to +139
hostInstance?.bridgeHook?.lifecycle?.beforeBridgeDestroy?.emit({
moduleName,
dom: renderDom.current,
basename,
memoryRoute,
fallback,
...resProps,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The lifecycle hook parameters are duplicated between beforeBridgeDestroy and afterBridgeDestroy. Consider extracting the common parameters into a shared object:

Suggested change
hostInstance?.bridgeHook?.lifecycle?.beforeBridgeDestroy?.emit({
moduleName,
dom: renderDom.current,
basename,
memoryRoute,
fallback,
...resProps,
});
const destroyParams = {
moduleName,
dom: renderDom.current,
basename,
memoryRoute,
fallback,
...resProps,
};
hostInstance?.bridgeHook?.lifecycle?.beforeBridgeDestroy?.emit(destroyParams);

Comment on lines +45 to +56
export const getModuleName = (id: string) => {
if (!id) {
return id;
}
// separate module name without detailed module path
// @vmok-e2e/edenx-demo-app2/button -> @vmok-e2e/edenx-demo-app2
const idArray = id.split('/');
if (idArray.length < 2) {
return id;
}
return idArray[0] + '/' + idArray[1];
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The getModuleName function could be more robust and maintainable by using a regular expression pattern to handle module name extraction. Here's a suggested improvement:

Suggested change
export const getModuleName = (id: string) => {
if (!id) {
return id;
}
// separate module name without detailed module path
// @vmok-e2e/edenx-demo-app2/button -> @vmok-e2e/edenx-demo-app2
const idArray = id.split('/');
if (idArray.length < 2) {
return id;
}
return idArray[0] + '/' + idArray[1];
};
export const getModuleName = (id: string) => {
if (!id) {
return id;
}
// Match scope/package pattern (e.g., @vmok-e2e/edenx-demo-app2)
const match = id.match(/^(@[^/]+\/[^/]+)/);
return match ? match[1] : id;
};

This approach is more resilient to different module name patterns and easier to maintain.

Comment on lines +58 to +64
export const getRootDomDefaultClassName = (moduleName: string) => {
if (!moduleName) {
return '';
}
const name = getModuleName(moduleName).replace(/\@/, '').replace(/\//, '-');
return `bridge-root-component-${name}`;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The getRootDomDefaultClassName function could benefit from improved string sanitization and TypeScript type safety:

Suggested change
export const getRootDomDefaultClassName = (moduleName: string) => {
if (!moduleName) {
return '';
}
const name = getModuleName(moduleName).replace(/\@/, '').replace(/\//, '-');
return `bridge-root-component-${name}`;
};
export const getRootDomDefaultClassName = (moduleName: string): string => {
if (!moduleName) {
return '';
}
const sanitizedName = getModuleName(moduleName)
.replace(/[@/]/g, '-')
.replace(/^-+/, ''); // Remove leading dashes
return `bridge-root-component-${sanitizedName}`;
};

This version:

  1. Uses a single regex replace for better performance
  2. Handles multiple occurrences of @ and /
  3. Removes potential leading dashes
  4. Adds explicit return type for better type safety

Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 6

Configuration

Squadron Mode: essential

Commits Reviewed

7d6846c84022c63d0847a4e66af8bc06acd026da...5177925fa52cf2780454005888454cf35dabe3d7

Files Reviewed
  • packages/bridge/bridge-react/src/provider.tsx
  • packages/bridge/bridge-react/src/remote/index.tsx
  • packages/bridge/bridge-react/src/utils.ts
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines +67 to +82
export const getHostInstance = (moduleName: string) => {
let hostInstance: FederationHost | undefined = undefined;
const currentName = getModuleName(moduleName);
const remoteInstance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === currentName,
);

// @ts-ignore
if (remoteInstance && remoteInstance?.hostName) {
hostInstance = window.__VMOK__.__INSTANCES__.find(
// @ts-ignore
(instance) => instance.name === remoteInstance?.hostName,
);
}
return hostInstance;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The getHostInstance function has several issues that should be addressed:

  1. Unsafe property access chains could lead to runtime errors. Use optional chaining consistently:
Suggested change
export const getHostInstance = (moduleName: string) => {
let hostInstance: FederationHost | undefined = undefined;
const currentName = getModuleName(moduleName);
const remoteInstance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === currentName,
);
// @ts-ignore
if (remoteInstance && remoteInstance?.hostName) {
hostInstance = window.__VMOK__.__INSTANCES__.find(
// @ts-ignore
(instance) => instance.name === remoteInstance?.hostName,
);
}
return hostInstance;
};
export const getHostInstance = (moduleName: string) => {
const currentName = getModuleName(moduleName);
const remoteInstance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === currentName,
);
if (remoteInstance?.hostName) {
return window?.__VMOK__?.__INSTANCES__?.find(
(instance) => instance.name === remoteInstance.hostName,
);
}
return undefined;
};

This improves:

  • Type safety by removing @ts-ignore
  • Null checks with consistent optional chaining
  • Code readability with early return
  • Removes unnecessary variable declaration

Comment on lines +84 to +90
export const getRemoteInstance = (moduleName: string) => {
const moduleNameWithoutExpose = getModuleName(moduleName);
const instance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === moduleNameWithoutExpose,
);
return instance;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The getRemoteInstance function could be simplified and made more type-safe:

Suggested change
export const getRemoteInstance = (moduleName: string) => {
const moduleNameWithoutExpose = getModuleName(moduleName);
const instance = window?.__FEDERATION__?.__INSTANCES__?.find(
(v) => v.name === moduleNameWithoutExpose,
);
return instance;
};
export const getRemoteInstance = (moduleName: string) => {
const moduleNameWithoutExpose = getModuleName(moduleName);
return window?.__FEDERATION__?.__INSTANCES__?.find(
(instance) => instance.name === moduleNameWithoutExpose,
) ?? undefined;
};

This improves:

  • Explicit return type with nullish coalescing
  • Consistent parameter naming
  • More descriptive variable name for the find callback

Comment on lines +63 to +65
const name = getModuleName(moduleName).replace(/\@/, '').replace(/\//, '-');
return `bridge-root-component-${name}`;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The string manipulation in this function could be more robust using a regular expression:

Suggested change
const name = getModuleName(moduleName).replace(/\@/, '').replace(/\//, '-');
return `bridge-root-component-${name}`;
};
const name = getModuleName(moduleName).replace(/[@/]/g, '-');
return `bridge-root-component-${name}`;

This improves:

  • Single regex replacement instead of multiple calls
  • More efficient string manipulation
  • Maintains the same functionality with cleaner code

export const getHostInstance = (moduleName: string) => {
let hostInstance: FederationHost | undefined = undefined;
const currentName = getModuleName(moduleName);
const remoteInstance = window?.__FEDERATION__?.__INSTANCES__?.find(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Consider possibly running in node

Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 6

Configuration

Squadron Mode: essential

Commits Reviewed

5177925fa52cf2780454005888454cf35dabe3d7...afca10800fce591a5594cdcca04df1e2a944b5d1

Files Reviewed
  • packages/bridge/bridge-react/src/provider.tsx
  • packages/bridge/bridge-react/src/remote/index.tsx
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines +14 to +15
type RenderParams = RenderFnParams & any;
type DestroyParams = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The RenderParams type extends RenderFnParams with any, which reduces type safety. Consider being more specific about additional parameters:

Suggested change
type RenderParams = RenderFnParams & any;
type DestroyParams = {
type RenderParams = RenderFnParams & {
// Add specific additional parameters here
[key: string]: unknown; // If truly extensible is needed
};

Comment on lines 35 to 37
const RawComponent = (info: { propsInfo: T; appInfo: ProviderParams }) => {
const { appInfo, propsInfo, ...restProps } = info;
const { moduleName, memoryRoute, basename = '/' } = appInfo;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The destructuring in RawComponent includes ...restProps but it's not used in the type definition. Add proper typing for additional props:

Suggested change
const RawComponent = (info: { propsInfo: T; appInfo: ProviderParams }) => {
const { appInfo, propsInfo, ...restProps } = info;
const { moduleName, memoryRoute, basename = '/' } = appInfo;
const RawComponent = (info: { propsInfo: T; appInfo: ProviderParams; [key: string]: unknown }) => {
const { appInfo, propsInfo, ...restProps } = info;
const { moduleName, memoryRoute, basename = '/' } = appInfo;

import { LoggerInstance, pathJoin, getRootDomDefaultClassName } from '../utils';
import { getInstance } from '@module-federation/runtime';

declare const __APP_VERSION__: string;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The __APP_VERSION__ declaration lacks a JSDoc comment explaining its purpose and expected format. Add documentation since it appears to be an important global constant: ```suggestion
/** Application version identifier injected during build time */
declare const APP_VERSION: string;


For the second code block (lines 60-66):

@@ -59,13 +60,15 @@ const RemoteAppWrapper = forwardRef(function (

const renderDom: React.MutableRefObject<HTMLElement | null> = useRef(null);
const providerInfoRef = useRef<any>(null);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The providerInfoRef uses any type which reduces type safety. Consider creating a proper interface for the provider info: ```suggestion
const providerInfoRef = useRef<ProviderParams | null>(null);

const renderDom: React.MutableRefObject<HTMLElement | null> = useRef(null);
const providerInfoRef = useRef<any>(null);
const hostInstance = getInstance();
LoggerInstance.log(`RemoteAppWrapper hostInstance >>>`, hostInstance);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Using string concatenation in logs makes it harder to parse and filter logs. Use object notation instead: ```suggestion
LoggerInstance.log('RemoteAppWrapper hostInstance', { hostInstance });

Copy link
Copy Markdown
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.

Incremental Review

Comments posted: 7

Configuration

Squadron Mode: essential

Commits Reviewed

afca10800fce591a5594cdcca04df1e2a944b5d1...92368fce91493967859d54e8aa9ac109d9cea82f

Files Reviewed
  • packages/bridge/bridge-react/src/provider.tsx
  • packages/bridge/vue3-bridge/src/provider.ts
  • packages/bridge/vue3-bridge/src/remoteApp.tsx
Files Ignored

These files were ignored due to the filter in the squadron.yaml file.

  • .changeset/great-feet-rule.md
  • packages/bridge/bridge-react/package.json
  • packages/bridge/vue3-bridge/package.json
  • pnpm-lock.yaml

Comment on lines +20 to +21
const beforeBridgeRenderRes =
instance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(info) || {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The beforeBridgeRender hook result should be properly type-checked. Consider adding type safety:

Suggested change
const beforeBridgeRenderRes =
instance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(info) || {};
const beforeBridgeRenderRes = (
instance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(info) || {}
) as { extraProps?: Record<string, any> };

app.mount(info.dom);
}

instance?.bridgeHook?.lifecycle?.afterBridgeRender?.emit(info) || {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The afterBridgeRender hook has an unnecessary || {} that doesn't serve any purpose since the result isn't used:

Suggested change
instance?.bridgeHook?.lifecycle?.afterBridgeRender?.emit(info) || {};
instance?.bridgeHook?.lifecycle?.afterBridgeRender?.emit(info);

memoryRoute: props.memoryRoute,
});

(providerInfoRef.value as any)?.destroy({ dom: rootRef.value });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The type assertion and optional chaining could be improved with proper type checking:

Suggested change
(providerInfoRef.value as any)?.destroy({ dom: rootRef.value });
if (providerInfoRef.value && typeof providerInfoRef.value.destroy === 'function') {
providerInfoRef.value.destroy({ dom: rootRef.value });
}

return () => {
const rootMap = new Map<any, RootType>();
const instance = getInstance();
LoggerInstance.log(`createBridgeComponent remote instance`, instance);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

instance may be null. What exception message is thrown for null

@zhoushaw zhoushaw merged commit ff8ce29 into main Nov 14, 2024
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.

4 participants