Skip to content
Open
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 @@ -119,7 +119,7 @@ describe('initContainerEntry with array-based share scopes', () => {
});

// Execute
const result = initContainerEntry(mockOptions);
initContainerEntry(mockOptions);

// Verify
expect(
Expand Down Expand Up @@ -432,7 +432,7 @@ describe('initContainerEntry with array-based share scopes', () => {

test('should behave differently for proxyInitializeSharing=false vs true with array shareScopeKey', () => {
// Mock setup for shared=false (proxyInitializeSharing=false)
const mockIFunctionFalse = jest.fn().mockImplementation((key) => {
const mockIFunctionFalse = jest.fn().mockImplementation((_key) => {
return Promise.resolve(true);
});

Expand Down Expand Up @@ -492,7 +492,7 @@ describe('initContainerEntry with array-based share scopes', () => {

test('should handle proxyInitializeSharing=false with array shareScopeKey', async () => {
// Setup with shared: false (making proxyInitializeSharing false)
const mockIFunction = jest.fn().mockImplementation((key) => {
const mockIFunction = jest.fn().mockImplementation((_key) => {
return Promise.resolve(true);
});

Expand Down Expand Up @@ -1114,4 +1114,94 @@ describe('initContainerEntry with array-based share scopes', () => {
expect(mockIFunction).toHaveBeenCalledWith('key3', ['test-scope']);
expect(mockIFunction).toHaveBeenCalledTimes(3);
});

test('should preserve non-default share scopes across repeated init with incomplete host shareScopeMap', () => {
const defaultScope = {
react: {
'18.2.0': {
scope: ['default'],
},
},
};
const customScope = {
'@tanstack/react-query': {
'5.0.0': {
scope: ['custom'],
},
},
};
const shareScopeMap: Record<string, Record<string, any>> = {};
const federationInstance = createMockFederationInstance({
shareScopeMap,
initShareScopeMap: jest.fn(
(scopeName: string, scope: Record<string, any>) => {
shareScopeMap[scopeName] = scope;
},
),
});
const webpackRequire = createMockWebpackRequire({
I: jest.fn().mockReturnValue(Promise.resolve(true)),
federation: createMockFederation({
instance: federationInstance,
initOptions: {
name: 'test-app',
shared: false,
},
}),
});

initContainerEntry(
createMockOptions({
webpackRequire,
shareScopeKey: ['default', 'custom'],
shareScope: defaultScope,
remoteEntryInitOptions: createMockRemoteEntryInitOptions({
shareScopeKeys: ['default', 'custom'],
shareScopeMap: {
default: defaultScope,
custom: customScope,
},
}),
}),
);

expect(federationInstance.shareScopeMap.custom).toBe(customScope);

const emptyCustomScopeInitOptions = createMockRemoteEntryInitOptions({
shareScopeKeys: ['default', 'custom'],
shareScopeMap: {
default: defaultScope,
custom: {},
},
});

initContainerEntry(
createMockOptions({
webpackRequire,
shareScopeKey: ['default', 'custom'],
shareScope: defaultScope,
remoteEntryInitOptions: emptyCustomScopeInitOptions,
}),
);

expect(federationInstance.shareScopeMap.custom).toBe(customScope);

const missingCustomScopeInitOptions = createMockRemoteEntryInitOptions({
shareScopeKeys: ['default', 'custom'],
shareScopeMap: {
default: defaultScope,
},
});

initContainerEntry(
createMockOptions({
webpackRequire,
shareScopeKey: ['default', 'custom'],
shareScope: defaultScope,
remoteEntryInitOptions: missingCustomScopeInitOptions,
}),
);

expect(federationInstance.shareScopeMap.custom).toBe(customScope);
});
});
84 changes: 56 additions & 28 deletions packages/webpack-bundler-runtime/src/initContainerEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,43 @@ export function initContainerEntry(
});

const hostShareScopeKeys = remoteEntryInitOptions?.shareScopeKeys;
const hostShareScopeMap = remoteEntryInitOptions?.shareScopeMap;
const hostShareScopeMap = remoteEntryInitOptions?.shareScopeMap || {};
const existingShareScopeMap = federationInstance.shareScopeMap || {};

const hasOwnScope = (scopeMap: Record<string, any>, key: string) =>
Object.prototype.hasOwnProperty.call(scopeMap, key);
const isEmptyShareScope = (scope: Record<string, any> | undefined) =>
!scope || !Object.keys(scope).length;
const resolveShareScope = (
key: string,
fallbackShareScope: Record<string, any>,
options: { fallbackWhenEmpty?: boolean } = {},
) => {
const currentShareScope = hostShareScopeMap[key];

if (
hasOwnScope(hostShareScopeMap, key) &&
!isEmptyShareScope(currentShareScope)
) {
return currentShareScope;
}

if (
hasOwnScope(existingShareScopeMap, key) &&
(!hasOwnScope(hostShareScopeMap, key) ||
isEmptyShareScope(currentShareScope))
) {
return existingShareScopeMap[key];
}

if (hasOwnScope(hostShareScopeMap, key)) {
return options.fallbackWhenEmpty && isEmptyShareScope(currentShareScope)
? fallbackShareScope
: currentShareScope;
}

return fallbackShareScope;
};

// host: 'default' remote: 'default' remote['default'] = hostShareScopeMap['default']
// host: ['default', 'scope1'] remote: 'default' remote['default'] = hostShareScopeMap['default']; remote['scope1'] = hostShareScopeMap['scop1']
Expand All @@ -36,43 +72,35 @@ export function initContainerEntry(
if (!shareScopeKey || typeof shareScopeKey === 'string') {
const key = shareScopeKey || 'default';
if (Array.isArray(hostShareScopeKeys)) {
// const sc = hostShareScopeMap![key];
// if (!sc) {
// throw new Error('shareScopeKey is not exist in hostShareScopeMap');
// }
// federationInstance.initShareScopeMap(key, sc, {
// hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {},
// });

hostShareScopeKeys.forEach((hostKey) => {
if (!hostShareScopeMap![hostKey]) {
hostShareScopeMap![hostKey] = {};
const sc = resolveShareScope(hostKey, {});
if (
!hasOwnScope(hostShareScopeMap, hostKey) ||
(hasOwnScope(existingShareScopeMap, hostKey) &&
isEmptyShareScope(hostShareScopeMap[hostKey]))
) {
hostShareScopeMap[hostKey] = sc;
}
const sc = hostShareScopeMap![hostKey];
federationInstance.initShareScopeMap(hostKey, sc, {
hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {},
hostShareScopeMap,
});
});
} else {
federationInstance.initShareScopeMap(key, shareScope, {
hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {},
const sc = resolveShareScope(key, shareScope, {
fallbackWhenEmpty: true,
});
federationInstance.initShareScopeMap(key, sc, {
hostShareScopeMap,
});
}
} else {
shareScopeKey.forEach((key) => {
if (!hostShareScopeKeys || !hostShareScopeMap) {
federationInstance.initShareScopeMap(key, shareScope, {
hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {},
});
return;
}

if (!hostShareScopeMap[key]) {
hostShareScopeMap[key] = {};
}
const sc = hostShareScopeMap[key];
const sc =
!hostShareScopeKeys || !remoteEntryInitOptions?.shareScopeMap
? resolveShareScope(key, shareScope, { fallbackWhenEmpty: true })
: resolveShareScope(key, {});
federationInstance.initShareScopeMap(key, sc, {
hostShareScopeMap: remoteEntryInitOptions?.shareScopeMap || {},
hostShareScopeMap,
});
});
}
Expand All @@ -89,7 +117,7 @@ export function initContainerEntry(
return webpackRequire.I(shareScopeKey || 'default', initScope);
}

var proxyInitializeSharing = Boolean(
const proxyInitializeSharing = Boolean(
webpackRequire.federation.initOptions.shared,
);

Expand Down
Loading