feat(react): add crystal mf support to host and remote (#30424)
## Current Behavior The `@nx/react` `host` and `remote` generators currently use executors to support Module Federation ## Expected Behavior When `bundler=rspack` use Crystal Module Federation with no executors for Module Federation ## Related Issues #30391
This commit is contained in:
parent
176e8f985a
commit
9669dfdb62
@ -15,11 +15,11 @@ import { readPort, runCLI } from './utils';
|
|||||||
|
|
||||||
describe('React Rspack Module Federation', () => {
|
describe('React Rspack Module Federation', () => {
|
||||||
describe('Default Configuration', () => {
|
describe('Default Configuration', () => {
|
||||||
beforeAll(() => {
|
beforeEach(() => {
|
||||||
newProject({ packages: ['@nx/react'] });
|
newProject({ packages: ['@nx/react'] });
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => cleanupProject());
|
afterEach(() => cleanupProject());
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
js
|
js
|
||||||
@ -100,21 +100,11 @@ describe('React Rspack Module Federation', () => {
|
|||||||
|
|
||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
const e2eResultsSwc = await runCommandUntil(
|
const e2eResultsSwc = await runCommandUntil(
|
||||||
`e2e ${shell}-e2e --no-watch --verbose`,
|
`e2e ${shell}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
|
|
||||||
await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));
|
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
|
500_000
|
||||||
@ -173,291 +163,9 @@ describe('React Rspack Module Federation', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));
|
await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));
|
||||||
|
|
||||||
const e2eResultsTsNode = await runCommandUntil(
|
|
||||||
`e2e ${shell}-e2e`,
|
|
||||||
(output) =>
|
|
||||||
output.includes('Successfully ran target e2e for project'),
|
|
||||||
{
|
|
||||||
env: { NX_PREFER_TS_NODE: 'true' },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await killProcessAndPorts(e2eResultsTsNode.pid, readPort(shell));
|
|
||||||
}
|
}
|
||||||
}, 500_000);
|
}, 500_000);
|
||||||
|
|
||||||
it('should generate host and remote apps in webpack, convert to rspack and use playwright for e2es', async () => {
|
|
||||||
const shell = uniq('shell');
|
|
||||||
const remote1 = uniq('remote1');
|
|
||||||
|
|
||||||
runCLI(
|
|
||||||
`generate @nx/react:host ${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=playwright --style=css --no-interactive --skipFormat`
|
|
||||||
);
|
|
||||||
|
|
||||||
runCLI(
|
|
||||||
`generate @nx/rspack:convert-webpack ${shell} --skipFormat --no-interactive`
|
|
||||||
);
|
|
||||||
runCLI(
|
|
||||||
`generate @nx/rspack:convert-webpack ${remote1} --skipFormat --no-interactive`
|
|
||||||
);
|
|
||||||
|
|
||||||
updateFile(
|
|
||||||
`apps/${shell}-e2e/src/example.spec.ts`,
|
|
||||||
stripIndents`
|
|
||||||
import { test, expect } from '@playwright/test';
|
|
||||||
test('should display welcome message', async ({page}) => {
|
|
||||||
await page.goto("/");
|
|
||||||
expect(await page.locator('h1').innerText()).toContain('Welcome');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should load remote 1', async ({page}) => {
|
|
||||||
await page.goto("/${remote1}");
|
|
||||||
expect(await page.locator('h1').innerText()).toContain('${remote1}');
|
|
||||||
});
|
|
||||||
`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (runE2ETests()) {
|
|
||||||
const e2eResultsSwc = await runCommandUntil(
|
|
||||||
`e2e ${shell}-e2e`,
|
|
||||||
(output) => output.includes('Successfully ran target e2e for project')
|
|
||||||
);
|
|
||||||
|
|
||||||
await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));
|
|
||||||
|
|
||||||
const e2eResultsTsNode = await runCommandUntil(
|
|
||||||
`e2e ${shell}-e2e`,
|
|
||||||
(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 webpack host and rspack remote', async () => {
|
|
||||||
const shell = uniq('shell');
|
|
||||||
const remote1 = uniq('remote1');
|
|
||||||
const remote2 = uniq('remote2');
|
|
||||||
|
|
||||||
runCLI(
|
|
||||||
`generate @nx/react:host apps/${shell} --name=${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
|
|
||||||
);
|
|
||||||
|
|
||||||
runCLI(
|
|
||||||
`generate @nx/react:remote apps/${remote2} --name=${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 apps/${shell} --name=${shell} --remotes=${remote1} --bundler=rspack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
|
|
||||||
);
|
|
||||||
|
|
||||||
runCLI(
|
|
||||||
`generate @nx/react:remote apps/${remote2} --name=${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');
|
|
||||||
const remote1 = uniq('remote1');
|
|
||||||
const remote2 = uniq('remote2');
|
|
||||||
const remote3 = uniq('remote3');
|
|
||||||
|
|
||||||
await runCLIAsync(
|
|
||||||
`generate @nx/react:host apps/${shell} --ssr --name=${shell} --remotes=${remote1},${remote2},${remote3} --bundler=rspack --style=css --no-interactive --skipFormat`
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(readPort(shell)).toEqual(4200);
|
|
||||||
expect(readPort(remote1)).toEqual(4201);
|
|
||||||
expect(readPort(remote2)).toEqual(4202);
|
|
||||||
expect(readPort(remote3)).toEqual(4203);
|
|
||||||
|
|
||||||
[shell, remote1, remote2, remote3].forEach((app) => {
|
|
||||||
checkFilesExist(
|
|
||||||
`apps/${app}/module-federation.config.ts`,
|
|
||||||
`apps/${app}/module-federation.server.config.ts`
|
|
||||||
);
|
|
||||||
['build', 'server'].forEach((target) => {
|
|
||||||
['development', 'production'].forEach(async (configuration) => {
|
|
||||||
const cliOutput = runCLI(`run ${app}:${target}:${configuration}`);
|
|
||||||
expect(cliOutput).toContain('Successfully ran target');
|
|
||||||
|
|
||||||
await killPorts(readPort(app));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, 500_000);
|
|
||||||
|
|
||||||
it('should serve remotes as static when running the host by default', async () => {
|
|
||||||
const shell = uniq('shell');
|
|
||||||
const remote1 = uniq('remote1');
|
|
||||||
const remote2 = uniq('remote2');
|
|
||||||
const remote3 = uniq('remote3');
|
|
||||||
|
|
||||||
await runCLIAsync(
|
|
||||||
`generate @nx/react:host apps/${shell} --ssr --name=${shell} --remotes=${remote1},${remote2},${remote3} --bundler=rspack --style=css --e2eTestRunner=cypress --no-interactive --skipFormat`
|
|
||||||
);
|
|
||||||
|
|
||||||
const serveResult = await runCommandUntil(`serve ${shell}`, (output) =>
|
|
||||||
output.includes(`Nx SSR Static remotes proxies started successfully`)
|
|
||||||
);
|
|
||||||
|
|
||||||
await killProcessAndPorts(serveResult.pid);
|
|
||||||
}, 500_000);
|
|
||||||
|
|
||||||
it('should serve remotes as static and they should be able to be accessed from the host', async () => {
|
|
||||||
const shell = uniq('shell');
|
|
||||||
const remote1 = uniq('remote1');
|
|
||||||
const remote2 = uniq('remote2');
|
|
||||||
const remote3 = uniq('remote3');
|
|
||||||
|
|
||||||
await runCLIAsync(
|
|
||||||
`generate @nx/react:host apps/${shell} --ssr --name=${shell} --remotes=${remote1},${remote2},${remote3} --bundler=rspack --style=css --e2eTestRunner=cypress --no-interactive --skipFormat`
|
|
||||||
);
|
|
||||||
|
|
||||||
const capitalize = (s: string) =>
|
|
||||||
s.charAt(0).toUpperCase() + s.slice(1);
|
|
||||||
|
|
||||||
updateFile(`apps/${shell}-e2e/src/e2e/app.cy.ts`, (content) => {
|
|
||||||
return `
|
|
||||||
describe('${shell}-e2e', () => {
|
|
||||||
beforeEach(() => cy.visit('/'));
|
|
||||||
|
|
||||||
it('should display welcome message', () => {
|
|
||||||
expect(cy.get('ul li').should('have.length', 4));
|
|
||||||
expect(cy.get('ul li').eq(0).should('have.text', 'Home'));
|
|
||||||
expect(cy.get('ul li').eq(1).should('have.text', '${capitalize(
|
|
||||||
remote1
|
|
||||||
)}'));
|
|
||||||
expect(cy.get('ul li').eq(2).should('have.text', '${capitalize(
|
|
||||||
remote2
|
|
||||||
)}'));
|
|
||||||
expect(cy.get('ul li').eq(3).should('have.text', '${capitalize(
|
|
||||||
remote3
|
|
||||||
)}'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (runE2ETests()) {
|
|
||||||
const hostE2eResults = await runCommandUntil(
|
|
||||||
`e2e ${shell}-e2e --no-watch --verbose`,
|
|
||||||
(output) => output.includes('All specs passed!')
|
|
||||||
);
|
|
||||||
await killProcessAndPorts(hostE2eResults.pid);
|
|
||||||
}
|
|
||||||
}, 600_000);
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO(Coly010): investigate this failure
|
// TODO(Coly010): investigate this failure
|
||||||
xit('should support generating host and remote apps with the new name and root format', async () => {
|
xit('should support generating host and remote apps with the new name and root format', async () => {
|
||||||
const shell = uniq('shell');
|
const shell = uniq('shell');
|
||||||
|
|||||||
@ -94,13 +94,13 @@ describe('Dynamic Module Federation', () => {
|
|||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
// Serve Remote since it is dynamic and won't be started with the host
|
// Serve Remote since it is dynamic and won't be started with the host
|
||||||
const remoteProcess = await runCommandUntil(
|
const remoteProcess = await runCommandUntil(
|
||||||
`serve-static ${remote} --no-watch --verbose`,
|
`serve ${remote} --verbose`,
|
||||||
() => {
|
() => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const hostE2eResultsSwc = await runCommandUntil(
|
const hostE2eResultsSwc = await runCommandUntil(
|
||||||
`e2e ${shell}-e2e --no-watch --verbose`,
|
`e2e ${shell}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@ describe('Federate Module', () => {
|
|||||||
|
|
||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
const hostE2eResults = await runCommandUntil(
|
const hostE2eResults = await runCommandUntil(
|
||||||
`e2e ${host}-e2e --no-watch --verbose`,
|
`e2e ${host}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
await killProcessAndPorts(
|
await killProcessAndPorts(
|
||||||
@ -195,7 +195,7 @@ describe('Federate Module', () => {
|
|||||||
|
|
||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
const hostE2eResults = await runCommandUntil(
|
const hostE2eResults = await runCommandUntil(
|
||||||
`e2e ${host}-e2e --no-watch --verbose`,
|
`e2e ${host}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
await killProcessAndPorts(
|
await killProcessAndPorts(
|
||||||
|
|||||||
@ -15,13 +15,11 @@ describe('Independent Deployability', () => {
|
|||||||
let proj: string;
|
let proj: string;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.NX_ADD_PLUGINS = 'false';
|
|
||||||
proj = newProject();
|
proj = newProject();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
cleanupProject();
|
cleanupProject();
|
||||||
delete process.env.NX_ADD_PLUGINS;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support promised based remotes', async () => {
|
it('should support promised based remotes', async () => {
|
||||||
@ -47,8 +45,8 @@ describe('Independent Deployability', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`${remote}/webpack.config.prod.js`,
|
`${remote}/rspack.config.prod.js`,
|
||||||
`module.exports = require('./webpack.config');`
|
`module.exports = require('./rspack.config');`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update host to use promise based remote
|
// Update host to use promise based remote
|
||||||
@ -86,27 +84,10 @@ describe('Independent Deployability', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`${host}/webpack.config.prod.js`,
|
`${host}/rspack.config.prod.js`,
|
||||||
`module.exports = require('./webpack.config');`
|
`module.exports = require('./rspack.config');`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update e2e project.json
|
|
||||||
updateJson(`${host}-e2e/project.json`, (json) => {
|
|
||||||
return {
|
|
||||||
...json,
|
|
||||||
targets: {
|
|
||||||
...json.targets,
|
|
||||||
e2e: {
|
|
||||||
...json.targets.e2e,
|
|
||||||
options: {
|
|
||||||
...json.targets.e2e.options,
|
|
||||||
devServerTarget: `${host}:serve-static:production`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// update e2e
|
// update e2e
|
||||||
updateFile(
|
updateFile(
|
||||||
`${host}-e2e/src/e2e/app.cy.ts`,
|
`${host}-e2e/src/e2e/app.cy.ts`,
|
||||||
@ -142,18 +123,16 @@ describe('Independent Deployability', () => {
|
|||||||
expect(remoteOutput).toContain('Successfully ran target build');
|
expect(remoteOutput).toContain('Successfully ran target build');
|
||||||
|
|
||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
const remoteProcess = await runCommandUntil(
|
|
||||||
`serve-static ${remote} --no-watch --verbose`,
|
|
||||||
() => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const hostE2eResults = await runCommandUntil(
|
const hostE2eResults = await runCommandUntil(
|
||||||
`e2e ${host}-e2e --no-watch --verbose`,
|
`e2e ${host}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
await killProcessAndPorts(hostE2eResults.pid, hostPort, hostPort + 1);
|
await killProcessAndPorts(
|
||||||
await killProcessAndPorts(remoteProcess.pid, remotePort);
|
hostE2eResults.pid,
|
||||||
|
hostPort,
|
||||||
|
hostPort + 1,
|
||||||
|
remotePort
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, 500_000);
|
}, 500_000);
|
||||||
|
|
||||||
@ -279,7 +258,7 @@ describe('Independent Deployability', () => {
|
|||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
// test remote e2e
|
// test remote e2e
|
||||||
const remoteE2eResults = await runCommandUntil(
|
const remoteE2eResults = await runCommandUntil(
|
||||||
`e2e ${remote}-e2e --no-watch --verbose`,
|
`e2e ${remote}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
await killProcessAndPorts(remoteE2eResults.pid, remotePort);
|
await killProcessAndPorts(remoteE2eResults.pid, remotePort);
|
||||||
@ -294,7 +273,7 @@ describe('Independent Deployability', () => {
|
|||||||
);
|
);
|
||||||
await killProcessAndPorts(remoteProcess.pid, remotePort);
|
await killProcessAndPorts(remoteProcess.pid, remotePort);
|
||||||
const shellE2eResults = await runCommandUntil(
|
const shellE2eResults = await runCommandUntil(
|
||||||
`e2e ${shell}-e2e --no-watch --verbose`,
|
`e2e ${shell}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) => output.includes('All specs passed!')
|
||||||
);
|
);
|
||||||
await killProcessAndPorts(
|
await killProcessAndPorts(
|
||||||
@ -321,7 +300,7 @@ describe('Independent Deployability', () => {
|
|||||||
updateFile(
|
updateFile(
|
||||||
`${shell}/module-federation.config.ts`,
|
`${shell}/module-federation.config.ts`,
|
||||||
stripIndents`
|
stripIndents`
|
||||||
import { ModuleFederationConfig } from '@nx/webpack';
|
import { ModuleFederationConfig } from '@nx/module-federation';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
name: '${shell}',
|
name: '${shell}',
|
||||||
@ -334,14 +313,14 @@ describe('Independent Deployability', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`${shell}/webpack.config.prod.ts`,
|
`${shell}/rspack.config.prod.ts`,
|
||||||
`export { default } from './webpack.config';`
|
`export { default } from './rspack.config';`
|
||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`${remote}/module-federation.config.ts`,
|
`${remote}/module-federation.config.ts`,
|
||||||
stripIndents`
|
stripIndents`
|
||||||
import { ModuleFederationConfig } from '@nx/webpack';
|
import { ModuleFederationConfig } from '@nx/module-federation';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
name: '${remote}',
|
name: '${remote}',
|
||||||
@ -356,8 +335,8 @@ describe('Independent Deployability', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`${remote}/webpack.config.prod.ts`,
|
`${remote}/rspack.config.prod.ts`,
|
||||||
`export { default } from './webpack.config';`
|
`export { default } from './rspack.config';`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update host e2e test to check that the remote works with library type var via navigation
|
// Update host e2e test to check that the remote works with library type var via navigation
|
||||||
@ -394,8 +373,9 @@ describe('Independent Deployability', () => {
|
|||||||
|
|
||||||
if (runE2ETests()) {
|
if (runE2ETests()) {
|
||||||
const hostE2eResultsSwc = await runCommandUntil(
|
const hostE2eResultsSwc = await runCommandUntil(
|
||||||
`e2e ${shell}-e2e --no-watch --verbose`,
|
`e2e ${shell}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) =>
|
||||||
|
output.includes('NX Successfully ran target e2e for project')
|
||||||
);
|
);
|
||||||
await killProcessAndPorts(
|
await killProcessAndPorts(
|
||||||
hostE2eResultsSwc.pid,
|
hostE2eResultsSwc.pid,
|
||||||
@ -405,32 +385,12 @@ describe('Independent Deployability', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const remoteE2eResultsSwc = await runCommandUntil(
|
const remoteE2eResultsSwc = await runCommandUntil(
|
||||||
`e2e ${remote}-e2e --no-watch --verbose`,
|
`e2e ${remote}-e2e --verbose`,
|
||||||
(output) => output.includes('All specs passed!')
|
(output) =>
|
||||||
|
output.includes('NX Successfully ran target e2e for project')
|
||||||
);
|
);
|
||||||
|
|
||||||
await killProcessAndPorts(remoteE2eResultsSwc.pid, remotePort);
|
await killProcessAndPorts(remoteE2eResultsSwc.pid, remotePort);
|
||||||
|
|
||||||
const hostE2eResultsTsNode = await runCommandUntil(
|
|
||||||
`e2e ${shell}-e2e --no-watch --verbose`,
|
|
||||||
(output) => output.includes('All specs passed!'),
|
|
||||||
{ env: { NX_PREFER_TS_NODE: 'true' } }
|
|
||||||
);
|
|
||||||
|
|
||||||
await killProcessAndPorts(
|
|
||||||
hostE2eResultsTsNode.pid,
|
|
||||||
shellPort,
|
|
||||||
shellPort + 1,
|
|
||||||
remotePort
|
|
||||||
);
|
|
||||||
|
|
||||||
const remoteE2eResultsTsNode = await runCommandUntil(
|
|
||||||
`e2e ${remote}-e2e --no-watch --verbose`,
|
|
||||||
(output) => output.includes('All specs passed!'),
|
|
||||||
{ env: { NX_PREFER_TS_NODE: 'true' } }
|
|
||||||
);
|
|
||||||
|
|
||||||
await killProcessAndPorts(remoteE2eResultsTsNode.pid, remotePort);
|
|
||||||
}
|
}
|
||||||
}, 500_000);
|
}, 500_000);
|
||||||
});
|
});
|
||||||
|
|||||||
183
e2e/react/src/module-federation/misc.rspack.test.ts
Normal file
183
e2e/react/src/module-federation/misc.rspack.test.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import {
|
||||||
|
cleanupProject,
|
||||||
|
killProcessAndPorts,
|
||||||
|
newProject,
|
||||||
|
runCommandUntil,
|
||||||
|
runE2ETests,
|
||||||
|
uniq,
|
||||||
|
updateFile,
|
||||||
|
} from '@nx/e2e/utils';
|
||||||
|
import { readPort, runCLI } from './utils';
|
||||||
|
import { stripIndents } from 'nx/src/utils/strip-indents';
|
||||||
|
|
||||||
|
describe('React Rspack Module Federation Misc', () => {
|
||||||
|
describe('Convert To Rspack', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
process.env.NX_ADD_PLUGINS = 'false';
|
||||||
|
newProject({ packages: ['@nx/react', '@nx/rspack'] });
|
||||||
|
});
|
||||||
|
afterAll(() => {
|
||||||
|
cleanupProject();
|
||||||
|
delete process.env.NX_ADD_PLUGINS;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host and remote apps in webpack, convert to rspack and use playwright for e2es', async () => {
|
||||||
|
const shell = uniq('shell');
|
||||||
|
const remote1 = uniq('remote1');
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:host ${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=playwright --style=css --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/rspack:convert-webpack ${shell} --skipFormat --no-interactive`
|
||||||
|
);
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/rspack:convert-webpack ${remote1} --skipFormat --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
updateFile(
|
||||||
|
`apps/${shell}-e2e/src/example.spec.ts`,
|
||||||
|
stripIndents`
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('should display welcome message', async ({page}) => {
|
||||||
|
await page.goto("/");
|
||||||
|
expect(await page.locator('h1').innerText()).toContain('Welcome');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should load remote 1', async ({page}) => {
|
||||||
|
await page.goto("/${remote1}");
|
||||||
|
expect(await page.locator('h1').innerText()).toContain('${remote1}');
|
||||||
|
});
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (runE2ETests()) {
|
||||||
|
const e2eResultsSwc = await runCommandUntil(
|
||||||
|
`e2e ${shell}-e2e`,
|
||||||
|
(output) => output.includes('Successfully ran target e2e for project')
|
||||||
|
);
|
||||||
|
|
||||||
|
await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));
|
||||||
|
}
|
||||||
|
}, 500_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Interoperability', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env.NX_ADD_PLUGINS = 'false';
|
||||||
|
newProject({ packages: ['@nx/react'] });
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupProject();
|
||||||
|
delete process.env.NX_ADD_PLUGINS;
|
||||||
|
});
|
||||||
|
|
||||||
|
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 apps/${shell} --name=${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:remote apps/${remote2} --name=${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 --verbose`,
|
||||||
|
(output) => output.includes('All specs passed!')
|
||||||
|
);
|
||||||
|
|
||||||
|
await killProcessAndPorts(e2eResultsSwc.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 apps/${shell} --name=${shell} --remotes=${remote1} --bundler=rspack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nx/react:remote apps/${remote2} --name=${remote2} --host=${shell} --bundler=webpack --style=css --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
updateFile(
|
||||||
|
`apps/${shell}-e2e/src/integration/app.cy.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 --verbose`,
|
||||||
|
(output) => output.includes('Successfully ran target e2e')
|
||||||
|
);
|
||||||
|
|
||||||
|
await killProcessAndPorts(e2eResultsSwc.pid, readPort(shell));
|
||||||
|
}
|
||||||
|
}, 500_000);
|
||||||
|
});
|
||||||
|
});
|
||||||
110
e2e/react/src/module-federation/ssr.rspack.test.ts
Normal file
110
e2e/react/src/module-federation/ssr.rspack.test.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import {
|
||||||
|
checkFilesExist,
|
||||||
|
cleanupProject,
|
||||||
|
killPorts,
|
||||||
|
killProcessAndPorts,
|
||||||
|
newProject,
|
||||||
|
runCLIAsync,
|
||||||
|
runCommandUntil,
|
||||||
|
runE2ETests,
|
||||||
|
uniq,
|
||||||
|
updateFile,
|
||||||
|
} from '@nx/e2e/utils';
|
||||||
|
import { readPort, runCLI } from './utils';
|
||||||
|
|
||||||
|
describe('React Rspack SSR Module Federation', () => {
|
||||||
|
describe('ssr', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
newProject({ packages: ['@nx/react'] });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should generate host and remote apps with ssr', async () => {
|
||||||
|
const shell = uniq('shell');
|
||||||
|
const remote1 = uniq('remote1');
|
||||||
|
const remote2 = uniq('remote2');
|
||||||
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
|
await runCLIAsync(
|
||||||
|
`generate @nx/react:host apps/${shell} --ssr --name=${shell} --remotes=${remote1},${remote2},${remote3} --bundler=rspack --style=css --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(readPort(shell)).toEqual(4000);
|
||||||
|
expect(readPort(remote1)).toEqual(4201);
|
||||||
|
expect(readPort(remote2)).toEqual(4202);
|
||||||
|
expect(readPort(remote3)).toEqual(4203);
|
||||||
|
|
||||||
|
for (const app of [shell, remote1, remote2, remote3]) {
|
||||||
|
checkFilesExist(
|
||||||
|
`apps/${app}/module-federation.config.ts`,
|
||||||
|
`apps/${app}/module-federation.server.config.ts`
|
||||||
|
);
|
||||||
|
const cliOutput = runCLI(`run ${app}:build`);
|
||||||
|
expect(cliOutput).toContain('Successfully ran target');
|
||||||
|
|
||||||
|
await killPorts(readPort(app));
|
||||||
|
}
|
||||||
|
}, 500_000);
|
||||||
|
|
||||||
|
it('should serve remotes as static when running the host by default', async () => {
|
||||||
|
const shell = uniq('shell');
|
||||||
|
const remote1 = uniq('remote1');
|
||||||
|
const remote2 = uniq('remote2');
|
||||||
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
|
await runCLIAsync(
|
||||||
|
`generate @nx/react:host apps/${shell} --ssr --name=${shell} --remotes=${remote1},${remote2},${remote3} --bundler=rspack --style=css --e2eTestRunner=cypress --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
const serveResult = await runCommandUntil(`serve ${shell}`, (output) =>
|
||||||
|
output.includes(`NX Static remotes proxies started successfully`)
|
||||||
|
);
|
||||||
|
|
||||||
|
await killProcessAndPorts(serveResult.pid);
|
||||||
|
}, 500_000);
|
||||||
|
|
||||||
|
it('should serve remotes as static and they should be able to be accessed from the host', async () => {
|
||||||
|
const shell = uniq('shell');
|
||||||
|
const remote1 = uniq('remote1');
|
||||||
|
const remote2 = uniq('remote2');
|
||||||
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
|
await runCLIAsync(
|
||||||
|
`generate @nx/react:host apps/${shell} --ssr --name=${shell} --remotes=${remote1},${remote2},${remote3} --bundler=rspack --style=css --e2eTestRunner=cypress --no-interactive --skipFormat`
|
||||||
|
);
|
||||||
|
|
||||||
|
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
|
||||||
|
|
||||||
|
updateFile(`apps/${shell}-e2e/src/e2e/app.cy.ts`, (content) => {
|
||||||
|
return `
|
||||||
|
describe('${shell}-e2e', () => {
|
||||||
|
beforeEach(() => cy.visit('/'));
|
||||||
|
|
||||||
|
it('should display welcome message', () => {
|
||||||
|
expect(cy.get('ul li').should('have.length', 4));
|
||||||
|
expect(cy.get('ul li').eq(0).should('have.text', 'Home'));
|
||||||
|
expect(cy.get('ul li').eq(1).should('have.text', '${capitalize(
|
||||||
|
remote1
|
||||||
|
)}'));
|
||||||
|
expect(cy.get('ul li').eq(2).should('have.text', '${capitalize(
|
||||||
|
remote2
|
||||||
|
)}'));
|
||||||
|
expect(cy.get('ul li').eq(3).should('have.text', '${capitalize(
|
||||||
|
remote3
|
||||||
|
)}'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (runE2ETests()) {
|
||||||
|
const hostE2eResults = await runCommandUntil(
|
||||||
|
`e2e ${shell}-e2e --verbose`,
|
||||||
|
(output) => output.includes('All specs passed!')
|
||||||
|
);
|
||||||
|
await killProcessAndPorts(hostE2eResults.pid);
|
||||||
|
}
|
||||||
|
}, 600_000);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -24,12 +24,10 @@ import {
|
|||||||
startStaticRemotesFileServer,
|
startStaticRemotesFileServer,
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { NxModuleFederationDevServerConfig } from '../../models';
|
import { NxModuleFederationDevServerConfig } from '../../models';
|
||||||
import { ChildProcess, fork } from 'node:child_process';
|
|
||||||
|
|
||||||
const PLUGIN_NAME = 'NxModuleFederationDevServerPlugin';
|
const PLUGIN_NAME = 'NxModuleFederationDevServerPlugin';
|
||||||
|
|
||||||
export class NxModuleFederationDevServerPlugin implements RspackPluginInstance {
|
export class NxModuleFederationDevServerPlugin implements RspackPluginInstance {
|
||||||
private devServerProcess: ChildProcess | undefined;
|
|
||||||
private nxBin = require.resolve('nx/bin/nx');
|
private nxBin = require.resolve('nx/bin/nx');
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -44,42 +42,52 @@ export class NxModuleFederationDevServerPlugin implements RspackPluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply(compiler: Compiler) {
|
apply(compiler: Compiler) {
|
||||||
compiler.hooks.beforeCompile.tapAsync(
|
const isDevServer = process.env['WEBPACK_SERVE'];
|
||||||
|
if (!isDevServer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
compiler.hooks.watchRun.tapAsync(
|
||||||
PLUGIN_NAME,
|
PLUGIN_NAME,
|
||||||
async (params, callback) => {
|
async (compiler, callback) => {
|
||||||
const staticRemotesConfig = await this.setup(compiler);
|
compiler.hooks.beforeCompile.tapAsync(
|
||||||
|
PLUGIN_NAME,
|
||||||
|
async (params, callback) => {
|
||||||
|
const staticRemotesConfig = await this.setup();
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`NX Starting module federation dev-server for ${pc.bold(
|
`NX Starting module federation dev-server for ${pc.bold(
|
||||||
this._options.config.name
|
this._options.config.name
|
||||||
)} with ${Object.keys(staticRemotesConfig).length} remotes`
|
)} with ${Object.keys(staticRemotesConfig).length} remotes`
|
||||||
|
);
|
||||||
|
|
||||||
|
const mappedLocationOfRemotes = await buildStaticRemotes(
|
||||||
|
staticRemotesConfig,
|
||||||
|
this._options.devServerConfig,
|
||||||
|
this.nxBin
|
||||||
|
);
|
||||||
|
startStaticRemotesFileServer(
|
||||||
|
staticRemotesConfig,
|
||||||
|
workspaceRoot,
|
||||||
|
this._options.devServerConfig.staticRemotesPort
|
||||||
|
);
|
||||||
|
startRemoteProxies(staticRemotesConfig, mappedLocationOfRemotes, {
|
||||||
|
pathToCert: this._options.devServerConfig.sslCert,
|
||||||
|
pathToKey: this._options.devServerConfig.sslCert,
|
||||||
|
});
|
||||||
|
|
||||||
|
new DefinePlugin({
|
||||||
|
'process.env.NX_MF_DEV_REMOTES': process.env.NX_MF_DEV_REMOTES,
|
||||||
|
}).apply(compiler);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const mappedLocationOfRemotes = await buildStaticRemotes(
|
|
||||||
staticRemotesConfig,
|
|
||||||
this._options.devServerConfig,
|
|
||||||
this.nxBin
|
|
||||||
);
|
|
||||||
startStaticRemotesFileServer(
|
|
||||||
staticRemotesConfig,
|
|
||||||
workspaceRoot,
|
|
||||||
this._options.devServerConfig.staticRemotesPort
|
|
||||||
);
|
|
||||||
startRemoteProxies(staticRemotesConfig, mappedLocationOfRemotes, {
|
|
||||||
pathToCert: this._options.devServerConfig.sslCert,
|
|
||||||
pathToKey: this._options.devServerConfig.sslCert,
|
|
||||||
});
|
|
||||||
|
|
||||||
new DefinePlugin({
|
|
||||||
'process.env.NX_MF_DEV_REMOTES': process.env.NX_MF_DEV_REMOTES,
|
|
||||||
}).apply(compiler);
|
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setup(compiler: Compiler) {
|
private async setup() {
|
||||||
const projectGraph = readCachedProjectGraph();
|
const projectGraph = readCachedProjectGraph();
|
||||||
const { projects: workspaceProjects } =
|
const { projects: workspaceProjects } =
|
||||||
readProjectsConfigurationFromProjectGraph(projectGraph);
|
readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||||
|
|||||||
@ -4,14 +4,11 @@ import {
|
|||||||
NxModuleFederationConfigOverride,
|
NxModuleFederationConfigOverride,
|
||||||
} from '../../../utils/models';
|
} from '../../../utils/models';
|
||||||
import { getModuleFederationConfig } from '../../../with-module-federation/rspack/utils';
|
import { getModuleFederationConfig } from '../../../with-module-federation/rspack/utils';
|
||||||
import { NxModuleFederationDevServerConfig } from '../../models';
|
|
||||||
import { NxModuleFederationDevServerPlugin } from './nx-module-federation-dev-server-plugin';
|
|
||||||
|
|
||||||
export class NxModuleFederationPlugin implements RspackPluginInstance {
|
export class NxModuleFederationPlugin implements RspackPluginInstance {
|
||||||
constructor(
|
constructor(
|
||||||
private _options: {
|
private _options: {
|
||||||
config: ModuleFederationConfig;
|
config: ModuleFederationConfig;
|
||||||
devServerConfig?: NxModuleFederationDevServerConfig;
|
|
||||||
isServer?: boolean;
|
isServer?: boolean;
|
||||||
},
|
},
|
||||||
private configOverride?: NxModuleFederationConfigOverride
|
private configOverride?: NxModuleFederationConfigOverride
|
||||||
@ -23,6 +20,7 @@ export class NxModuleFederationPlugin implements RspackPluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is required to ensure Module Federation will build the project correctly
|
// This is required to ensure Module Federation will build the project correctly
|
||||||
|
compiler.options.optimization ??= {};
|
||||||
compiler.options.optimization.runtimeChunk = false;
|
compiler.options.optimization.runtimeChunk = false;
|
||||||
compiler.options.output.uniqueName = this._options.config.name;
|
compiler.options.output.uniqueName = this._options.config.name;
|
||||||
if (this._options.isServer) {
|
if (this._options.isServer) {
|
||||||
|
|||||||
@ -46,6 +46,10 @@ export class NxModuleFederationSSRDevServerPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply(compiler: Compiler) {
|
apply(compiler: Compiler) {
|
||||||
|
const isDevServer = process.env['WEBPACK_SERVE'];
|
||||||
|
if (!isDevServer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
compiler.hooks.watchRun.tapAsync(
|
compiler.hooks.watchRun.tapAsync(
|
||||||
PLUGIN_NAME,
|
PLUGIN_NAME,
|
||||||
async (compiler, callback) => {
|
async (compiler, callback) => {
|
||||||
@ -95,7 +99,7 @@ export class NxModuleFederationSSRDevServerPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async startServer(compiler: Compiler) {
|
private async startServer(compiler: Compiler) {
|
||||||
compiler.hooks.afterEmit.tapAsync(PLUGIN_NAME, async (_, callback) => {
|
compiler.hooks.done.tapAsync(PLUGIN_NAME, async (_, callback) => {
|
||||||
const serverPath = join(
|
const serverPath = join(
|
||||||
compiler.options.output.path,
|
compiler.options.output.path,
|
||||||
(compiler.options.output.filename as string) ?? 'server.js'
|
(compiler.options.output.filename as string) ?? 'server.js'
|
||||||
@ -105,14 +109,14 @@ export class NxModuleFederationSSRDevServerPlugin
|
|||||||
this.devServerProcess.on('exit', () => {
|
this.devServerProcess.on('exit', () => {
|
||||||
res();
|
res();
|
||||||
});
|
});
|
||||||
this.devServerProcess.kill();
|
this.devServerProcess.kill('SIGKILL');
|
||||||
this.devServerProcess = undefined;
|
this.devServerProcess = undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existsSync(serverPath)) {
|
if (!existsSync(serverPath)) {
|
||||||
for (let retries = 0; retries < 10; retries++) {
|
for (let retries = 0; retries < 10; retries++) {
|
||||||
await new Promise<void>((res) => setTimeout(res, 100));
|
await new Promise<void>((res) => setTimeout(res, 200));
|
||||||
if (existsSync(serverPath)) {
|
if (existsSync(serverPath)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -124,10 +128,10 @@ export class NxModuleFederationSSRDevServerPlugin
|
|||||||
|
|
||||||
this.devServerProcess = fork(serverPath);
|
this.devServerProcess = fork(serverPath);
|
||||||
process.on('exit', () => {
|
process.on('exit', () => {
|
||||||
this.devServerProcess?.kill();
|
this.devServerProcess?.kill('SIGKILL');
|
||||||
});
|
});
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
this.devServerProcess?.kill();
|
this.devServerProcess?.kill('SIGKILL');
|
||||||
});
|
});
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export function parseRemotesConfig(
|
|||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (outputPath.startsWith(projectRoot)) {
|
if (!outputPath.startsWith(workspaceRoot)) {
|
||||||
outputPath = joinPathFragments(workspaceRoot, outputPath);
|
outputPath = joinPathFragments(workspaceRoot, outputPath);
|
||||||
}
|
}
|
||||||
const basePath = dirname(outputPath);
|
const basePath = dirname(outputPath);
|
||||||
|
|||||||
@ -59,7 +59,19 @@ function collectRemoteProjects(
|
|||||||
collected.add(remote);
|
collected.add(remote);
|
||||||
|
|
||||||
const remoteProjectRoot = remoteProject.root;
|
const remoteProjectRoot = remoteProject.root;
|
||||||
const remoteProjectTsConfig = remoteProject.targets['build'].options.tsConfig;
|
let remoteProjectTsConfig =
|
||||||
|
remoteProject.targets['build'].options.tsConfig ??
|
||||||
|
[
|
||||||
|
join(remoteProjectRoot, 'tsconfig.app.json'),
|
||||||
|
join(remoteProjectRoot, 'tsconfig.json'),
|
||||||
|
join(context.root, 'tsconfig.json'),
|
||||||
|
join(context.root, 'tsconfig.base.json'),
|
||||||
|
].find((p) => existsSync(p));
|
||||||
|
if (!remoteProjectTsConfig) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find a tsconfig for remote project ${remote}. Please add a tsconfig.app.json or tsconfig.json to the project.`
|
||||||
|
);
|
||||||
|
}
|
||||||
const remoteProjectConfig = getModuleFederationConfig(
|
const remoteProjectConfig = getModuleFederationConfig(
|
||||||
remoteProjectTsConfig,
|
remoteProjectTsConfig,
|
||||||
context.root,
|
context.root,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { ExecutorContext } from '@nx/devkit';
|
import { ExecutorContext, joinPathFragments } from '@nx/devkit';
|
||||||
import { basename, dirname } from 'path';
|
import { basename, dirname } from 'path';
|
||||||
|
import { interpolate } from 'nx/src/tasks-runner/utils';
|
||||||
|
|
||||||
export type StaticRemoteConfig = {
|
export type StaticRemoteConfig = {
|
||||||
basePath: string;
|
basePath: string;
|
||||||
@ -22,8 +23,21 @@ export function parseStaticRemotesConfig(
|
|||||||
|
|
||||||
const config: Record<string, StaticRemoteConfig> = {};
|
const config: Record<string, StaticRemoteConfig> = {};
|
||||||
for (const app of staticRemotes) {
|
for (const app of staticRemotes) {
|
||||||
const outputPath =
|
const projectGraph = context.projectGraph;
|
||||||
context.projectGraph.nodes[app].data.targets['build'].options.outputPath; // dist || dist/checkout
|
const projectRoot = projectGraph.nodes[app].data.root;
|
||||||
|
let outputPath = interpolate(
|
||||||
|
projectGraph.nodes[app].data.targets?.['build']?.options?.outputPath ??
|
||||||
|
projectGraph.nodes[app].data.targets?.['build']?.outputs?.[0] ??
|
||||||
|
`${context.root}/${projectGraph.nodes[app].data.root}/dist`,
|
||||||
|
{
|
||||||
|
projectName: projectGraph.nodes[app].data.name,
|
||||||
|
projectRoot,
|
||||||
|
workspaceRoot: context.root,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (outputPath.startsWith(projectRoot)) {
|
||||||
|
outputPath = joinPathFragments(context.root, outputPath);
|
||||||
|
}
|
||||||
const basePath = ['', '/', '.'].some((p) => dirname(outputPath) === p)
|
const basePath = ['', '/', '.'].some((p) => dirname(outputPath) === p)
|
||||||
? outputPath
|
? outputPath
|
||||||
: dirname(outputPath); // dist || dist/checkout -> dist
|
: dirname(outputPath); // dist || dist/checkout -> dist
|
||||||
@ -45,10 +59,22 @@ export function parseStaticSsrRemotesConfig(
|
|||||||
}
|
}
|
||||||
const config: Record<string, StaticRemoteConfig> = {};
|
const config: Record<string, StaticRemoteConfig> = {};
|
||||||
for (const app of staticRemotes) {
|
for (const app of staticRemotes) {
|
||||||
let outputPath = context.projectGraph.nodes[app].data.targets['build']
|
const projectGraph = context.projectGraph;
|
||||||
.options.outputPath as string;
|
const projectRoot = projectGraph.nodes[app].data.root;
|
||||||
outputPath = dirname(outputPath); // dist/browser => dist || dist/checkout/browser -> checkout
|
let outputPath = interpolate(
|
||||||
|
projectGraph.nodes[app].data.targets?.['build']?.options?.outputPath ??
|
||||||
|
projectGraph.nodes[app].data.targets?.['build']?.outputs?.[0] ??
|
||||||
|
`${context.root}/${projectGraph.nodes[app].data.root}/dist`,
|
||||||
|
{
|
||||||
|
projectName: projectGraph.nodes[app].data.name,
|
||||||
|
projectRoot,
|
||||||
|
workspaceRoot: context.root,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (outputPath.startsWith(projectRoot)) {
|
||||||
|
outputPath = joinPathFragments(context.root, outputPath);
|
||||||
|
}
|
||||||
|
outputPath = dirname(outputPath);
|
||||||
const basePath = ['', '/', '.'].some((p) => dirname(outputPath) === p)
|
const basePath = ['', '/', '.'].some((p) => dirname(outputPath) === p)
|
||||||
? outputPath
|
? outputPath
|
||||||
: dirname(outputPath); // dist || dist/checkout -> dist
|
: dirname(outputPath); // dist || dist/checkout -> dist
|
||||||
|
|||||||
@ -454,7 +454,6 @@ function normalizeOutput(
|
|||||||
relative(workspaceRoot, fullPath)
|
relative(workspaceRoot, fullPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return joinPathFragments('{projectRoot}', pathRelativeToProjectRoot);
|
return joinPathFragments('{projectRoot}', pathRelativeToProjectRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,7 @@
|
|||||||
"@nx/jest",
|
"@nx/jest",
|
||||||
"@nx/rollup",
|
"@nx/rollup",
|
||||||
"@nx/rsbuild",
|
"@nx/rsbuild",
|
||||||
|
"@nx/rspack",
|
||||||
"@nx/storybook",
|
"@nx/storybook",
|
||||||
"@nx/vite",
|
"@nx/vite",
|
||||||
"@nx/webpack",
|
"@nx/webpack",
|
||||||
|
|||||||
@ -58,6 +58,20 @@ export async function addE2e(
|
|||||||
options.addPlugin,
|
options.addPlugin,
|
||||||
options.devServerPort ?? 4200
|
options.devServerPort ?? 4200
|
||||||
);
|
);
|
||||||
|
} else if (options.bundler === 'rspack') {
|
||||||
|
const { getRspackE2EWebServerInfo } = ensurePackage<
|
||||||
|
typeof import('@nx/rspack')
|
||||||
|
>('@nx/rspack', nxVersion);
|
||||||
|
e2eWebServerInfo = await getRspackE2EWebServerInfo(
|
||||||
|
tree,
|
||||||
|
options.projectName,
|
||||||
|
joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
`rspack.config.${options.js ? 'js' : 'ts'}`
|
||||||
|
),
|
||||||
|
options.addPlugin,
|
||||||
|
options.devServerPort ?? 4200
|
||||||
|
);
|
||||||
} else if (options.bundler === 'vite') {
|
} else if (options.bundler === 'vite') {
|
||||||
const { getViteE2EWebServerInfo, getReactRouterE2EWebServerInfo } =
|
const { getViteE2EWebServerInfo, getReactRouterE2EWebServerInfo } =
|
||||||
ensurePackage<typeof import('@nx/vite')>('@nx/vite', nxVersion);
|
ensurePackage<typeof import('@nx/vite')>('@nx/vite', nxVersion);
|
||||||
|
|||||||
@ -33,41 +33,13 @@ import {
|
|||||||
typesReactVersion,
|
typesReactVersion,
|
||||||
} from '../../../utils/versions';
|
} from '../../../utils/versions';
|
||||||
|
|
||||||
export async function createApplicationFiles(
|
export function getDefaultTemplateVariables(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: NormalizedSchema
|
options: NormalizedSchema
|
||||||
) {
|
) {
|
||||||
let styleSolutionSpecificAppFiles: string;
|
|
||||||
if (options.styledModule && options.style !== 'styled-jsx') {
|
|
||||||
styleSolutionSpecificAppFiles = '../files/style-styled-module';
|
|
||||||
} else if (options.style === 'styled-jsx') {
|
|
||||||
styleSolutionSpecificAppFiles = '../files/style-styled-jsx';
|
|
||||||
} else if (options.style === 'tailwind') {
|
|
||||||
styleSolutionSpecificAppFiles = '../files/style-tailwind';
|
|
||||||
} else if (options.style === 'none') {
|
|
||||||
styleSolutionSpecificAppFiles = '../files/style-none';
|
|
||||||
} else if (options.globalCss) {
|
|
||||||
styleSolutionSpecificAppFiles = '../files/style-global-css';
|
|
||||||
} else {
|
|
||||||
styleSolutionSpecificAppFiles = '../files/style-css-module';
|
|
||||||
}
|
|
||||||
const hasStyleFile = ['scss', 'css', 'less'].includes(options.style);
|
const hasStyleFile = ['scss', 'css', 'less'].includes(options.style);
|
||||||
|
|
||||||
const onBoardingStatus = await createNxCloudOnboardingURLForWelcomeApp(
|
|
||||||
host,
|
|
||||||
options.nxCloudToken
|
|
||||||
);
|
|
||||||
|
|
||||||
const connectCloudUrl =
|
|
||||||
onBoardingStatus === 'unclaimed' &&
|
|
||||||
(await getNxCloudAppOnBoardingUrl(options.nxCloudToken));
|
|
||||||
|
|
||||||
const relativePathToRootTsConfig = getRelativePathToRootTsConfig(
|
|
||||||
host,
|
|
||||||
options.appProjectRoot
|
|
||||||
);
|
|
||||||
const appTests = getAppTests(options);
|
const appTests = getAppTests(options);
|
||||||
const templateVariables = {
|
return {
|
||||||
...options.names,
|
...options.names,
|
||||||
...options,
|
...options,
|
||||||
typesNodeVersion,
|
typesNodeVersion,
|
||||||
@ -86,6 +58,79 @@ export async function createApplicationFiles(
|
|||||||
hasStyleFile,
|
hasStyleFile,
|
||||||
isUsingTsSolutionSetup: isUsingTsSolutionSetup(host),
|
isUsingTsSolutionSetup: isUsingTsSolutionSetup(host),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNxRspackPluginOptions(
|
||||||
|
options: NormalizedSchema,
|
||||||
|
rootOffset: string,
|
||||||
|
tsx: boolean = true
|
||||||
|
): WithNxOptions & WithReactOptions {
|
||||||
|
return {
|
||||||
|
target: 'web',
|
||||||
|
outputPath: options.isUsingTsSolutionConfig
|
||||||
|
? 'dist'
|
||||||
|
: joinPathFragments(
|
||||||
|
rootOffset,
|
||||||
|
'dist',
|
||||||
|
options.appProjectRoot != '.'
|
||||||
|
? options.appProjectRoot
|
||||||
|
: options.projectName
|
||||||
|
),
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
main: maybeJs(
|
||||||
|
{
|
||||||
|
js: options.js,
|
||||||
|
useJsx: true,
|
||||||
|
},
|
||||||
|
`./src/main.${tsx ? 'tsx' : 'ts'}`
|
||||||
|
),
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
assets: ['./src/favicon.ico', './src/assets'],
|
||||||
|
styles:
|
||||||
|
options.styledModule || !options.hasStyles
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
`./src/styles.${
|
||||||
|
options.style !== 'tailwind' ? options.style : 'css'
|
||||||
|
}`,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createApplicationFiles(
|
||||||
|
host: Tree,
|
||||||
|
options: NormalizedSchema
|
||||||
|
) {
|
||||||
|
let styleSolutionSpecificAppFiles: string;
|
||||||
|
if (options.styledModule && options.style !== 'styled-jsx') {
|
||||||
|
styleSolutionSpecificAppFiles = '../files/style-styled-module';
|
||||||
|
} else if (options.style === 'styled-jsx') {
|
||||||
|
styleSolutionSpecificAppFiles = '../files/style-styled-jsx';
|
||||||
|
} else if (options.style === 'tailwind') {
|
||||||
|
styleSolutionSpecificAppFiles = '../files/style-tailwind';
|
||||||
|
} else if (options.style === 'none') {
|
||||||
|
styleSolutionSpecificAppFiles = '../files/style-none';
|
||||||
|
} else if (options.globalCss) {
|
||||||
|
styleSolutionSpecificAppFiles = '../files/style-global-css';
|
||||||
|
} else {
|
||||||
|
styleSolutionSpecificAppFiles = '../files/style-css-module';
|
||||||
|
}
|
||||||
|
|
||||||
|
const onBoardingStatus = await createNxCloudOnboardingURLForWelcomeApp(
|
||||||
|
host,
|
||||||
|
options.nxCloudToken
|
||||||
|
);
|
||||||
|
|
||||||
|
const connectCloudUrl =
|
||||||
|
onBoardingStatus === 'unclaimed' &&
|
||||||
|
(await getNxCloudAppOnBoardingUrl(options.nxCloudToken));
|
||||||
|
|
||||||
|
const relativePathToRootTsConfig = getRelativePathToRootTsConfig(
|
||||||
|
host,
|
||||||
|
options.appProjectRoot
|
||||||
|
);
|
||||||
|
const templateVariables = getDefaultTemplateVariables(host, options);
|
||||||
|
|
||||||
if (options.bundler === 'vite' && !options.useReactRouter) {
|
if (options.bundler === 'vite' && !options.useReactRouter) {
|
||||||
generateFiles(
|
generateFiles(
|
||||||
@ -279,43 +324,6 @@ function createNxWebpackPluginOptions(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNxRspackPluginOptions(
|
|
||||||
options: NormalizedSchema,
|
|
||||||
rootOffset: string
|
|
||||||
): WithNxOptions & WithReactOptions {
|
|
||||||
return {
|
|
||||||
target: 'web',
|
|
||||||
outputPath: options.isUsingTsSolutionConfig
|
|
||||||
? 'dist'
|
|
||||||
: joinPathFragments(
|
|
||||||
rootOffset,
|
|
||||||
'dist',
|
|
||||||
options.appProjectRoot != '.'
|
|
||||||
? options.appProjectRoot
|
|
||||||
: options.projectName
|
|
||||||
),
|
|
||||||
index: './src/index.html',
|
|
||||||
baseHref: '/',
|
|
||||||
main: maybeJs(
|
|
||||||
{
|
|
||||||
js: options.js,
|
|
||||||
useJsx: true,
|
|
||||||
},
|
|
||||||
`./src/main.tsx`
|
|
||||||
),
|
|
||||||
tsConfig: './tsconfig.app.json',
|
|
||||||
assets: ['./src/favicon.ico', './src/assets'],
|
|
||||||
styles:
|
|
||||||
options.styledModule || !options.hasStyles
|
|
||||||
? []
|
|
||||||
: [
|
|
||||||
`./src/styles.${
|
|
||||||
options.style !== 'tailwind' ? options.style : 'css'
|
|
||||||
}`,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateReactRouterFiles(
|
function generateReactRouterFiles(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: NormalizedSchema,
|
options: NormalizedSchema,
|
||||||
|
|||||||
@ -1,30 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`hostGenerator bundler=rspack should generate host files and configs for SSR 1`] = `
|
exports[`hostGenerator bundler=rspack should generate host files and configs for SSR 1`] = `
|
||||||
"const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
|
||||||
const { withModuleFederationForSSR } = require('@nx/module-federation/rspack');
|
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(
|
|
||||||
withNx(),
|
|
||||||
withReact({ ssr: true }),
|
|
||||||
withModuleFederationForSSR(defaultConfig, { dts: false })
|
|
||||||
);
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`hostGenerator bundler=rspack should generate host files and configs for SSR 2`] = `
|
|
||||||
"// @ts-check
|
"// @ts-check
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,6 +9,12 @@ exports[`hostGenerator bundler=rspack should generate host files and configs for
|
|||||||
const moduleFederationConfig = {
|
const moduleFederationConfig = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
remotes: [],
|
remotes: [],
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,35 +25,17 @@ module.exports = moduleFederationConfig;
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator bundler=rspack should generate host files and configs for SSR when --typescriptConfiguration=true 1`] = `
|
exports[`hostGenerator bundler=rspack should generate host files and configs for SSR when --typescriptConfiguration=true 1`] = `
|
||||||
"import { composePlugins, withNx, withReact } from '@nx/rspack';
|
|
||||||
import { withModuleFederationForSSR } from '@nx/module-federation/rspack';
|
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(
|
|
||||||
withNx(),
|
|
||||||
withReact({ ssr: true }),
|
|
||||||
withModuleFederationForSSR(defaultConfig, { dts: false })
|
|
||||||
);
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`hostGenerator bundler=rspack should generate host files and configs for SSR when --typescriptConfiguration=true 2`] = `
|
|
||||||
"import { ModuleFederationConfig } from '@nx/module-federation';
|
"import { ModuleFederationConfig } from '@nx/module-federation';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
remotes: [],
|
remotes: [],
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,26 +46,49 @@ export default config;
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator bundler=rspack should generate host files and configs when --typescriptConfiguration=false 1`] = `
|
exports[`hostGenerator bundler=rspack should generate host files and configs when --typescriptConfiguration=false 1`] = `
|
||||||
"const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
"const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
const { withModuleFederation } = require('@nx/module-federation/rspack');
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const {
|
||||||
|
NxModuleFederationPlugin,
|
||||||
|
NxModuleFederationDevServerPlugin,
|
||||||
|
} = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
const config = {
|
module.exports = {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '../dist/test'),
|
||||||
|
publicPath: 'auto',
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: 4200,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
main: './src/main.ts',
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
assets: ['./src/favicon.ico', './src/assets'],
|
||||||
|
styles: ['./src/styles.css'],
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(
|
|
||||||
withNx(),
|
|
||||||
withReact(),
|
|
||||||
withModuleFederation(config, { dts: false })
|
|
||||||
);
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -129,23 +116,46 @@ module.exports = {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator bundler=rspack should generate host files and configs when --typescriptConfiguration=true 1`] = `
|
exports[`hostGenerator bundler=rspack should generate host files and configs when --typescriptConfiguration=true 1`] = `
|
||||||
"import {composePlugins, withNx, withReact} from '@nx/rspack';
|
"import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
import { withModuleFederation } from '@nx/module-federation/rspack';
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
import { ModuleFederationConfig } from '@nx/module-federation';
|
import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/rspack';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
import config from './module-federation.config';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
export default {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '../dist/test'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: 4200,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
main: './src/main.ts',
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
assets: ["./src/favicon.ico","./src/assets"],
|
||||||
|
styles: ["./src/styles.css"],
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,12 @@ const config: ModuleFederationConfig = {
|
|||||||
"<%= r.fileName %>",
|
"<%= r.fileName %>",
|
||||||
<%_ }); } _%>
|
<%_ }); } _%>
|
||||||
],
|
],
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
|
import { NxModuleFederationPlugin, NxModuleFederationSSRDevServerPlugin } from '@nx/module-federation/rspack';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import browserMfConfig from './module-federation.config';
|
||||||
|
import serverMfConfig from './module-federation.server.config';
|
||||||
|
|
||||||
|
const browserRspackConfig = {
|
||||||
|
name: 'browser',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'browser'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
devMiddleware: {
|
||||||
|
writeToDisk: (file: string) => !file.includes('.hot-update.'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: browserMfConfig }, { dts: false }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const serverRspackConfig = {
|
||||||
|
name: 'server',
|
||||||
|
target: 'async-node',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
filename: 'server.js'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
outputPath: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
outputFileName: 'server.js',
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.mainServer %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: serverMfConfig, isServer: true }, { dts: false }),
|
||||||
|
new NxModuleFederationSSRDevServerPlugin({ config: serverMfConfig }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default [browserRspackConfig, serverRspackConfig];
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
|
||||||
import {withModuleFederationForSSR} from '@nx/module-federation/rspack';
|
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
|
||||||
@ -7,7 +7,7 @@ import { handleRequest } from './src/main.server';
|
|||||||
const port = process.env['PORT'] || <%= port %>;
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
const browserDist = path.join(process.cwd(), '<%= rspackPluginOptions.outputPath %>', 'browser');
|
||||||
const indexPath = path.join(browserDist, 'index.html');
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
import type { Request, Response } from 'express';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as ReactDOMServer from 'react-dom/server';
|
||||||
|
import isbot from 'isbot'
|
||||||
|
|
||||||
|
import App from './app/app';
|
||||||
|
|
||||||
|
import { StaticRouter } from 'react-router-dom/server';
|
||||||
|
|
||||||
|
|
||||||
|
let indexHtml: null | string = null;
|
||||||
|
|
||||||
|
export function handleRequest(indexPath: string) {
|
||||||
|
return function render(req: Request, res: Response) {
|
||||||
|
let didError = false;
|
||||||
|
|
||||||
|
if (!indexHtml) {
|
||||||
|
indexHtml = fs.readFileSync(indexPath).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [htmlStart, htmlEnd] = indexHtml.split(`<div id="root"></div>`);
|
||||||
|
|
||||||
|
// For bots (e.g. search engines), the content will not be streamed but render all at once.
|
||||||
|
// For users, content should be streamed to the user as they are ready.
|
||||||
|
const callbackName = isbot(req.headers['user-agent']) ? 'onAllReady' : 'onShellReady';
|
||||||
|
|
||||||
|
const stream = ReactDOMServer.renderToPipeableStream(
|
||||||
|
<StaticRouter location={req.originalUrl}><App /></StaticRouter>,
|
||||||
|
{
|
||||||
|
[callbackName]() {
|
||||||
|
res.statusCode = didError ? 500 : 200;
|
||||||
|
res.setHeader('Content-type', 'text/html; charset=utf-8');
|
||||||
|
res.write(`${htmlStart}<div id="root">`);
|
||||||
|
stream.pipe(res);
|
||||||
|
res.write(`</div>${htmlEnd}`);
|
||||||
|
},
|
||||||
|
onShellError(error) {
|
||||||
|
console.error(error);
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.send('<!doctype html><h1>Server Error</h1>');
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
didError = true;
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,6 +13,12 @@ const moduleFederationConfig = {
|
|||||||
}
|
}
|
||||||
_%>
|
_%>
|
||||||
],
|
],
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationSSRDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
|
const browserMfConfig = require('./module-federation.config');
|
||||||
|
const serverMfConfig = require('./module-federation.server.config');
|
||||||
|
|
||||||
|
const browserRspackConfig = {
|
||||||
|
name: 'browser',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'browser'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
devMiddleware: {
|
||||||
|
writeToDisk: (file: string) => !file.includes('.hot-update.'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: browserMfConfig }, { dts: false }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const serverRspackConfig = {
|
||||||
|
name: 'server',
|
||||||
|
target: 'async-node',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
filename: 'server.js'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
outputPath: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
outputFileName: 'server.js',
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.mainServer %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: serverMfConfig, isServer: true }, { dts: false }),
|
||||||
|
new NxModuleFederationSSRDevServerPlugin({ config: serverMfConfig }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = [browserRspackConfig, serverRspackConfig];
|
||||||
@ -1,16 +0,0 @@
|
|||||||
const {composePlugins, withNx, withReact} = require('@nx/rspack');
|
|
||||||
const {withModuleFederationForSSR} = require('@nx/module-federation/rspack');
|
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
|
||||||
@ -7,7 +7,7 @@ import { handleRequest } from './src/main.server';
|
|||||||
const port = process.env['PORT'] || <%= port %>;
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
const browserDist = path.join(process.cwd(), '<%= rspackPluginOptions.outputPath %>', 'browser');
|
||||||
const indexPath = path.join(browserDist, 'index.html');
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
import type { Request, Response } from 'express';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as ReactDOMServer from 'react-dom/server';
|
||||||
|
import isbot from 'isbot'
|
||||||
|
|
||||||
|
import App from './app/app';
|
||||||
|
|
||||||
|
import { StaticRouter } from 'react-router-dom/server';
|
||||||
|
|
||||||
|
|
||||||
|
let indexHtml: null | string = null;
|
||||||
|
|
||||||
|
export function handleRequest(indexPath: string) {
|
||||||
|
return function render(req: Request, res: Response) {
|
||||||
|
let didError = false;
|
||||||
|
|
||||||
|
if (!indexHtml) {
|
||||||
|
indexHtml = fs.readFileSync(indexPath).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [htmlStart, htmlEnd] = indexHtml.split(`<div id="root"></div>`);
|
||||||
|
|
||||||
|
// For bots (e.g. search engines), the content will not be streamed but render all at once.
|
||||||
|
// For users, content should be streamed to the user as they are ready.
|
||||||
|
const callbackName = isbot(req.headers['user-agent']) ? 'onAllReady' : 'onShellReady';
|
||||||
|
|
||||||
|
const stream = ReactDOMServer.renderToPipeableStream(
|
||||||
|
<StaticRouter location={req.originalUrl}><App /></StaticRouter>,
|
||||||
|
{
|
||||||
|
[callbackName]() {
|
||||||
|
res.statusCode = didError ? 500 : 200;
|
||||||
|
res.setHeader('Content-type', 'text/html; charset=utf-8');
|
||||||
|
res.write(`${htmlStart}<div id="root">`);
|
||||||
|
stream.pipe(res);
|
||||||
|
res.write(`</div>${htmlEnd}`);
|
||||||
|
},
|
||||||
|
onShellError(error) {
|
||||||
|
console.error(error);
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.send('<!doctype html><h1>Server Error</h1>');
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
didError = true;
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import { composePlugins, withNx, withReact } from '@nx/rspack';
|
import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
import { withModuleFederation } from '@nx/module-federation/rspack';
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
|
import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/rspack';
|
||||||
import { ModuleFederationConfig } from '@nx/module-federation';
|
import { ModuleFederationConfig } from '@nx/module-federation';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
@ -30,10 +32,36 @@ const prodConfig: ModuleFederationConfig = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
export default {
|
||||||
/**
|
output: {
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>'),
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
publicPath: 'auto'
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
},
|
||||||
*/
|
devServer: {
|
||||||
export default composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
|
port: <%= devServerPort %>,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: prodConfig }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config: prodConfig }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|||||||
@ -1,17 +1,40 @@
|
|||||||
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
import { withModuleFederation } from '@nx/module-federation/rspack';
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
import { ModuleFederationConfig } from '@nx/module-federation';
|
import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/rspack';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
import config from './module-federation.config';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
export default {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
|
|||||||
@ -1,16 +1,40 @@
|
|||||||
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
const { withModuleFederation } = require('@nx/module-federation/rspack');
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
const config = {
|
module.exports = {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
|
|||||||
@ -1,38 +1,66 @@
|
|||||||
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
const { withModuleFederation } = require('@nx/module-federation/rspack');
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
const prodConfig = {
|
const prodConfig = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
/*
|
/*
|
||||||
* Remote overrides for production.
|
* Remote overrides for production.
|
||||||
* Each entry is a pair of a unique name and the URL where it is deployed.
|
* Each entry is a pair of a unique name and the URL where it is deployed.
|
||||||
*
|
*
|
||||||
* e.g.
|
* e.g.
|
||||||
* remotes: [
|
* remotes: [
|
||||||
* ['app1', 'http://app1.example.com'],
|
* ['app1', 'http://app1.example.com'],
|
||||||
* ['app2', 'http://app2.example.com'],
|
* ['app2', 'http://app2.example.com'],
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
* You can also use a full path to the remoteEntry.js file if desired.
|
* You can also use a full path to the remoteEntry.js file if desired.
|
||||||
*
|
*
|
||||||
* remotes: [
|
* remotes: [
|
||||||
* ['app1', 'http://example.com/path/to/app1/remoteEntry.js'],
|
* ['app1', 'http://example.com/path/to/app1/remoteEntry.js'],
|
||||||
* ['app2', 'http://example.com/path/to/app2/remoteEntry.js'],
|
* ['app2', 'http://example.com/path/to/app2/remoteEntry.js'],
|
||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
remotes: [
|
remotes: [
|
||||||
<%_ remotes.forEach(function(r) { _%>
|
<%_ remotes.forEach(function(r) { _%>
|
||||||
['<%= r.fileName %>', 'http://localhost:<%= r.port %>/'],
|
['<%= r.fileName %>', 'http://localhost:<%= r.port %>/'],
|
||||||
<%_ }); _%>
|
<%_ }); _%>
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
module.exports = {
|
||||||
/**
|
output: {
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>'),
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
publicPath: 'auto'
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
},
|
||||||
*/
|
devServer: {
|
||||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
|
port: <%= devServerPort %>,
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: prodConfig }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config: prodConfig }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|||||||
@ -226,7 +226,6 @@ describe('hostGenerator', () => {
|
|||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
expect(tree.exists('test/rspack.config.prod.js')).toBeTruthy();
|
expect(tree.exists('test/rspack.config.prod.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/rspack.server.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/rspack.config.js')).toBeTruthy();
|
expect(tree.exists('test/rspack.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
@ -250,9 +249,6 @@ describe('hostGenerator', () => {
|
|||||||
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/rspack.server.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
expect(
|
||||||
tree.read('test/module-federation.server.config.js', 'utf-8')
|
tree.read('test/module-federation.server.config.js', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
@ -272,7 +268,6 @@ describe('hostGenerator', () => {
|
|||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
expect(tree.exists('test/rspack.config.prod.ts')).toBeTruthy();
|
expect(tree.exists('test/rspack.config.prod.ts')).toBeTruthy();
|
||||||
expect(tree.exists('test/rspack.server.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/rspack.config.ts')).toBeTruthy();
|
expect(tree.exists('test/rspack.config.ts')).toBeTruthy();
|
||||||
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
@ -296,9 +291,6 @@ describe('hostGenerator', () => {
|
|||||||
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/rspack.server.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
expect(
|
||||||
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
|
|||||||
@ -29,15 +29,18 @@ import {
|
|||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { ensureRootProjectName } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
import { ensureRootProjectName } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import { updateModuleFederationTsconfig } from './lib/update-module-federation-tsconfig';
|
import { updateModuleFederationTsconfig } from './lib/update-module-federation-tsconfig';
|
||||||
|
import { normalizeHostName } from './lib/normalize-host-name';
|
||||||
|
|
||||||
export async function hostGenerator(
|
export async function hostGenerator(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
schema: Schema
|
schema: Schema
|
||||||
): Promise<GeneratorCallback> {
|
): Promise<GeneratorCallback> {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
const name = await normalizeHostName(host, schema.directory, schema.name);
|
||||||
const options: NormalizedSchema = {
|
const options: NormalizedSchema = {
|
||||||
...(await normalizeOptions<Schema>(host, {
|
...(await normalizeOptions<Schema>(host, {
|
||||||
...schema,
|
...schema,
|
||||||
|
name,
|
||||||
useProjectJson: true,
|
useProjectJson: true,
|
||||||
})),
|
})),
|
||||||
js: schema.js ?? false,
|
js: schema.js ?? false,
|
||||||
@ -45,8 +48,8 @@ export async function hostGenerator(
|
|||||||
? false
|
? false
|
||||||
: schema.typescriptConfiguration ?? true,
|
: schema.typescriptConfiguration ?? true,
|
||||||
dynamic: schema.dynamic ?? false,
|
dynamic: schema.dynamic ?? false,
|
||||||
// TODO(colum): remove when MF works with Crystal
|
// TODO(colum): remove when Webpack MF works with Crystal
|
||||||
addPlugin: false,
|
addPlugin: !schema.bundler || schema.bundler === 'rspack' ? true : false,
|
||||||
bundler: schema.bundler ?? 'rspack',
|
bundler: schema.bundler ?? 'rspack',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,17 +109,19 @@ export async function hostGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
addModuleFederationFiles(host, options, remotesWithPorts);
|
addModuleFederationFiles(host, options, remotesWithPorts);
|
||||||
updateModuleFederationProject(host, options);
|
updateModuleFederationProject(host, options, true);
|
||||||
updateModuleFederationE2eProject(host, options);
|
updateModuleFederationE2eProject(host, options);
|
||||||
updateModuleFederationTsconfig(host, options);
|
updateModuleFederationTsconfig(host, options);
|
||||||
|
|
||||||
if (options.ssr) {
|
if (options.ssr) {
|
||||||
const setupSsrTask = await setupSsrGenerator(host, {
|
if (options.bundler !== 'rspack') {
|
||||||
project: options.projectName,
|
const setupSsrTask = await setupSsrGenerator(host, {
|
||||||
serverPort: options.devServerPort,
|
project: options.projectName,
|
||||||
skipFormat: true,
|
serverPort: options.devServerPort,
|
||||||
});
|
skipFormat: true,
|
||||||
tasks.push(setupSsrTask);
|
});
|
||||||
|
tasks.push(setupSsrTask);
|
||||||
|
}
|
||||||
|
|
||||||
const setupSsrForHostTask = await setupSsrForHost(
|
const setupSsrForHostTask = await setupSsrForHost(
|
||||||
host,
|
host,
|
||||||
@ -127,14 +132,7 @@ export async function hostGenerator(
|
|||||||
tasks.push(setupSsrForHostTask);
|
tasks.push(setupSsrForHostTask);
|
||||||
|
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
if (options.bundler === 'rspack') {
|
if (options.bundler !== 'rspack') {
|
||||||
projectConfig.targets.server.executor = '@nx/rspack:rspack';
|
|
||||||
projectConfig.targets.server.options.rspackConfig = joinPathFragments(
|
|
||||||
projectConfig.root,
|
|
||||||
`rspack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
|
||||||
);
|
|
||||||
delete projectConfig.targets.server.options.webpackConfig;
|
|
||||||
} else {
|
|
||||||
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
|
|||||||
@ -4,27 +4,52 @@ import {
|
|||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
names,
|
names,
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
|
offsetFromRoot,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { maybeJs } from '../../../utils/maybe-js';
|
import { maybeJs } from '../../../utils/maybe-js';
|
||||||
import { NormalizedSchema } from '../schema';
|
import { NormalizedSchema } from '../schema';
|
||||||
|
import {
|
||||||
|
createNxRspackPluginOptions,
|
||||||
|
getDefaultTemplateVariables,
|
||||||
|
} from '../../application/lib/create-application-files';
|
||||||
|
|
||||||
export function addModuleFederationFiles(
|
export function addModuleFederationFiles(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: NormalizedSchema,
|
options: NormalizedSchema,
|
||||||
defaultRemoteManifest: { name: string; port: number }[]
|
defaultRemoteManifest: { name: string; port: number }[]
|
||||||
) {
|
) {
|
||||||
const templateVariables = {
|
const templateVariables =
|
||||||
...names(options.projectName),
|
options.bundler === 'rspack'
|
||||||
...options,
|
? {
|
||||||
static: !options?.dynamic,
|
...getDefaultTemplateVariables(host, options as any),
|
||||||
tmpl: '',
|
rspackPluginOptions: {
|
||||||
remotes: defaultRemoteManifest.map(({ name, port }) => {
|
...createNxRspackPluginOptions(
|
||||||
return {
|
options as any,
|
||||||
...names(name),
|
offsetFromRoot(options.appProjectRoot),
|
||||||
port,
|
false
|
||||||
};
|
),
|
||||||
}),
|
mainServer: `./server.ts`,
|
||||||
};
|
},
|
||||||
|
static: !options?.dynamic,
|
||||||
|
remotes: defaultRemoteManifest.map(({ name, port }) => {
|
||||||
|
return {
|
||||||
|
...names(name),
|
||||||
|
port,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...names(options.projectName),
|
||||||
|
...options,
|
||||||
|
static: !options?.dynamic,
|
||||||
|
tmpl: '',
|
||||||
|
remotes: defaultRemoteManifest.map(({ name, port }) => {
|
||||||
|
return {
|
||||||
|
...names(name),
|
||||||
|
port,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
const pathToMFManifest = joinPathFragments(
|
const pathToMFManifest = joinPathFragments(
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { Tree } from '@nx/devkit';
|
||||||
|
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
|
||||||
|
export async function normalizeHostName(
|
||||||
|
tree: Tree,
|
||||||
|
directory: string,
|
||||||
|
name?: string
|
||||||
|
): Promise<string> {
|
||||||
|
const { projectName } = await determineProjectNameAndRootOptions(tree, {
|
||||||
|
name,
|
||||||
|
directory,
|
||||||
|
projectType: 'application',
|
||||||
|
});
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import type { GeneratorCallback, Tree } from '@nx/devkit';
|
import { GeneratorCallback, offsetFromRoot, Tree } from '@nx/devkit';
|
||||||
import {
|
import {
|
||||||
addDependenciesToPackageJson,
|
addDependenciesToPackageJson,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
@ -8,23 +8,32 @@ import {
|
|||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
updateProjectConfiguration,
|
updateProjectConfiguration,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
|
import type { NormalizedSchema } from '../schema';
|
||||||
import type { Schema } from '../schema';
|
import {
|
||||||
import { moduleFederationNodeVersion } from '../../../utils/versions';
|
corsVersion,
|
||||||
|
expressVersion,
|
||||||
|
isbotVersion,
|
||||||
|
moduleFederationNodeVersion,
|
||||||
|
typesExpressVersion,
|
||||||
|
} from '../../../utils/versions';
|
||||||
|
import {
|
||||||
|
createNxRspackPluginOptions,
|
||||||
|
getDefaultTemplateVariables,
|
||||||
|
} from '../../application/lib/create-application-files';
|
||||||
|
|
||||||
export async function setupSsrForHost(
|
export async function setupSsrForHost(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: Schema,
|
options: NormalizedSchema,
|
||||||
appName: string,
|
appName: string,
|
||||||
defaultRemoteManifest: { name: string; port: number }[]
|
defaultRemoteManifest: { name: string; port: number }[]
|
||||||
) {
|
) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
let project = readProjectConfiguration(tree, appName);
|
let project = readProjectConfiguration(tree, appName);
|
||||||
project.targets.serve.executor =
|
if (options.bundler !== 'rspack') {
|
||||||
options.bundler === 'rspack'
|
project.targets.serve.executor =
|
||||||
? '@nx/rspack:module-federation-ssr-dev-server'
|
'@nx/react:module-federation-ssr-dev-server';
|
||||||
: '@nx/react:module-federation-ssr-dev-server';
|
updateProjectConfiguration(tree, appName, project);
|
||||||
updateProjectConfiguration(tree, appName, project);
|
}
|
||||||
|
|
||||||
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
||||||
? `${
|
? `${
|
||||||
@ -34,30 +43,58 @@ export async function setupSsrForHost(
|
|||||||
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
}module-federation-ssr`;
|
}module-federation-ssr`;
|
||||||
|
|
||||||
|
const templateVariables =
|
||||||
|
options.bundler === 'rspack'
|
||||||
|
? {
|
||||||
|
...getDefaultTemplateVariables(tree, options as any),
|
||||||
|
rspackPluginOptions: {
|
||||||
|
...createNxRspackPluginOptions(
|
||||||
|
options as any,
|
||||||
|
offsetFromRoot(options.appProjectRoot),
|
||||||
|
false
|
||||||
|
),
|
||||||
|
mainServer: `./server.ts`,
|
||||||
|
},
|
||||||
|
port: Number(options?.devServerPort) || 4200,
|
||||||
|
appName,
|
||||||
|
static: !options?.dynamic,
|
||||||
|
remotes: defaultRemoteManifest.map(({ name, port }) => {
|
||||||
|
return {
|
||||||
|
...names(name),
|
||||||
|
port,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...options,
|
||||||
|
static: !options?.dynamic,
|
||||||
|
port: Number(options?.devServerPort) || 4200,
|
||||||
|
appName,
|
||||||
|
tmpl: '',
|
||||||
|
browserBuildOutputPath: project.targets.build?.options?.outputPath,
|
||||||
|
remotes: defaultRemoteManifest.map(({ name, port }) => {
|
||||||
|
return {
|
||||||
|
...names(name),
|
||||||
|
port,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
joinPathFragments(__dirname, `../files/${pathToModuleFederationSsrFiles}`),
|
joinPathFragments(__dirname, `../files/${pathToModuleFederationSsrFiles}`),
|
||||||
project.root,
|
project.root,
|
||||||
{
|
templateVariables
|
||||||
...options,
|
|
||||||
static: !options?.dynamic,
|
|
||||||
port: Number(options?.devServerPort) || 4200,
|
|
||||||
remotes: defaultRemoteManifest.map(({ name, port }) => {
|
|
||||||
return {
|
|
||||||
...names(name),
|
|
||||||
port,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
appName,
|
|
||||||
tmpl: '',
|
|
||||||
browserBuildOutputPath: project.targets.build.options.outputPath,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const installTask = addDependenciesToPackageJson(
|
const installTask = addDependenciesToPackageJson(
|
||||||
tree,
|
tree,
|
||||||
{
|
{
|
||||||
'@module-federation/node': moduleFederationNodeVersion,
|
'@module-federation/node': moduleFederationNodeVersion,
|
||||||
|
cors: corsVersion,
|
||||||
|
isbot: isbotVersion,
|
||||||
|
express: expressVersion,
|
||||||
|
'@types/express': typesExpressVersion,
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,22 +1,49 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should create the remote with the correct config files 1`] = `
|
exports[`remote generator bundler=rspack should create the remote with the correct config files 1`] = `
|
||||||
"const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
"const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
const { withModuleFederation } = require('@nx/module-federation/rspack');
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
const config = {
|
module.exports = {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '../dist/test'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: 4201,
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
main: './src/main.ts',
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
assets: ["./src/favicon.ico","./src/assets"],
|
||||||
|
styles: ["./src/styles.css"],
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -36,22 +63,49 @@ module.exports = {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should create the remote with the correct config files when --js=true 1`] = `
|
exports[`remote generator bundler=rspack should create the remote with the correct config files when --js=true 1`] = `
|
||||||
"const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
"const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
const { withModuleFederation } = require('@nx/module-federation/rspack');
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
const config = {
|
module.exports = {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '../dist/test'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: 4201,
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
main: './src/main.jsx',
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
assets: ["./src/favicon.ico","./src/assets"],
|
||||||
|
styles: ["./src/styles.css"],
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -71,26 +125,52 @@ module.exports = {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should create the remote with the correct config files when --typescriptConfiguration=true 1`] = `
|
exports[`remote generator bundler=rspack should create the remote with the correct config files when --typescriptConfiguration=true 1`] = `
|
||||||
"import { composePlugins, withNx, withReact } from '@nx/rspack';
|
"import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
import { withModuleFederation } from '@nx/module-federation/rspack';
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
|
import {
|
||||||
|
NxModuleFederationPlugin,
|
||||||
|
NxModuleFederationDevServerPlugin,
|
||||||
|
} from '@nx/module-federation/rspack';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
import config from './module-federation.config';
|
||||||
|
|
||||||
const config = {
|
export default {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '../dist/test'),
|
||||||
|
publicPath: 'auto',
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: 4201,
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: './tsconfig.app.json',
|
||||||
|
main: './src/main.ts',
|
||||||
|
index: './src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
assets: ['./src/favicon.ico', './src/assets'],
|
||||||
|
styles: ['./src/styles.css'],
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(
|
|
||||||
withNx(),
|
|
||||||
withReact(),
|
|
||||||
withModuleFederation(config, { dts: false })
|
|
||||||
);
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -117,26 +197,6 @@ export default config;
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr 1`] = `
|
exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr 1`] = `
|
||||||
"const {composePlugins, withNx, withReact} = require('@nx/rspack');
|
|
||||||
const {withModuleFederationForSSR} = require('@nx/module-federation/rspack');
|
|
||||||
|
|
||||||
const baseConfig = require("./module-federation.server.config");
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr 2`] = `
|
|
||||||
"/**
|
"/**
|
||||||
* Nx requires a default export of the config to allow correct resolution of the module federation graph.
|
* Nx requires a default export of the config to allow correct resolution of the module federation graph.
|
||||||
**/
|
**/
|
||||||
@ -145,35 +205,17 @@ module.exports = {
|
|||||||
exposes: {
|
exposes: {
|
||||||
'./Module': './src/remote-entry.ts',
|
'./Module': './src/remote-entry.ts',
|
||||||
},
|
},
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 1`] = `
|
exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 1`] = `
|
||||||
"import { composePlugins, withNx, withReact } from '@nx/rspack';
|
|
||||||
import { withModuleFederationForSSR } from '@nx/module-federation/rspack';
|
|
||||||
|
|
||||||
import baseConfig from './module-federation.server.config';
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(
|
|
||||||
withNx(),
|
|
||||||
withReact({ ssr: true }),
|
|
||||||
withModuleFederationForSSR(defaultConfig, { dts: false })
|
|
||||||
);
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 2`] = `
|
|
||||||
"import { ModuleFederationConfig } from '@nx/module-federation';
|
"import { ModuleFederationConfig } from '@nx/module-federation';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
@ -181,6 +223,12 @@ const config: ModuleFederationConfig = {
|
|||||||
exposes: {
|
exposes: {
|
||||||
'./Module': './src/remote-entry.ts',
|
'./Module': './src/remote-entry.ts',
|
||||||
},
|
},
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -5,6 +5,12 @@ const config: ModuleFederationConfig = {
|
|||||||
exposes: {
|
exposes: {
|
||||||
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
||||||
},
|
},
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
|
import { NxModuleFederationPlugin, NxModuleFederationSSRDevServerPlugin } from '@nx/module-federation/rspack';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import browserMfConfig from './module-federation.config';
|
||||||
|
import serverMfConfig from './module-federation.server.config';
|
||||||
|
|
||||||
|
const browserRspackConfig = {
|
||||||
|
name: 'browser',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'browser'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
devMiddleware: {
|
||||||
|
writeToDisk: (file: string) => !file.includes('.hot-update.'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: browserMfConfig }, { dts: false }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const serverRspackConfig = {
|
||||||
|
name: 'server',
|
||||||
|
target: 'async-node',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
filename: 'server.js'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
outputPath: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
outputFileName: 'server.js',
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.mainServer %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: serverMfConfig, isServer: true }, { dts: false }),
|
||||||
|
new NxModuleFederationSSRDevServerPlugin({ config: serverMfConfig }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default [browserRspackConfig, serverRspackConfig];
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
|
||||||
import {withModuleFederationForSSR} from '@nx/module-federation/rspack';
|
|
||||||
|
|
||||||
import baseConfig from "./module-federation.server.config";
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
|
||||||
@ -7,8 +7,8 @@ import { handleRequest } from './src/main.server';
|
|||||||
const port = process.env['PORT'] || <%= port %>;
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
const browserDist = path.join(process.cwd(), '<%= rspackPluginOptions.outputPath %>', 'browser');
|
||||||
const serverDist = path.join(process.cwd(), '<%= serverBuildOutputPath %>');
|
const serverDist = path.join(process.cwd(), '<%= rspackPluginOptions.outputPath %>', 'server');
|
||||||
const indexPath = path.join(browserDist, 'index.html');
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
import type { Request, Response } from 'express';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as ReactDOMServer from 'react-dom/server';
|
||||||
|
import isbot from 'isbot';
|
||||||
|
|
||||||
|
import App from './app/app';
|
||||||
|
|
||||||
|
let indexHtml: null | string = null;
|
||||||
|
|
||||||
|
export function handleRequest(indexPath: string) {
|
||||||
|
return function render(req: Request, res: Response) {
|
||||||
|
let didError = false;
|
||||||
|
|
||||||
|
if (!indexHtml) {
|
||||||
|
indexHtml = fs.readFileSync(indexPath).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [htmlStart, htmlEnd] = indexHtml.split(`<div id="root"></div>`);
|
||||||
|
|
||||||
|
// For bots (e.g. search engines), the content will not be streamed but render all at once.
|
||||||
|
// For users, content should be streamed to the user as they are ready.
|
||||||
|
const callbackName = isbot(req.headers['user-agent'])
|
||||||
|
? 'onAllReady'
|
||||||
|
: 'onShellReady';
|
||||||
|
|
||||||
|
const stream = ReactDOMServer.renderToPipeableStream(<App />, {
|
||||||
|
[callbackName]() {
|
||||||
|
res.statusCode = didError ? 500 : 200;
|
||||||
|
res.setHeader('Content-type', 'text/html; charset=utf-8');
|
||||||
|
res.write(`${htmlStart}<div id="root">`);
|
||||||
|
stream.pipe(res);
|
||||||
|
res.write(`</div>${htmlEnd}`);
|
||||||
|
},
|
||||||
|
onShellError(error) {
|
||||||
|
console.error(error);
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.send('<!doctype html><h1>Server Error</h1>');
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
didError = true;
|
||||||
|
console.error(error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -6,4 +6,10 @@ module.exports = {
|
|||||||
exposes: {
|
exposes: {
|
||||||
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
||||||
},
|
},
|
||||||
|
shared: (libraryName, libraryConfig) => {
|
||||||
|
return {
|
||||||
|
...libraryConfig,
|
||||||
|
eager: true
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationSSRDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
|
const browserMfConfig = require('./module-federation.config');
|
||||||
|
const serverMfConfig = require('./module-federation.server.config');
|
||||||
|
|
||||||
|
const browserRspackConfig = {
|
||||||
|
name: 'browser',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'browser'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
devMiddleware: {
|
||||||
|
writeToDisk: (file: string) => !file.includes('.hot-update.'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: browserMfConfig }, { dts: false }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const serverRspackConfig = {
|
||||||
|
name: 'server',
|
||||||
|
target: 'async-node',
|
||||||
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
filename: 'server.js'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
outputPath: join(__dirname, '<%= rspackPluginOptions.outputPath %>', 'server'),
|
||||||
|
outputFileName: 'server.js',
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.mainServer %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config: serverMfConfig, isServer: true }, { dts: false }),
|
||||||
|
new NxModuleFederationSSRDevServerPlugin({ config: serverMfConfig }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = [browserRspackConfig, serverRspackConfig];
|
||||||
@ -1,16 +0,0 @@
|
|||||||
const {composePlugins, withNx, withReact} = require('@nx/rspack');
|
|
||||||
const {withModuleFederationForSSR} = require('@nx/module-federation/rspack');
|
|
||||||
|
|
||||||
const baseConfig = require("./module-federation.server.config");
|
|
||||||
|
|
||||||
const defaultConfig = {
|
|
||||||
...baseConfig,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
|
||||||
@ -7,8 +7,8 @@ import { handleRequest } from './src/main.server';
|
|||||||
const port = process.env['PORT'] || <%= port %>;
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
const browserDist = path.join(process.cwd(), '<%= rspackPluginOptions.outputPath %>', 'browser');
|
||||||
const serverDist = path.join(process.cwd(), '<%= serverBuildOutputPath %>');
|
const serverDist = path.join(process.cwd(), '<%= rspackPluginOptions.outputPath %>', 'server');
|
||||||
const indexPath = path.join(browserDist, 'index.html');
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
|||||||
@ -0,0 +1,45 @@
|
|||||||
|
import type { Request, Response } from 'express';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as ReactDOMServer from 'react-dom/server';
|
||||||
|
import isbot from 'isbot';
|
||||||
|
|
||||||
|
import App from './app/app';
|
||||||
|
|
||||||
|
let indexHtml: null | string = null;
|
||||||
|
|
||||||
|
export function handleRequest(indexPath: string) {
|
||||||
|
return function render(req: Request, res: Response) {
|
||||||
|
let didError = false;
|
||||||
|
|
||||||
|
if (!indexHtml) {
|
||||||
|
indexHtml = fs.readFileSync(indexPath).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [htmlStart, htmlEnd] = indexHtml.split(`<div id="root"></div>`);
|
||||||
|
|
||||||
|
// For bots (e.g. search engines), the content will not be streamed but render all at once.
|
||||||
|
// For users, content should be streamed to the user as they are ready.
|
||||||
|
const callbackName = isbot(req.headers['user-agent'])
|
||||||
|
? 'onAllReady'
|
||||||
|
: 'onShellReady';
|
||||||
|
|
||||||
|
const stream = ReactDOMServer.renderToPipeableStream(<App />, {
|
||||||
|
[callbackName]() {
|
||||||
|
res.statusCode = didError ? 500 : 200;
|
||||||
|
res.setHeader('Content-type', 'text/html; charset=utf-8');
|
||||||
|
res.write(`${htmlStart}<div id="root">`);
|
||||||
|
stream.pipe(res);
|
||||||
|
res.write(`</div>${htmlEnd}`);
|
||||||
|
},
|
||||||
|
onShellError(error) {
|
||||||
|
console.error(error);
|
||||||
|
res.statusCode = 500;
|
||||||
|
res.send('<!doctype html><h1>Server Error</h1>');
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
didError = true;
|
||||||
|
console.error(error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,16 +1,43 @@
|
|||||||
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
import { NxAppRspackPlugin } from '@nx/rspack/app-plugin';
|
||||||
import {withModuleFederation} from '@nx/module-federation/rspack';
|
import { NxReactRspackPlugin } from '@nx/rspack/react-plugin';
|
||||||
|
import { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } from '@nx/module-federation/rspack';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import baseConfig from './module-federation.config';
|
import config from './module-federation.config';
|
||||||
|
|
||||||
const config = {
|
export default {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
|
|||||||
@ -1,16 +1,43 @@
|
|||||||
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
|
||||||
const { withModuleFederation } = require('@nx/module-federation/rspack');
|
const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin');
|
||||||
|
const { NxModuleFederationPlugin, NxModuleFederationDevServerPlugin } = require('@nx/module-federation/rspack');
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const baseConfig = require('./module-federation.config');
|
const config = require('./module-federation.config');
|
||||||
|
|
||||||
const config = {
|
module.exports = {
|
||||||
...baseConfig,
|
output: {
|
||||||
|
path: join(__dirname, '<%= rspackPluginOptions.outputPath %>'),
|
||||||
|
publicPath: 'auto'
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: <%= devServerPort %>,
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
},
|
||||||
|
historyApiFallback: {
|
||||||
|
index: '/index.html',
|
||||||
|
disableDotRule: true,
|
||||||
|
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NxAppRspackPlugin({
|
||||||
|
tsConfig: '<%= rspackPluginOptions.tsConfig %>',
|
||||||
|
main: '<%= rspackPluginOptions.main %>',
|
||||||
|
index: '<%= rspackPluginOptions.index %>',
|
||||||
|
baseHref: '<%= rspackPluginOptions.baseHref %>',
|
||||||
|
assets: <%- JSON.stringify(rspackPluginOptions.assets) %>,
|
||||||
|
styles: <%- JSON.stringify(rspackPluginOptions.styles) %>,
|
||||||
|
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
|
||||||
|
optimization: process.env['NODE_ENV'] === 'production',
|
||||||
|
}),
|
||||||
|
new NxReactRspackPlugin({
|
||||||
|
// Uncomment this line if you don't want to use SVGR
|
||||||
|
// See: https://react-svgr.com/
|
||||||
|
// svgr: false
|
||||||
|
}),
|
||||||
|
new NxModuleFederationPlugin({ config }, { dts: false }),
|
||||||
|
new NxModuleFederationDevServerPlugin({ config }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Nx plugins for rspack to build config object from Nx options and context.
|
|
||||||
/**
|
|
||||||
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
|
||||||
* The DTS Plugin can be enabled by setting dts: true
|
|
||||||
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
|
||||||
*/
|
|
||||||
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { GeneratorCallback, Tree } from '@nx/devkit';
|
import { GeneratorCallback, names, offsetFromRoot, Tree } from '@nx/devkit';
|
||||||
import {
|
import {
|
||||||
addDependenciesToPackageJson,
|
addDependenciesToPackageJson,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
@ -10,7 +10,17 @@ import {
|
|||||||
|
|
||||||
import { NormalizedSchema } from '../../application/schema';
|
import { NormalizedSchema } from '../../application/schema';
|
||||||
import type { Schema } from '../schema';
|
import type { Schema } from '../schema';
|
||||||
import { moduleFederationNodeVersion } from '../../../utils/versions';
|
import {
|
||||||
|
corsVersion,
|
||||||
|
expressVersion,
|
||||||
|
isbotVersion,
|
||||||
|
moduleFederationNodeVersion,
|
||||||
|
typesExpressVersion,
|
||||||
|
} from '../../../utils/versions';
|
||||||
|
import {
|
||||||
|
createNxRspackPluginOptions,
|
||||||
|
getDefaultTemplateVariables,
|
||||||
|
} from '../../application/lib/create-application-files';
|
||||||
|
|
||||||
export async function setupSsrForRemote(
|
export async function setupSsrForRemote(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
@ -28,22 +38,49 @@ export async function setupSsrForRemote(
|
|||||||
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
}module-federation-ssr`;
|
}module-federation-ssr`;
|
||||||
|
|
||||||
|
const templateVariables =
|
||||||
|
options.bundler === 'rspack'
|
||||||
|
? {
|
||||||
|
...getDefaultTemplateVariables(tree, options),
|
||||||
|
rspackPluginOptions: {
|
||||||
|
...createNxRspackPluginOptions(
|
||||||
|
options,
|
||||||
|
offsetFromRoot(options.appProjectRoot),
|
||||||
|
false
|
||||||
|
),
|
||||||
|
mainServer: `./server.ts`,
|
||||||
|
},
|
||||||
|
port: Number(options?.devServerPort) || 4200,
|
||||||
|
appName,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...options,
|
||||||
|
port: Number(options?.devServerPort) || 4200,
|
||||||
|
appName,
|
||||||
|
tmpl: '',
|
||||||
|
browserBuildOutputPath: project.targets.build?.options?.outputPath,
|
||||||
|
serverBuildOutputPath: project.targets.server?.options?.outputPath,
|
||||||
|
};
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
joinPathFragments(__dirname, `../files/${pathToModuleFederationSsrFiles}`),
|
joinPathFragments(__dirname, `../files/${pathToModuleFederationSsrFiles}`),
|
||||||
project.root,
|
project.root,
|
||||||
{
|
templateVariables
|
||||||
...options,
|
|
||||||
port: Number(options?.devServerPort) || 4200,
|
|
||||||
appName,
|
|
||||||
tmpl: '',
|
|
||||||
browserBuildOutputPath: project.targets.build.options.outputPath,
|
|
||||||
serverBuildOutputPath: project.targets.server.options.outputPath,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// For hosts to use when running remotes in static mode.
|
// For hosts to use when running remotes in static mode.
|
||||||
const originalOutputPath = project.targets.build?.options?.outputPath;
|
const originalOutputPath =
|
||||||
|
project.targets.build?.options?.outputPath ??
|
||||||
|
options.isUsingTsSolutionConfig
|
||||||
|
? 'dist'
|
||||||
|
: joinPathFragments(
|
||||||
|
offsetFromRoot(options.appProjectRoot),
|
||||||
|
'dist',
|
||||||
|
options.appProjectRoot != '.'
|
||||||
|
? options.appProjectRoot
|
||||||
|
: options.projectName
|
||||||
|
);
|
||||||
const serverOptions = project.targets.server?.options;
|
const serverOptions = project.targets.server?.options;
|
||||||
const serverOutputPath =
|
const serverOutputPath =
|
||||||
serverOptions?.outputPath ??
|
serverOptions?.outputPath ??
|
||||||
@ -66,6 +103,10 @@ export async function setupSsrForRemote(
|
|||||||
tree,
|
tree,
|
||||||
{
|
{
|
||||||
'@module-federation/node': moduleFederationNodeVersion,
|
'@module-federation/node': moduleFederationNodeVersion,
|
||||||
|
cors: corsVersion,
|
||||||
|
isbot: isbotVersion,
|
||||||
|
express: expressVersion,
|
||||||
|
'@types/express': typesExpressVersion,
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -242,7 +242,9 @@ describe('remote generator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mainFile = tree.read('test/server.ts', 'utf-8');
|
const mainFile = tree.read('test/server.ts', 'utf-8');
|
||||||
expect(mainFile).toContain(`join(process.cwd(), 'dist/test/browser')`);
|
expect(mainFile).toContain(
|
||||||
|
`join(process.cwd(), '../dist/test', 'browser')`
|
||||||
|
);
|
||||||
expect(mainFile).toContain('nx.server.ready');
|
expect(mainFile).toContain('nx.server.ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -262,14 +264,10 @@ describe('remote generator', () => {
|
|||||||
bundler: 'rspack',
|
bundler: 'rspack',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('test/rspack.server.config.js')).toBeTruthy();
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists('test/module-federation.server.config.js')
|
tree.exists('test/module-federation.server.config.js')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/rspack.server.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
expect(
|
||||||
tree.read('test/module-federation.server.config.js', 'utf-8')
|
tree.read('test/module-federation.server.config.js', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
@ -291,14 +289,10 @@ describe('remote generator', () => {
|
|||||||
bundler: 'rspack',
|
bundler: 'rspack',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('test/rspack.server.config.ts')).toBeTruthy();
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists('test/module-federation.server.config.ts')
|
tree.exists('test/module-federation.server.config.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/rspack.server.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
expect(
|
||||||
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
GeneratorCallback,
|
GeneratorCallback,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
names,
|
names,
|
||||||
|
offsetFromRoot,
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
stripIndents,
|
stripIndents,
|
||||||
@ -31,16 +32,33 @@ import {
|
|||||||
nxVersion,
|
nxVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { ensureRootProjectName } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
import { ensureRootProjectName } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
|
import {
|
||||||
|
createNxRspackPluginOptions,
|
||||||
|
getDefaultTemplateVariables,
|
||||||
|
} from '../application/lib/create-application-files';
|
||||||
|
|
||||||
export function addModuleFederationFiles(
|
export function addModuleFederationFiles(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: NormalizedSchema<Schema>
|
options: NormalizedSchema<Schema>
|
||||||
) {
|
) {
|
||||||
const templateVariables = {
|
const templateVariables =
|
||||||
...names(options.projectName),
|
options.bundler === 'rspack'
|
||||||
...options,
|
? {
|
||||||
tmpl: '',
|
...getDefaultTemplateVariables(host, options),
|
||||||
};
|
rspackPluginOptions: {
|
||||||
|
...createNxRspackPluginOptions(
|
||||||
|
options,
|
||||||
|
offsetFromRoot(options.appProjectRoot),
|
||||||
|
false
|
||||||
|
),
|
||||||
|
mainServer: `./server.ts`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...names(options.projectName),
|
||||||
|
...options,
|
||||||
|
tmpl: '',
|
||||||
|
};
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
@ -106,8 +124,8 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
|
|||||||
? false
|
? false
|
||||||
: schema.typescriptConfiguration ?? true,
|
: schema.typescriptConfiguration ?? true,
|
||||||
dynamic: schema.dynamic ?? false,
|
dynamic: schema.dynamic ?? false,
|
||||||
// TODO(colum): remove when MF works with Crystal
|
// TODO(colum): remove when Webpack MF works with Crystal
|
||||||
addPlugin: false,
|
addPlugin: !schema.bundler || schema.bundler === 'rspack' ? true : false,
|
||||||
bundler: schema.bundler ?? 'rspack',
|
bundler: schema.bundler ?? 'rspack',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -170,13 +188,15 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
|
|||||||
setupTspathForRemote(host, options);
|
setupTspathForRemote(host, options);
|
||||||
|
|
||||||
if (options.ssr) {
|
if (options.ssr) {
|
||||||
const setupSsrTask = await setupSsrGenerator(host, {
|
if (options.bundler !== 'rspack') {
|
||||||
project: options.projectName,
|
const setupSsrTask = await setupSsrGenerator(host, {
|
||||||
serverPort: options.devServerPort,
|
project: options.projectName,
|
||||||
skipFormat: true,
|
serverPort: options.devServerPort,
|
||||||
bundler: options.bundler,
|
skipFormat: true,
|
||||||
});
|
bundler: options.bundler,
|
||||||
tasks.push(setupSsrTask);
|
});
|
||||||
|
tasks.push(setupSsrTask);
|
||||||
|
}
|
||||||
|
|
||||||
const setupSsrForRemoteTask = await setupSsrForRemote(
|
const setupSsrForRemoteTask = await setupSsrForRemote(
|
||||||
host,
|
host,
|
||||||
@ -186,20 +206,13 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
|
|||||||
tasks.push(setupSsrForRemoteTask);
|
tasks.push(setupSsrForRemoteTask);
|
||||||
|
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
if (options.bundler === 'rspack') {
|
if (options.bundler !== 'rspack') {
|
||||||
projectConfig.targets.server.executor = '@nx/rspack:rspack';
|
|
||||||
projectConfig.targets.server.options.rspackConfig = joinPathFragments(
|
|
||||||
projectConfig.root,
|
|
||||||
`rspack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
|
||||||
);
|
|
||||||
delete projectConfig.targets.server.options.webpackConfig;
|
|
||||||
} else {
|
|
||||||
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
);
|
);
|
||||||
|
updateProjectConfiguration(host, options.projectName, projectConfig);
|
||||||
}
|
}
|
||||||
updateProjectConfiguration(host, options.projectName, projectConfig);
|
|
||||||
}
|
}
|
||||||
if (!options.setParserOptionsProject) {
|
if (!options.setParserOptionsProject) {
|
||||||
host.delete(
|
host.delete(
|
||||||
|
|||||||
@ -20,33 +20,13 @@ export function updateModuleFederationProject(
|
|||||||
typescriptConfiguration?: boolean;
|
typescriptConfiguration?: boolean;
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
bundler?: 'rspack' | 'webpack';
|
bundler?: 'rspack' | 'webpack';
|
||||||
}
|
ssr?: boolean;
|
||||||
|
},
|
||||||
|
isHost = false
|
||||||
) {
|
) {
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
|
|
||||||
if (options.bundler === 'rspack') {
|
if (options.bundler !== 'rspack') {
|
||||||
projectConfig.targets.build.executor = '@nx/rspack:rspack';
|
|
||||||
projectConfig.targets.build.options = {
|
|
||||||
...(projectConfig.targets.build.options ?? {}),
|
|
||||||
main: maybeJs(
|
|
||||||
{ js: options.js, useJsx: true },
|
|
||||||
`${options.appProjectRoot}/src/main.ts`
|
|
||||||
),
|
|
||||||
rspackConfig: `${options.appProjectRoot}/rspack.config.${
|
|
||||||
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
|
||||||
}`,
|
|
||||||
target: 'web',
|
|
||||||
};
|
|
||||||
|
|
||||||
projectConfig.targets.build.configurations ??= {};
|
|
||||||
|
|
||||||
projectConfig.targets.build.configurations.production = {
|
|
||||||
...(projectConfig.targets.build.configurations?.production ?? {}),
|
|
||||||
rspackConfig: `${options.appProjectRoot}/rspack.config.prod.${
|
|
||||||
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
|
||||||
}`,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
projectConfig.targets.build.options = {
|
projectConfig.targets.build.options = {
|
||||||
...(projectConfig.targets.build.options ?? {}),
|
...(projectConfig.targets.build.options ?? {}),
|
||||||
main: maybeJs(options, `${options.appProjectRoot}/src/main.ts`),
|
main: maybeJs(options, `${options.appProjectRoot}/src/main.ts`),
|
||||||
@ -67,20 +47,7 @@ export function updateModuleFederationProject(
|
|||||||
|
|
||||||
// If host should be configured to use dynamic federation
|
// If host should be configured to use dynamic federation
|
||||||
if (options.dynamic) {
|
if (options.dynamic) {
|
||||||
if (options.bundler === 'rspack') {
|
if (options.bundler !== 'rspack') {
|
||||||
const pathToProdRspackConfig = joinPathFragments(
|
|
||||||
projectConfig.root,
|
|
||||||
`rspack.prod.config.${
|
|
||||||
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
if (host.exists(pathToProdRspackConfig)) {
|
|
||||||
host.delete(pathToProdRspackConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete projectConfig.targets.build.configurations.production
|
|
||||||
?.rspackConfig;
|
|
||||||
} else {
|
|
||||||
const pathToProdWebpackConfig = joinPathFragments(
|
const pathToProdWebpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
`webpack.prod.config.${
|
`webpack.prod.config.${
|
||||||
@ -96,38 +63,41 @@ export function updateModuleFederationProject(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.bundler === 'rspack') {
|
if (options.bundler !== 'rspack') {
|
||||||
projectConfig.targets.serve.executor =
|
|
||||||
'@nx/rspack:module-federation-dev-server';
|
|
||||||
} else {
|
|
||||||
projectConfig.targets.serve.executor =
|
projectConfig.targets.serve.executor =
|
||||||
'@nx/react:module-federation-dev-server';
|
'@nx/react:module-federation-dev-server';
|
||||||
}
|
}
|
||||||
projectConfig.targets.serve.options.port = options.devServerPort;
|
projectConfig.targets.serve ??= {};
|
||||||
|
projectConfig.targets.serve.options ??= {};
|
||||||
|
projectConfig.targets.serve.options.port =
|
||||||
|
options.bundler === 'rspack' && options.ssr && isHost
|
||||||
|
? 4000
|
||||||
|
: options.devServerPort;
|
||||||
|
|
||||||
// `serve-static` for remotes that don't need to be in development mode
|
// `serve-static` for remotes that don't need to be in development mode
|
||||||
const serveStaticExecutor =
|
if (options.bundler !== 'rspack') {
|
||||||
options.bundler === 'rspack'
|
const serveStaticExecutor = '@nx/react:module-federation-static-server';
|
||||||
? '@nx/rspack:module-federation-static-server'
|
projectConfig.targets['serve-static'] = {
|
||||||
: '@nx/react:module-federation-static-server';
|
executor: serveStaticExecutor,
|
||||||
projectConfig.targets['serve-static'] = {
|
defaultConfiguration: 'production',
|
||||||
executor: serveStaticExecutor,
|
options: {
|
||||||
defaultConfiguration: 'production',
|
serveTarget: `${options.projectName}:serve`,
|
||||||
options: {
|
|
||||||
serveTarget: `${options.projectName}:serve`,
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
development: {
|
|
||||||
serveTarget: `${options.projectName}:serve:development`,
|
|
||||||
},
|
},
|
||||||
production: {
|
configurations: {
|
||||||
serveTarget: `${options.projectName}:serve:production`,
|
development: {
|
||||||
|
serveTarget: `${options.projectName}:serve:development`,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
serveTarget: `${options.projectName}:serve:production`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
// Typechecks must be performed first before build and serve to generate remote d.ts files.
|
// Typechecks must be performed first before build and serve to generate remote d.ts files.
|
||||||
if (isUsingTsSolutionSetup(host)) {
|
if (isUsingTsSolutionSetup(host)) {
|
||||||
|
projectConfig.targets.build ??= {};
|
||||||
|
projectConfig.targets.serve ??= {};
|
||||||
projectConfig.targets.build.dependsOn = ['^build', 'typecheck'];
|
projectConfig.targets.build.dependsOn = ['^build', 'typecheck'];
|
||||||
projectConfig.targets.serve.dependsOn = ['typecheck'];
|
projectConfig.targets.serve.dependsOn = ['typecheck'];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@
|
|||||||
"@module-federation/node",
|
"@module-federation/node",
|
||||||
// @nx/workspace is only required in < 15.8
|
// @nx/workspace is only required in < 15.8
|
||||||
"@nx/workspace",
|
"@nx/workspace",
|
||||||
|
"@nx/react",
|
||||||
// Imported types only
|
// Imported types only
|
||||||
"@module-federation/sdk",
|
"@module-federation/sdk",
|
||||||
"@module-federation/enhanced",
|
"@module-federation/enhanced",
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
import { readProjectConfiguration } from '@nx/devkit';
|
import { readProjectConfiguration } from '@nx/devkit';
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
|
|||||||
@ -2,7 +2,9 @@ import {
|
|||||||
addDependenciesToPackageJson,
|
addDependenciesToPackageJson,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
getProjects,
|
getProjects,
|
||||||
|
readNxJson,
|
||||||
type Tree,
|
type Tree,
|
||||||
|
updateNxJson,
|
||||||
updateProjectConfiguration,
|
updateProjectConfiguration,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
@ -78,6 +80,37 @@ export default async function (tree: Tree, options: Schema) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateProjectConfiguration(tree, options.project, project);
|
updateProjectConfiguration(tree, options.project, project);
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
if (nxJson.plugins !== undefined && nxJson.plugins.length > 0) {
|
||||||
|
const nonRspackPlugins = nxJson.plugins.filter(
|
||||||
|
(plugin) =>
|
||||||
|
(typeof plugin !== 'string' && plugin.plugin !== '@nx/rspack/plugin') ||
|
||||||
|
(typeof plugin === 'string' && plugin !== '@nx/rspack/plugin')
|
||||||
|
);
|
||||||
|
let rspackPlugins = nxJson.plugins.filter(
|
||||||
|
(plugin) =>
|
||||||
|
(typeof plugin !== 'string' && plugin.plugin === '@nx/rspack/plugin') ||
|
||||||
|
(typeof plugin === 'string' && plugin === '@nx/rspack/plugin')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rspackPlugins.length === 0) {
|
||||||
|
rspackPlugins = rspackPlugins.map((plugin) => {
|
||||||
|
if (typeof plugin === 'string') {
|
||||||
|
return {
|
||||||
|
plugin: plugin,
|
||||||
|
exclude: [`${project.root}/*`],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...plugin,
|
||||||
|
exclude: [...(plugin.exclude ?? []), `${project.root}/*`],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
nxJson.plugins = [...nonRspackPlugins, ...rspackPlugins];
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
const installTask = addDependenciesToPackageJson(
|
const installTask = addDependenciesToPackageJson(
|
||||||
tree,
|
tree,
|
||||||
{},
|
{},
|
||||||
|
|||||||
@ -4,4 +4,5 @@ export * from './utils/config';
|
|||||||
export * from './utils/with-nx';
|
export * from './utils/with-nx';
|
||||||
export * from './utils/with-react';
|
export * from './utils/with-react';
|
||||||
export * from './utils/with-web';
|
export * from './utils/with-web';
|
||||||
|
export * from './utils/e2e-web-server-info-utils';
|
||||||
export * from './plugins/use-legacy-nx-plugin/use-legacy-nx-plugin';
|
export * from './plugins/use-legacy-nx-plugin/use-legacy-nx-plugin';
|
||||||
|
|||||||
39
packages/rspack/src/utils/e2e-web-server-info-utils.ts
Normal file
39
packages/rspack/src/utils/e2e-web-server-info-utils.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { type Tree, readNxJson } from '@nx/devkit';
|
||||||
|
import { getE2EWebServerInfo } from '@nx/devkit/src/generators/e2e-web-server-info-utils';
|
||||||
|
|
||||||
|
export async function getRspackE2EWebServerInfo(
|
||||||
|
tree: Tree,
|
||||||
|
projectName: string,
|
||||||
|
configFilePath: string,
|
||||||
|
isPluginBeingAdded: boolean,
|
||||||
|
e2ePortOverride?: number
|
||||||
|
) {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
let e2ePort = e2ePortOverride ?? 4200;
|
||||||
|
|
||||||
|
if (
|
||||||
|
nxJson.targetDefaults?.['serve'] &&
|
||||||
|
nxJson.targetDefaults?.['serve'].options?.port
|
||||||
|
) {
|
||||||
|
e2ePort = nxJson.targetDefaults?.['serve'].options?.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getE2EWebServerInfo(
|
||||||
|
tree,
|
||||||
|
projectName,
|
||||||
|
{
|
||||||
|
plugin: '@nx/rspack/plugin',
|
||||||
|
serveTargetName: 'serveTargetName',
|
||||||
|
serveStaticTargetName: 'previewTargetName',
|
||||||
|
configFilePath,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
defaultServeTargetName: 'serve',
|
||||||
|
defaultServeStaticTargetName: 'preview',
|
||||||
|
defaultE2EWebServerAddress: `http://localhost:${e2ePort}`,
|
||||||
|
defaultE2ECiBaseUrl: 'http://localhost:4200',
|
||||||
|
defaultE2EPort: e2ePort,
|
||||||
|
},
|
||||||
|
isPluginBeingAdded
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user