-
-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathstart-dev-server.ts
124 lines (112 loc) · 4.17 KB
/
start-dev-server.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
112
113
114
115
116
117
118
119
120
121
122
123
124
import * as fs from 'fs-extra';
import { resolve } from 'path';
import webpack = require('webpack');
const Server = require('webpack-dev-server/lib/Server');
import { Callback } from './serve';
import { BACKEND_TARGET, FRONTEND_TARGET } from '@celljs/cli-common/lib/constants';
import * as delay from 'delay';
import { ConfigurationContext } from '../context/context-protocol';
const clearModule = require('clear-module');
import { importx } from 'importx-tsup';
let server: any;
let moduleCaches: string[] = [];
let mounted = false;
function createCompiler(configuration: webpack.Configuration, options: any, log: any) {
try {
return webpack(configuration);
} catch (err) {
if (err instanceof (webpack as any).WebpackOptionsValidationError) {
log.error(err.message);
process.exit(1);
}
throw err;
}
}
function getEntryPath(configuration: webpack.Configuration) {
const { path } = configuration.output as any;
return resolve(path as string, 'index.js');
}
function mountRuntimeModuleCaches() {
if (mounted) {
return;
}
mounted = true;
const BuiltinModule = require('module');
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule;
const originResolveFilename = Module._resolveFilename.bind(Module);
Module._resolveFilename = (...args: any[]) => {
const filename = originResolveFilename(...args);
if (!moduleCaches.includes(filename)) {
moduleCaches.push(filename);
}
return filename;
};
}
function clearRuntimeModuleCaches() {
for (const cache of moduleCaches) {
clearModule.single(cache);
}
moduleCaches = [];
}
async function attachBackendServer(ctx: ConfigurationContext, callback: Callback, configuration: webpack.Configuration, options: any, log: any, c?: webpack.Compiler) {
const compiler = c || createCompiler(configuration, options, log);
if (!c) {
compiler.watch(options.watchOptions, err => {
if (err) {
log.error(err.stack || err);
}
});
}
const entryContextProvider = async () => {
const entryPath = getEntryPath(configuration);
clearRuntimeModuleCaches();
while (true) {
if (fs.existsSync(entryPath)) {
mountRuntimeModuleCaches();
return importx(entryPath, __filename);
}
await delay(200);
}
};
await callback(server.server, server.app, compiler, entryContextProvider);
}
async function doStartDevServer(ctx: ConfigurationContext, configurations: webpack.Configuration[], options: any, callback: Callback) {
let frontendConfiguration: webpack.Configuration | undefined;
let backendConfiguration: webpack.Configuration | undefined;
for (const c of configurations) {
if (c.name === FRONTEND_TARGET) {
frontendConfiguration = c;
} else {
backendConfiguration = c;
}
}
const configuration = frontendConfiguration || backendConfiguration;
if (!configuration) {
console.error('No suitable target found.');
process.exit(-1);
}
const compiler = createCompiler(configuration, options, console);
try {
server = new Server(options, compiler);
await server.start();
if (frontendConfiguration && backendConfiguration) {
await attachBackendServer(ctx, callback, backendConfiguration, options, console);
} else if (configuration.name === BACKEND_TARGET) {
await attachBackendServer(ctx, callback, configuration, options, console, compiler);
}
process.send!({ type: 'address', data: server.server?.address() });
} catch (err) {
if (err.name === 'ValidationError') {
console.error(err.message);
} else {
console.error(err);
}
process.exit(2);
}
return compiler;
}
export function startDevServer(ctx: ConfigurationContext, callback: Callback) {
const cs = ctx.configurations.map(c => c.toConfig());
const devServer = (cs[cs.length - 1] as any).devServer;
return doStartDevServer(ctx, cs, { ...devServer }, callback);
}