Skip to content

Commit

Permalink
fix(react): ensure interop between webpack and rspack module federation
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 committed Sep 9, 2024
1 parent 4766031 commit 86a5f09
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 17 deletions.
129 changes: 128 additions & 1 deletion e2e/react/src/react-module-federation.rspack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('React Rspack Module Federation', () => {
newProject({ packages: ['@nx/react'] });
});

// afterAll(() => cleanupProject());
afterAll(() => cleanupProject());

it.each`
js
Expand Down Expand Up @@ -126,6 +126,133 @@ describe('React Rspack Module Federation', () => {
500_000
);

it('should have interop between webpack host and rspack remote', async () => {
const shell = uniq('shell');
const remote1 = uniq('remote1');
const remote2 = uniq('remote2');

runCLI(
`generate @nx/react:host ${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
);

runCLI(
`generate @nx/react:remote ${remote2} --host=${shell} --bundler=rspack --style=css --no-interactive --skipFormat`
);

updateFile(
`apps/${shell}-e2e/src/integration/app.spec.ts`,
stripIndents`
import { getGreeting } from '../support/app.po';
describe('shell app', () => {
it('should display welcome message', () => {
cy.visit('/')
getGreeting().contains('Welcome ${shell}');
});
it('should load remote 1', () => {
cy.visit('/${remote1}')
getGreeting().contains('Welcome ${remote1}');
});
it('should load remote 2', () => {
cy.visit('/${remote2}')
getGreeting().contains('Welcome ${remote2}');
});
});
`
);

[shell, remote1, remote2].forEach((app) => {
['development', 'production'].forEach(async (configuration) => {
const cliOutput = runCLI(`run ${app}:build:${configuration}`);
expect(cliOutput).toContain('Successfully ran target');
});
});

const serveResult = await runCommandUntil(`serve ${shell}`, (output) =>
output.includes(`http://localhost:${readPort(shell)}`)
);

await killProcessAndPorts(serveResult.pid, readPort(shell));

if (runE2ETests()) {
const e2eResultsSwc = await runCommandUntil(
`e2e ${shell}-e2e --no-watch --verbose`,
(output) => output.includes('All specs passed!')
);

await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));

const e2eResultsTsNode = await runCommandUntil(
`e2e ${shell}-e2e --no-watch --verbose`,
(output) =>
output.includes('Successfully ran target e2e for project'),
{
env: { NX_PREFER_TS_NODE: 'true' },
}
);
await killProcessAndPorts(e2eResultsTsNode.pid, readPort(shell));
}
}, 500_000);

it('should have interop between rspack host and webpack remote', async () => {
const shell = uniq('shell');
const remote1 = uniq('remote1');
const remote2 = uniq('remote2');
runCLI(
`generate @nx/react:host ${shell} --remotes=${remote1} --bundler=rspack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
);

runCLI(
`generate @nx/react:remote ${remote2} --host=${shell} --bundler=webpack --style=css --no-interactive --skipFormat`
);

updateFile(
`apps/${shell}-e2e/src/integration/app.spec.ts`,
stripIndents`
import { getGreeting } from '../support/app.po';
describe('shell app', () => {
it('should display welcome message', () => {
cy.visit('/')
getGreeting().contains('Welcome ${shell}');
});
it('should load remote 1', () => {
cy.visit('/${remote1}')
getGreeting().contains('Welcome ${remote1}');
});
it('should load remote 2', () => {
cy.visit('/${remote2}')
getGreeting().contains('Welcome ${remote2}');
});
});
`
);

if (runE2ETests()) {
const e2eResultsSwc = await runCommandUntil(
`e2e ${shell}-e2e --no-watch --verbose`,
(output) => output.includes('All specs passed!')
);

await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));

const e2eResultsTsNode = await runCommandUntil(
`e2e ${shell}-e2e --no-watch --verbose`,
(output) =>
output.includes('Successfully ran target e2e for project'),
{
env: { NX_PREFER_TS_NODE: 'true' },
}
);
await killProcessAndPorts(e2eResultsTsNode.pid, readPort(shell));
}
}, 500_000);

describe('ssr', () => {
it('should generate host and remote apps with ssr', async () => {
const shell = uniq('shell');
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/module-federation/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export async function getModuleFederationConfig(
mfConfig.remotes,
'js',
determineRemoteUrlFunction,
isLibraryTypeVar
true
);
}

Expand Down
17 changes: 2 additions & 15 deletions packages/react/src/module-federation/with-module-federation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import { getModuleFederationConfig } from './utils';
import type { AsyncNxComposableWebpackPlugin } from '@nx/webpack';
import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';

const isVarOrWindow = (libType?: string) =>
libType === 'var' || libType === 'window';

/**
* @param {ModuleFederationConfig} options
* @return {Promise<AsyncNxComposableWebpackPlugin>}
Expand All @@ -23,16 +20,12 @@ export async function withModuleFederation(

const { sharedDependencies, sharedLibraries, mappedRemotes } =
await getModuleFederationConfig(options);
const isGlobal = isVarOrWindow(options.library?.type);

return (config, ctx) => {
config.output.uniqueName = options.name;
config.output.publicPath = 'auto';

if (isGlobal) {
config.output.scriptType = 'text/javascript';
}

config.output.scriptType = 'text/javascript';
config.optimization = {
...(config.optimization ?? {}),
runtimeChunk: false,
Expand All @@ -46,15 +39,9 @@ export async function withModuleFederation(
config.optimization.runtimeChunk = 'single';
}

config.experiments = {
...config.experiments,
outputModule: !isGlobal,
};

config.plugins.push(
new ModuleFederationPlugin({
name: options.name,
library: options.library ?? { type: 'module' },
filename: 'remoteEntry.js',
exposes: options.exposes,
remotes: mappedRemotes,
Expand All @@ -67,7 +54,7 @@ export async function withModuleFederation(
* { appX: 'appX@http://localhost:3001/remoteEntry.js' }
* { appY: 'appY@http://localhost:3002/remoteEntry.js' }
*/
...(isGlobal ? { remoteType: 'script' } : {}),
remoteType: 'script',
/**
* Apply user-defined config overrides
*/
Expand Down

0 comments on commit 86a5f09

Please sign in to comment.