Skip to content
Merged
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 @@ -18,4 +18,11 @@ describe('SDK module loading', () => {
cy.get(`[aria-label="Checked"]`).should('exist');
cy.get('#plugin-manifest').should('exist');
});

it('should render delayed module without processing entire manifest', () => {
cy.visit('http://localhost:4200/sdk');
// Delayed module is fetched after 5 seconds
cy.wait(5001);
cy.get('#delayed-module').should('exist');
});
});
32 changes: 29 additions & 3 deletions examples/test-app/src/routes/SDKModules.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
import React from 'react';
import { useEffect, useState } from 'react';
import { ScalprumComponent } from '@scalprum/react-core';
import { Grid } from '@mui/material';
import { Grid, Typography } from '@mui/material';

const SDKModules = () => {
const [delayed, setDelayed] = useState(false);
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => {
setDelayed(true);
}, 5000);
const interval = setInterval(() => {
if (seconds >= 6) {
clearInterval(interval);
return;
}
setSeconds((prevSeconds) => prevSeconds + 1);
}, 1000);
return () => {
clearTimeout(timeout);
clearInterval(interval);
};
}, []);
const props = {
name: 'plugin-manifest',
};
return (
<Grid container spacing={4}>
<Grid xs={12} md={6} item>
<ScalprumComponent scope="sdk-plugin" module="./SDKComponent" />;
<ScalprumComponent scope="sdk-plugin" module="./SDKComponent" />
</Grid>
<Grid xs={12} md={6} item>
<ScalprumComponent scope="sdk-plugin" module="./SDKComponent" importName="NamedSDKComponent" />
</Grid>
<Grid xs={12} md={6} item>
<ScalprumComponent scope="full-manifest" module="./SDKComponent" importName="PluginSDKComponent" {...props} />
</Grid>

<Grid xs={12} md={6} item>
{delayed ? (
<ScalprumComponent scope="sdk-plugin" module="./DelayedModule" />
) : (
<Typography>Loading delayed module in {5 - seconds} seconds</Typography>
)}
</Grid>
</Grid>
);
};
Expand Down
41 changes: 41 additions & 0 deletions federation-cdn-mock/src/modules/delayedModule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react';
import FormatAlignLeftIcon from '@mui/icons-material/FormatAlignLeft';
import FormatAlignCenterIcon from '@mui/icons-material/FormatAlignCenter';
import FormatAlignRightIcon from '@mui/icons-material/FormatAlignRight';
import FormatAlignJustifyIcon from '@mui/icons-material/FormatAlignJustify';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';

export default function ToggleButtons() {
const [alignment, setAlignment] = React.useState<string | null>('left');

const handleAlignment = (
event: React.MouseEvent<HTMLElement>,
newAlignment: string | null,
) => {
setAlignment(newAlignment);
};

return (
<ToggleButtonGroup
value={alignment}
exclusive
onChange={handleAlignment}
aria-label="text alignment"
id="delayed-module"
>
<ToggleButton value="left" aria-label="left aligned">
<FormatAlignLeftIcon />
</ToggleButton>
<ToggleButton value="center" aria-label="centered">
<FormatAlignCenterIcon />
</ToggleButton>
<ToggleButton value="right" aria-label="right aligned">
<FormatAlignRightIcon />
</ToggleButton>
<ToggleButton value="justify" aria-label="justified" disabled>
<FormatAlignJustifyIcon />
</ToggleButton>
</ToggleButtonGroup>
);
}
1 change: 1 addition & 0 deletions federation-cdn-mock/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const TestSDKPlugin = new DynamicRemotePlugin({
'./ModuleFour': resolve(__dirname, './src/modules/moduleFour.tsx'),
'./SDKComponent': resolve(__dirname, './src/modules/SDKComponent.tsx'),
'./ApiModule': resolve(__dirname, './src/modules/apiModule.tsx'),
'./DelayedModule': resolve(__dirname, './src/modules/delayedModule.tsx'),
},
},
});
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ describe('scalprum', () => {
pendingInjections: {},
pendingLoading: {},
pendingPrefetch: {},
existingScopes: new Set(),
api: {},
scalprumOptions: {
cacheTimeout: 120,
Expand Down
26 changes: 22 additions & 4 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type Scalprum<T extends Record<string, any> = Record<string, any>> = {
pendingPrefetch: {
[key: string]: Promise<unknown>;
};
existingScopes: Set<string>;
exposedModules: {
[moduleId: string]: ExposedScalprumModule;
};
Expand Down Expand Up @@ -248,6 +249,7 @@ export const initialize = <T extends Record<string, any> = Record<string, any>>(
pendingInjections: {},
pendingLoading: {},
pendingPrefetch: {},
existingScopes: new Set<string>(),
exposedModules: {},
scalprumOptions: defaultOptions,
api: api || {},
Expand All @@ -263,7 +265,11 @@ export const removeScalprum = () => {

export const getAppData = (name: string): AppMetadata => getScalprum().appsConfig[name];

const setExposedModule = (moduleId: string, exposedModule: ExposedScalprumModule) => {
const setExposedModule = (scope: string, module: string, exposedModule: ExposedScalprumModule) => {
if (!getScalprum().existingScopes.has(scope)) {
getScalprum().existingScopes.add(scope);
}
const moduleId = getModuleIdentifier(scope, module);
getScalprum().exposedModules[moduleId] = exposedModule;
};

Expand Down Expand Up @@ -302,11 +308,23 @@ export async function processManifest(
processor?: (manifest: any) => string[],
): Promise<void> {
let pendingInjection = getPendingInjection(scope);
const { pluginStore } = getScalprum();
const { pluginStore, existingScopes } = getScalprum();

if (existingScopes.has(scope)) {
try {
const exposedModule = await pluginStore.getExposedModule<ExposedScalprumModule>(scope, module);
setExposedModule(scope, module, exposedModule);
return;
} catch (error) {
console.warn('Unable to load module from existing container', error);
console.warn('Scalprum will try to process manifest from scratch.');
}
}

if (pendingInjection) {
await pendingInjection;
const exposedModule = await pluginStore.getExposedModule<ExposedScalprumModule>(scope, module);
setExposedModule(getModuleIdentifier(scope, module), exposedModule);
setExposedModule(scope, module, exposedModule);
return;
}

Expand Down Expand Up @@ -361,7 +379,7 @@ export async function processManifest(
await pluginStore.loadPlugin(sdkManifest);
try {
const exposedModule = await pluginStore.getExposedModule<ExposedScalprumModule>(scope, module);
setExposedModule(getModuleIdentifier(scope, module), exposedModule);
setExposedModule(scope, module, exposedModule);
return;
} catch (error) {
clearPendingInjection(scope);
Expand Down