Skip to content

Commit

Permalink
feat: Utilities - Add async callback to importRemote (#459)
Browse files Browse the repository at this point in the history
* Add async callback to importRemote

* Update readme

* chore(utils): release version 1.0.3

* chore(node): release version 0.9.9

* chore(typescript): release version 2.1.1

* chore(nextjs-mf): release version 5.12.14

* ci: git tags should match with release versions (#461)

* fix(typescript): compiler host re-writes compiled files in case-insensitive file systems (#451)

* fix(typescript): compiler output folder structure
* chore: finalise
* docs: update readme
* docs: update typescript readme
* chore(typescript): cleanup - addressing comments

Co-authored-by: DV <pavan.divi@outlook.com>
Co-authored-by: Zack Jackson <zackary.l.jackson@gmail.com>

* chore: update nx to latest version (#463)

* Fix readme

Co-authored-by: Zack Jackson <zackary.l.jackson@gmail.com>
Co-authored-by: GitHub Bot <gituser@example.com>
Co-authored-by: Kyle Tsang <6854874+kyletsang@users.noreply.github.com>
Co-authored-by: Haydon Lam <58468238+hongkiulam@users.noreply.github.com>
Co-authored-by: DV <pavan.divi@outlook.com>
  • Loading branch information
6 people authored Dec 29, 2022
1 parent b1218c2 commit 508d83c
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 7 deletions.
94 changes: 89 additions & 5 deletions packages/utilities/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Run `nx build utils` to build the library.
Run `nx test utils` to execute the unit tests via [Jest](https://jestjs.io).

## React utilities

---

`FederatedBoundary`

A component wrapper that provides a fallback for safe imports if something were to fail when grabbing a module off of a remote host.
Expand All @@ -21,16 +23,98 @@ This wrapper also exposes an optional property for a custom react error boundary
Any extra props will be passed directly to the imported module.

Usage looks something like this:

```js
import { FederationBoundary } from "@module-federation/utilities/src/react";
import { FederationBoundary } from '@module-federation/utilities/src/react';

const MyPage = () => {
return (
<FederationBoundary
dynamicImport={() => import("some_remote_host_name").then((m) => m.Component)}
fallback={() => import("@npm/backup").then((m) => m.Component)}
dynamicImport={() =>
import('some_remote_host_name').then((m) => m.Component)
}
fallback={() => import('@npm/backup').then((m) => m.Component)}
customBoundary={CustomErrorBoundary}
/>
)
}
);
};
```

---

`ImportRemote`

A function which will enable dynamic imports of remotely exposed modules using the Module Federation plugin. It uses the method described in the official Webpack configuration under <a href="https://webpack.js.org/concepts/module-federation/#dynamic-remote-containers" target="_blank">Dynamic Remote Containers</a>.

This function will allow you to provide a static url or an async method to retrieve a url from a configuration service.

Usage looks something like this:

```js
import { importRemote } from '@module-federation/utilities';

// If it's a regular js module:
importRemote({
url: 'http://localhost:3001',
scope: 'Foo',
module: 'Bar',
}).then(
(
{
/* list of Bar exports */
}
) => {
// Use Bar exports
}
);

// If Bar is a React component you can use it with lazy and Suspense just like a dynamic import:
const Bar = lazy(() =>
importRemote({ url: 'http://localhost:3001', scope: 'Foo', module: 'Bar' })
);

return (
<Suspense fallback={<div>Loading Bar...</div>}>
<Bar />
</Suspense>
);
```

```js
import { importRemote } from '@module-federation/utilities';

// If it's a regular js module:
importRemote({
url: () => MyAsyncMethod('remote_name'),
scope: 'Foo',
module: 'Bar',
}).then(
(
{
/* list of Bar exports */
}
) => {
// Use Bar exports
}
);

// If Bar is a React component you can use it with lazy and Suspense just like a dynamic import:
const Bar = lazy(() =>
importRemote({
url: () => MyAsyncMethod('remote_name'),
scope: 'Foo',
module: 'Bar',
})
);

return (
<Suspense fallback={<div>Loading Bar...</div>}>
<Bar />
</Suspense>
);
```

Apart from **url**, **scope** and **module** you can also pass additional options to the **importRemote()** function:

- **remoteEntryFileName**: The name of the remote entry file. Defaults to "remoteEntry.js".
- **bustRemoteEntryCache**: Whether to add a cache busting query parameter to the remote entry file URL. Defaults to **true**. You can disable it if cachebusting is handled by the server.
18 changes: 16 additions & 2 deletions packages/utilities/src/utils/importRemote.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { WebpackRequire, WebpackShareScopes } from '../types';

type RemoteUrl = string | (() => Promise<string>);

export interface ImportRemoteOptions {
url: string;
url: RemoteUrl;
scope: string;
module: string;
remoteEntryFileName?: string;
Expand Down Expand Up @@ -71,9 +73,21 @@ export const importRemote = async <T>({
bustRemoteEntryCache = true,
}: ImportRemoteOptions): Promise<T> => {
if (!window[scope]) {
let remoteUrl = '';

if (typeof url === 'string') {
remoteUrl = url;
} else {
remoteUrl = await url();
}

// Load the remote and initialize the share scope if it's empty
await Promise.all([
loadRemote(`${url}/${remoteEntryFileName}`, scope, bustRemoteEntryCache),
loadRemote(
`${remoteUrl}/${remoteEntryFileName}`,
scope,
bustRemoteEntryCache
),
initSharing(),
]);
if (!window[scope]) {
Expand Down

0 comments on commit 508d83c

Please sign in to comment.