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

Remote example #57

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const pages = [
{name: 'Open API', link: '/open-api'},
{name: 'Frames', link: '/frames'},
// Uncomment to debug shared components
// {name: 'Shared', link: '/shared'},
{name: 'Shared', link: '/shared'},
];
const StyledLink = styled(Link)(({theme}) => {
return {
Expand Down
142 changes: 59 additions & 83 deletions packages/yii-dev-panel/src/Application/Pages/RemoteComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,79 @@
import {Alert} from '@mui/material';
import {FullScreenCircularProgress} from '@yiisoft/yii-dev-panel-sdk/Component/FullScreenCircularProgress';
import React, {Suspense} from 'react';

type UseDynamicScriptProps = {
url: string | undefined;
import {lazy, Suspense} from 'react';
import {ErrorBoundary} from 'react-error-boundary';

const remotesMap = {};
// const __vite__import = name => import(/* @vite-ignore */name);

const shareScope = {
// 'react': {
// 'default': {
// get: () => __vite__import('http://localhost:5001/assets/__federation_shared_react.js'),
// loaded: 1
// }
// },
// 'react-dom': {
// 'default': {
// get: () => __vite__import('http://localhost:5001/assets/__federation_shared_react-dom.js'),
// loaded: 1
// }
// },
};
const useDynamicScript = ({url}: UseDynamicScriptProps) => {
const [ready, setReady] = React.useState(false);
const [failed, setFailed] = React.useState(false);

React.useEffect(() => {
if (!url) {
return;
if (!globalThis.__federation_shared__) {
globalThis.__federation_shared__ = shareScope;
}
var __federation__ = {
ensure: async (remoteId) => {
const remote = remotesMap[remoteId];
if (remote.inited) {
return remote.lib;
}

const element = document.createElement('script');

element.src = url;
element.type = 'text/javascript';
element.async = true;

setReady(false);
setFailed(false);

element.onload = () => {
console.debug(`Dynamic Script Loaded: ${url}`);
setReady(true);
};

element.onerror = () => {
console.error(`Dynamic Script Error: ${url}`);
setReady(false);
setFailed(true);
};

document.head.appendChild(element);

return () => {
console.debug(`Dynamic Script Unloaded: ${url}`);
document.head.removeChild(element);
};
}, [url]);

return {
ready,
failed,
};
};

const loadComponent = (scope: string, module: string) => async () => {
/**
* Initializes the shared scope. This fills it with known provided modules from this build and all remotes
*/
// @ts-ignore
await __webpack_init_sharing__('default');
// @ts-ignore
const container = window[scope];
/**
* Initialize the container, it may provide shared modules
*/
// @ts-ignore
await container.init(__webpack_share_scopes__.default);
// @ts-ignore
const factory = await window[scope].get(module);
const Module = factory();
return Module;
return new Promise((resolve) => {
// debugger;
import(/* @vite-ignore */ remote.url).then((lib) => {
debugger;
console.log('lib', lib);
if (!remote.inited) {
lib.init(globalThis.__federation_shared__);
remote.lib = lib;
remote.inited = true;
}
resolve(remote.lib);
});
});
},
};
const loadComponent = (url, scope, module) => async () => {
remotesMap[scope] = {
url: url,
};

type ModuleLoaderProps = {
module: string;
url: string;
scope: string;
props: any;
return __federation__.ensure(scope).then((remote) => remote.get(module).then((factory) => factory()));
};

const ModuleLoader = ({module, props, scope, url}: ModuleLoaderProps) => {
const dynamicScript = useDynamicScript({
url: module && url,
});

const ModuleLoader = ({module, props, scope, url}) => {
if (!module) {
return <Alert severity="error">Module name cannot be empty</Alert>;
}

if (!dynamicScript.ready) {
return <FullScreenCircularProgress />;
}

if (dynamicScript.failed) {
return <Alert severity="error">Failed to load dynamic script: {url}</Alert>;
}

const Component = React.lazy(loadComponent(scope, module));
const Component = lazy(loadComponent(url, scope, module));

return (
<Suspense fallback={<FullScreenCircularProgress />}>
<Suspense fallback={<>Loading...</>}>
<Component {...props} />
</Suspense>
);
};

export const RemoteButton = () => {
const url = 'http://localhost:5001/assets/remoteEntry.js';

return (
<ErrorBoundary fallback={<>Failed to load remote component...</>}>
<ModuleLoader module={'./Button'} scope={'remote_app'} url={url} props={undefined} />
</ErrorBoundary>
);
};

export default ModuleLoader;
4 changes: 2 additions & 2 deletions packages/yii-dev-panel/src/Application/Pages/SharedPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ export function SharedPage() {
return (
<React.Suspense fallback={<FullScreenCircularProgress />}>
<ModuleLoader
url={'http://localhost:3002/external.js'}
url={'http://localhost:3002/assets/external.js'}
module={'./LogPanel'}
scope={'remote'}
props={{
data: logsData,
}}
/>
<ModuleLoader
url={'http://localhost:3002/external.js'}
url={'http://localhost:3002/assets/external.js'}
module={'./CachePanel'}
scope={'remote'}
props={{
Expand Down