Skip to content

Commit 6858cb9

Browse files
fix: stabilize loadable-react-16 e2e startup (#4392)
1 parent 9187b5c commit 6858cb9

File tree

2 files changed

+143
-2
lines changed

2 files changed

+143
-2
lines changed

loadable-react-16/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
"ignored": true,
44
"version": "0.0.1",
55
"scripts": {
6-
"start": "pnpm --filter loadable-react-16_* --parallel start",
6+
"start": "node scripts/run-servers.cjs",
77
"build": "pnpm --filter loadable-react-16_* build",
8-
"serve": "pnpm --filter loadable-react-16_* --parallel serve",
8+
"serve": "node scripts/run-servers.cjs --skip-build",
99
"clean": "pnpm --filter loadable-react-16_* --parallel clean",
1010
"e2e:ci": "pnpm exec playwright test"
1111
},
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env node
2+
const { spawn } = require('child_process');
3+
const waitOn = require('wait-on');
4+
5+
const skipBuild = process.argv.includes('--skip-build');
6+
const isWindows = process.platform === 'win32';
7+
const longRunningProcesses = [];
8+
let isShuttingDown = false;
9+
10+
process.on('SIGINT', () => shutdown(0));
11+
process.on('SIGTERM', () => shutdown(0));
12+
13+
function spawnPnpmProcess(args) {
14+
return spawn('pnpm', args, {
15+
stdio: 'inherit',
16+
shell: isWindows,
17+
});
18+
}
19+
20+
function runCommand(args, label) {
21+
return new Promise((resolve, reject) => {
22+
const command = spawnPnpmProcess(args);
23+
24+
command.on('error', error => {
25+
reject(new Error(`${label} failed to start: ${error.message}`));
26+
});
27+
28+
command.on('exit', code => {
29+
if (code === 0) {
30+
resolve();
31+
return;
32+
}
33+
34+
reject(new Error(`${label} exited with code ${code ?? 0}`));
35+
});
36+
});
37+
}
38+
39+
function spawnServer(args, label) {
40+
const serverProcess = spawnPnpmProcess(args);
41+
42+
longRunningProcesses.push(serverProcess);
43+
44+
serverProcess.on('error', error => {
45+
console.error(`${label} failed: ${error.message}`);
46+
shutdown(1);
47+
});
48+
49+
serverProcess.on('exit', code => {
50+
if (isShuttingDown) {
51+
return;
52+
}
53+
54+
const exitCode = typeof code === 'number' ? code : 1;
55+
console.error(`${label} exited with code ${exitCode}`);
56+
shutdown(exitCode === 0 ? 0 : exitCode);
57+
});
58+
59+
return serverProcess;
60+
}
61+
62+
function waitForResources(resources) {
63+
return new Promise((resolve, reject) => {
64+
waitOn(
65+
{
66+
resources,
67+
interval: 200,
68+
timeout: 120_000,
69+
validateStatus: status => status >= 200 && status < 500,
70+
},
71+
error => {
72+
if (error) {
73+
reject(error);
74+
return;
75+
}
76+
77+
resolve();
78+
},
79+
);
80+
});
81+
}
82+
83+
function shutdown(code = 0) {
84+
if (isShuttingDown) {
85+
return;
86+
}
87+
88+
isShuttingDown = true;
89+
90+
const terminationPromises = longRunningProcesses.map(child => {
91+
if (child.exitCode !== null || child.killed) {
92+
return Promise.resolve();
93+
}
94+
95+
return new Promise(resolve => {
96+
child.once('exit', resolve);
97+
98+
if (!child.killed) {
99+
child.kill('SIGTERM');
100+
}
101+
102+
const forceKillTimeout = setTimeout(() => {
103+
if (child.exitCode === null && !child.killed) {
104+
child.kill('SIGKILL');
105+
}
106+
clearTimeout(forceKillTimeout);
107+
}, 5000);
108+
});
109+
});
110+
111+
Promise.allSettled(terminationPromises).finally(() => {
112+
process.exit(code);
113+
});
114+
}
115+
116+
async function main() {
117+
try {
118+
if (!skipBuild) {
119+
console.log('Building loadable-react-16 applications...');
120+
await runCommand(['--filter', 'loadable-react-16_*', 'build'], 'Build command');
121+
console.log('Build completed successfully.');
122+
}
123+
124+
console.log('Starting loadable-react-16_app2 server...');
125+
spawnServer(['--filter', 'loadable-react-16_app2', 'serve'], 'loadable-react-16_app2 serve');
126+
127+
console.log('Waiting for app2 remote assets to become available...');
128+
await waitForResources([
129+
'http-get://localhost:3001/server/remoteEntry.js',
130+
'http-get://localhost:3001/static/federation-stats.json',
131+
]);
132+
console.log('App2 remote assets are available. Starting loadable-react-16_app1 server...');
133+
134+
spawnServer(['--filter', 'loadable-react-16_app1', 'serve'], 'loadable-react-16_app1 serve');
135+
} catch (error) {
136+
console.error(error && error.message ? error.message : error);
137+
shutdown(1);
138+
}
139+
}
140+
141+
main();

0 commit comments

Comments
 (0)