Skip to content

Commit

Permalink
Merge pull request openshift#11700 from vojtechszocs/allow-shared-plu…
Browse files Browse the repository at this point in the history
…gin-module-fallbacks

CONSOLE-3179: Improve control over shared modules provided by Console to dynamic plugins
  • Loading branch information
openshift-merge-robot authored Sep 1, 2022
2 parents b1eb4fb + a02fda8 commit 8f857a6
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ import { initSharedPluginModules } from '../shared-modules-init';
import { getEntryModuleMocks } from '../utils/test-utils';

describe('initSharedPluginModules', () => {
const expectSameValues = (arr1: string[], arr2: string[]) => {
expect(new Set(arr1)).toEqual(new Set(arr2));
};

it('is consistent with sharedPluginModules definition', () => {
const [, entryModule] = getEntryModuleMocks({});

initSharedPluginModules(entryModule);

expect(entryModule.init).toHaveBeenCalledTimes(1);

expectSameValues(Object.keys(entryModule.init.mock.calls[0][0]), sharedPluginModules);
expect(new Set(Object.keys(entryModule.init.mock.calls[0][0]))).toEqual(
new Set(sharedPluginModules),
);
});

it('supports plugins built with an older version of plugin SDK', () => {
Expand All @@ -26,6 +24,8 @@ describe('initSharedPluginModules', () => {
expect(entryModule.override).toHaveBeenCalledTimes(1);
expect(entryModule.init).not.toHaveBeenCalled();

expectSameValues(Object.keys(entryModule.override.mock.calls[0][0]), sharedPluginModules);
expect(new Set(Object.keys(entryModule.override.mock.calls[0][0]))).toEqual(
new Set(sharedPluginModules),
);
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
type SharedModuleMetadata = Partial<{
/**
* If `true`, only a single version of the module can be loaded at runtime.
*
* @default true
*/
singleton: boolean;

/**
* If `true`, plugins will provide their own fallback version of the module.
*
* The fallback module will be loaded when a matching module is not found within
* the Console shared scope object. If the given module is declared as singleton
* and is already loaded, the fallback module will not load.
*
* @default false
*/
allowFallback: boolean;
}>;

/**
* Modules shared between the Console application and its dynamic plugins.
*/
Expand All @@ -15,4 +35,26 @@ export const sharedPluginModules = [
'react-redux',
'redux',
'redux-thunk',
];
] as const;

/**
* Metadata associated with the shared modules.
*/
export const sharedPluginModulesMetadata: Record<
typeof sharedPluginModules[number],
SharedModuleMetadata
> = {
'@openshift-console/dynamic-plugin-sdk': {},
'@openshift-console/dynamic-plugin-sdk-internal': {},
'@patternfly/react-core': {},
'@patternfly/react-table': {},
'@patternfly/quickstarts': {},
react: {},
'react-helmet': { singleton: false, allowFallback: true },
'react-i18next': {},
'react-router': {},
'react-router-dom': {},
'react-redux': {},
redux: {},
'redux-thunk': {},
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as readPkg from 'read-pkg';
import * as webpack from 'webpack';
import { remoteEntryFile } from '../constants';
import { ConsolePackageJSON } from '../schema/plugin-package';
import { sharedPluginModules } from '../shared-modules';
import { sharedPluginModulesMetadata } from '../shared-modules';
import { SchemaValidator } from '../validation/SchemaValidator';
import { loadSchema, ConsoleAssetPlugin } from './ConsoleAssetPlugin';

Expand Down Expand Up @@ -85,17 +85,25 @@ export class ConsoleRemotePlugin {
name: `exposed-${moduleName}`,
}),
),
shared: sharedPluginModules.reduce(
(acc, moduleRequest) => ({
...acc,
// https://webpack.js.org/plugins/module-federation-plugin/#sharing-hints
[moduleRequest]: {
// Allow only a single version of the shared module at runtime
// https://webpack.js.org/plugins/module-federation-plugin/#sharing-hints
shared: Object.entries(sharedPluginModulesMetadata).reduce(
(acc, [moduleRequest, moduleMetadata]) => {
const adaptedMetadata = _.defaults({}, moduleMetadata, {
singleton: true,
// Prevent plugins from using a fallback version of the shared module
import: false,
},
}),
allowFallback: false,
});

const moduleConfig: Record<string, any> = {
singleton: adaptedMetadata.singleton,
};

if (!adaptedMetadata.allowFallback) {
moduleConfig.import = false;
}

acc[moduleRequest] = moduleConfig;
return acc;
},
{},
),
}).apply(compiler);
Expand Down

0 comments on commit 8f857a6

Please sign in to comment.