Skip to content

Commit f432619

Browse files
authored
feat: support component data fetch in ssr mode (#3753)
1 parent f777710 commit f432619

File tree

112 files changed

+4797
-515
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+4797
-515
lines changed

.changeset/rich-bees-applaud.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@module-federation/manifest': patch
3+
---
4+
5+
fix(manifest): record all exposes even if the expose value is the same file

.changeset/spotty-trains-stare.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@module-federation/modern-js': minor
3+
---
4+
5+
feat(modern-js-plugin): support component-level data fetch

apps/manifest-demo/webpack-host/runtimePlugin.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ export default function (): FederationRuntimePlugin {
44
return {
55
name: 'custom-plugin-build',
66
beforeInit(args) {
7+
const { userOptions, origin } = args;
8+
if (origin.options.name && origin.options.name !== userOptions.name) {
9+
userOptions.name = origin.options.name;
10+
}
711
console.log('[build time inject] beforeInit: ', args);
812
return args;
913
},
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# modern-component-data-fetch
2+
3+
## Running Demo
4+
5+
- host: [localhost:5001](http://localhost:5001/)
6+
- provider: [localhost:5002](http://localhost:5002/)
7+
- provider-csr: [localhost:5003](http://localhost:5003/)
8+
9+
## How to start the demos ?
10+
11+
```bash
12+
# Root directory
13+
pnpm i
14+
15+
nx build modern-js-plugin
16+
17+
pnpm run app:component-data-fetch:dev
18+
19+
open http://localhost:5001/
20+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
strict-peer-dependencies=false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# modernjs-ssr-nested-remote
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
2+
import { defineConfig } from 'cypress';
3+
4+
export default defineConfig({
5+
projectId: 'sa6wfn',
6+
e2e: nxE2EPreset(__filename, { cypressDir: 'cypress' }),
7+
defaultCommandTimeout: 20000,
8+
retries: {
9+
runMode: 2,
10+
openMode: 1,
11+
},
12+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getH2 } from '../support/app.po';
2+
3+
describe('csr with fetch data', () => {
4+
it('[ /csr ] - should render in client side and fetch data from server', () => {
5+
cy.visit('/csr');
6+
cy.wait(3000);
7+
cy.url().should('include', '/csr');
8+
getH2().contains('[ csr provider - server ] fetched data');
9+
10+
const stub = cy.stub();
11+
cy.on('window:alert', stub);
12+
13+
cy.get('#provider-csr-btn')
14+
.should('be.visible')
15+
.click()
16+
.then(() => {
17+
expect(stub.getCall(0)).to.be.calledWith(
18+
'[provider-csr-btn] Client side Javascript works!',
19+
);
20+
});
21+
22+
// it only render in client side, so it not have downgrade identifier
23+
cy.window().then((win) => {
24+
expect(win.globalThis._mfSSRDowngrade).to.not.exist;
25+
});
26+
});
27+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { getH1, getH2, getH3 } from '../support/app.po';
2+
3+
declare global {
4+
interface Window {
5+
globalThis: {
6+
_mfSSRDowngrade?: boolean | string[];
7+
};
8+
}
9+
}
10+
11+
describe('downgrade', () => {
12+
beforeEach(() => {
13+
// the page will hydrate failed and downgrade, so catch the error, keep on testing
14+
cy.on('uncaught:exception', (err, runnable) => {
15+
return false;
16+
});
17+
});
18+
19+
it('[ /client-downgrade ] - should downgrade load data.client.js to fetch data', () => {
20+
cy.visit('/client-downgrade');
21+
cy.wait(3000);
22+
cy.url().should('include', '/client-downgrade');
23+
getH2().contains('fetch data from provider client');
24+
25+
const stub = cy.stub();
26+
cy.on('window:alert', stub);
27+
28+
cy.get('#client-downgrade-btn')
29+
.should('be.visible')
30+
.click()
31+
.then(() => {
32+
expect(stub.getCall(0)).to.be.calledWith(
33+
'[client-downgrade] Client side Javascript works!',
34+
);
35+
});
36+
cy.window().then((win) => {
37+
expect(win.globalThis._mfSSRDowngrade).to.exist;
38+
});
39+
});
40+
41+
it('[ /server-downgrade ] - should downgrade and fetch data from server', () => {
42+
cy.visit('/server-downgrade');
43+
cy.wait(3000);
44+
cy.url().should('include', '/server-downgrade');
45+
getH2().contains('[ provider - server - ServerDowngrade]');
46+
47+
const stub = cy.stub();
48+
cy.on('window:alert', stub);
49+
50+
cy.get('#server-downgrade-btn')
51+
.should('be.visible')
52+
.click()
53+
.then(() => {
54+
expect(stub.getCall(0)).to.be.calledWith(
55+
'[server-downgrade] Client side Javascript works!',
56+
);
57+
});
58+
cy.window().then((win) => {
59+
console.log(win);
60+
expect(win.globalThis._mfSSRDowngrade).to.exist;
61+
});
62+
});
63+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { getH1, getH2, getH3 } from '../support/app.po';
2+
3+
declare global {
4+
interface Window {
5+
globalThis: {
6+
_mfSSRDowngrade?: boolean | string[];
7+
};
8+
}
9+
}
10+
11+
describe('/', () => {
12+
beforeEach(() => cy.visit('/'));
13+
14+
describe('Welcome message', () => {
15+
it('should display welcome message', () => {
16+
getH1().contains('Welcome message from host');
17+
});
18+
});
19+
20+
// check that clicking back and forwards in client side routeing still renders the content correctly
21+
describe('Routing checks', () => {
22+
it('[ /basic ] - should render in client side and fetch data from server side', () => {
23+
// wait nav hydration
24+
cy.wait(3000);
25+
cy.get('.basic').parent().click();
26+
cy.url().should('include', '/basic');
27+
getH2().contains('[ provider - client ] fetched data');
28+
29+
const stub = cy.stub();
30+
cy.on('window:alert', stub);
31+
32+
cy.get('#provider-btn')
33+
.should('be.visible')
34+
.click()
35+
.then(() => {
36+
expect(stub.getCall(0)).to.be.calledWith(
37+
'[provider] Client side Javascript works!',
38+
);
39+
});
40+
41+
cy.window().then((win) => {
42+
expect(win.globalThis._mfSSRDowngrade).to.not.exist;
43+
});
44+
});
45+
46+
it('[ /client-downgrade ] - should render in client side and load data.client.js to fetch data', () => {
47+
cy.wait(3000);
48+
cy.get('.client-downgrade').parent().click();
49+
cy.url().should('include', '/client-downgrade');
50+
getH2().contains('fetch data from provider client');
51+
52+
const stub = cy.stub();
53+
cy.on('window:alert', stub);
54+
55+
cy.get('#client-downgrade-btn')
56+
.should('be.visible')
57+
.click()
58+
.then(() => {
59+
expect(stub.getCall(0)).to.be.calledWith(
60+
'[client-downgrade] Client side Javascript works!',
61+
);
62+
});
63+
cy.window().then((win) => {
64+
expect(win.globalThis._mfSSRDowngrade).to.not.exist;
65+
});
66+
});
67+
});
68+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { getH1, getH2, getH3 } from '../support/app.po';
2+
3+
describe('ssr with fetch data', () => {
4+
it('[ /basic ] - should render in server side and fetch data from server', () => {
5+
cy.visit('/basic');
6+
cy.wait(3000);
7+
cy.url().should('include', '/basic');
8+
getH2().contains('[ provider - server ] fetched data');
9+
10+
const stub = cy.stub();
11+
cy.on('window:alert', stub);
12+
13+
cy.get('#provider-btn')
14+
.should('be.visible')
15+
.click()
16+
.then(() => {
17+
expect(stub.getCall(0)).to.be.calledWith(
18+
'[provider] Client side Javascript works!',
19+
);
20+
});
21+
22+
cy.window().then((win) => {
23+
expect(win.globalThis._mfSSRDowngrade).to.not.exist;
24+
});
25+
});
26+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const getH1 = () => cy.get('h1');
2+
export const getH2 = () => cy.get('h2');
3+
export const getH3 = () => cy.get('h3');
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference types="cypress" />
2+
3+
// ***********************************************
4+
// This example commands.ts shows you how to
5+
// create various custom commands and overwrite
6+
// existing commands.
7+
//
8+
// For more comprehensive examples of custom
9+
// commands please read more here:
10+
// https://on.cypress.io/custom-commands
11+
// ***********************************************
12+
13+
// eslint-disable-next-line @typescript-eslint/no-namespace
14+
declare namespace Cypress {
15+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
16+
interface Chainable<Subject> {
17+
login(email: string, password: string): void;
18+
}
19+
}
20+
21+
// -- This is a parent command --
22+
//
23+
// -- This is a child command --
24+
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
25+
//
26+
//
27+
// -- This is a dual command --
28+
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
29+
//
30+
//
31+
// -- This will overwrite an existing command --
32+
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// ***********************************************************
2+
// This example support/e2e.ts is processed and
3+
// loaded automatically before your test files.
4+
//
5+
// This is a great place to put global configuration and
6+
// behavior that modifies Cypress.
7+
//
8+
// You can change the location of this file or turn off
9+
// automatically serving support files with the
10+
// 'supportFile' configuration option.
11+
//
12+
// You can read more here:
13+
// https://on.cypress.io/configuration
14+
// ***********************************************************
15+
16+
// Import commands.ts using ES2015 syntax:
17+
import './commands';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"allowJs": true,
5+
"outDir": "../../dist/out-tsc",
6+
"module": "commonjs",
7+
"types": ["cypress", "node"],
8+
"sourceMap": false
9+
},
10+
"include": [
11+
"**/*.ts",
12+
"**/*.js",
13+
"../cypress.config.ts",
14+
"../**/*.cy.ts",
15+
"../**/*.cy.tsx",
16+
"../**/*.cy.js",
17+
"../**/*.cy.jsx",
18+
"../**/*.d.ts"
19+
]
20+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { appTools, defineConfig } from '@modern-js/app-tools';
2+
import { moduleFederationPlugin } from '@module-federation/modern-js';
3+
4+
// https://modernjs.dev/en/configure/app/usage
5+
export default defineConfig({
6+
runtime: {
7+
router: true,
8+
},
9+
server: {
10+
ssr: {
11+
mode: 'stream',
12+
},
13+
port: 5001,
14+
},
15+
plugins: [
16+
appTools({
17+
bundler: 'rspack',
18+
}),
19+
moduleFederationPlugin({
20+
fetchServerQuery: { extraQuery: true },
21+
}),
22+
],
23+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { createModuleFederationConfig } from '@module-federation/modern-js';
2+
export default createModuleFederationConfig({
3+
name: 'host',
4+
remotes: {
5+
remote: 'provider@http://localhost:5002/mf-manifest.json',
6+
'provider-csr': 'provider_csr@http://localhost:5003/mf-manifest.json',
7+
},
8+
shared: {
9+
react: { singleton: true },
10+
'react-dom': { singleton: true },
11+
},
12+
});

0 commit comments

Comments
 (0)