/
mfe-dev-server.impl.ts
111 lines (97 loc) · 2.79 KB
/
mfe-dev-server.impl.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { ExecutorContext, runExecutor } from '@nrwl/devkit';
import devServerExecutor, {
WebDevServerOptions,
} from '@nrwl/web/src/executors/dev-server/dev-server.impl';
import { join } from 'path';
type MFEDevServerOptions = WebDevServerOptions & {
apps?: string[];
};
export default async function* mfeDevServer(
options: MFEDevServerOptions,
context: ExecutorContext
) {
let iter = devServerExecutor(options, context);
const p = context.workspace.projects[context.projectName];
const mfeConfigPath = join(context.root, p.root, 'mfe.config.js');
let mfeConfig: any;
try {
mfeConfig = require(mfeConfigPath);
} catch {
// TODO(jack): Add a link to guide
throw new Error(
`Could not load ${mfeConfigPath}. Was this project generated with "@nrwl/react:mfe-host"?`
);
}
// Remotes can be specified with a custom location
// e.g.
// ```
// remotes: ['app1', 'http://example.com']
// ```
// This shouldn't happen for local dev, but we support it regardless.
let apps = options.apps ?? mfeConfig.remotes ?? [];
apps = apps.map((a) => (Array.isArray(a) ? a[0] : a));
for (const app of apps) {
iter = combineAsyncIterators(
iter,
await runExecutor(
{
project: app,
target: 'serve',
configuration: context.configurationName,
},
{},
context
)
);
}
return yield* iter;
}
// TODO(jack): Extract this helper
function getNextAsyncIteratorFactory(options) {
return async (asyncIterator, index) => {
try {
const iterator = await asyncIterator.next();
return { index, iterator };
} catch (err) {
if (options.errorCallback) {
options.errorCallback(err, index);
}
if (options.throwError !== false) {
return Promise.reject(err);
}
return { index, iterator: { done: true } };
}
};
}
async function* combineAsyncIterators(
...iterators: { 0: AsyncIterator<any> } & AsyncIterator<any>[]
) {
let [options] = iterators;
if (typeof options.next === 'function') {
options = Object.create(null);
} else {
iterators.shift();
}
const getNextAsyncIteratorValue = getNextAsyncIteratorFactory(options);
try {
const asyncIteratorsValues = new Map(
iterators.map((it, idx) => [idx, getNextAsyncIteratorValue(it, idx)])
);
do {
const { iterator, index } = await Promise.race(
asyncIteratorsValues.values()
);
if (iterator.done) {
asyncIteratorsValues.delete(index);
} else {
yield iterator.value;
asyncIteratorsValues.set(
index,
getNextAsyncIteratorValue(iterators[index], index)
);
}
} while (asyncIteratorsValues.size > 0);
} finally {
await Promise.allSettled(iterators.map((it) => it.return()));
}
}