diff --git a/e2e/react/src/react-module-federation.rspack.test.ts b/e2e/react/src/react-module-federation.rspack.test.ts index 4db6867ab0548..32a0ceb56488e 100644 --- a/e2e/react/src/react-module-federation.rspack.test.ts +++ b/e2e/react/src/react-module-federation.rspack.test.ts @@ -25,7 +25,7 @@ describe('React Rspack Module Federation', () => { newProject({ packages: ['@nx/react'] }); }); - // afterAll(() => cleanupProject()); + afterAll(() => cleanupProject()); it.each` js @@ -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'); diff --git a/packages/react/src/module-federation/utils.ts b/packages/react/src/module-federation/utils.ts index 68c0b31b24cfb..1ce5818025de5 100644 --- a/packages/react/src/module-federation/utils.ts +++ b/packages/react/src/module-federation/utils.ts @@ -127,7 +127,7 @@ export async function getModuleFederationConfig( mfConfig.remotes, 'js', determineRemoteUrlFunction, - isLibraryTypeVar + true ); } diff --git a/packages/react/src/module-federation/with-module-federation.ts b/packages/react/src/module-federation/with-module-federation.ts index 4047ba8c92f8c..6ca1a841009b1 100644 --- a/packages/react/src/module-federation/with-module-federation.ts +++ b/packages/react/src/module-federation/with-module-federation.ts @@ -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} @@ -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, @@ -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, @@ -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 */