Skip to content

Commit 055333e

Browse files
committed
refactor: apply ops on child compilers directly
1 parent 324b733 commit 055333e

File tree

2 files changed

+217
-13
lines changed

2 files changed

+217
-13
lines changed

lib/Server.js

Lines changed: 209 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Server {
5050
this.logger = this.compiler.getInfrastructureLogger(this.name);
5151

5252
normalizeOptions(this.compiler, this.options);
53-
this.applyDevServerPlugin();
53+
this.configureChildCompilers();
5454

5555
this.SocketServerImplementation = getSocketServerImplementation(
5656
this.options
@@ -84,14 +84,218 @@ class Server {
8484
}, this);
8585
}
8686

87-
applyDevServerPlugin() {
88-
const DevServerPlugin = require('./utils/DevServerPlugin');
87+
configureChildCompilers() {
88+
const {
89+
EntryPlugin,
90+
ProvidePlugin,
91+
HotModuleReplacementPlugin,
92+
} = require('webpack');
93+
const createDomain = require('./utils/createDomain');
94+
const getSocketClientPath = require('./utils/getSocketClientPath');
8995

9096
const compilers = this.compiler.compilers || [this.compiler];
97+
const options = this.options;
9198

92-
// eslint-disable-next-line no-shadow
9399
compilers.forEach((compiler) => {
94-
new DevServerPlugin(this.options).apply(compiler);
100+
/** @type {string} */
101+
const domain = createDomain(options);
102+
103+
// SockJS is not supported server mode, so `host` and `port` can't specified, let's ignore them
104+
// TODO show warning about this
105+
const isSockJSType = options.webSocketServer.type === 'sockjs';
106+
107+
/** @type {string} */
108+
let hostString = '';
109+
110+
if (options.client && options.client.host) {
111+
hostString = `&host=${options.client.host}`;
112+
} else if (options.webSocketServer.options.host && !isSockJSType) {
113+
hostString = `&host=${options.webSocketServer.options.host}`;
114+
}
115+
116+
/** @type {string} */
117+
let portString = '';
118+
119+
if (options.client && options.client.port) {
120+
portString = `&port=${options.client.port}`;
121+
} else if (options.webSocketServer.options.port && !isSockJSType) {
122+
portString = `&port=${options.webSocketServer.options.port}`;
123+
} else {
124+
portString = `&port=${options.port}`;
125+
}
126+
127+
/** @type {string} */
128+
let pathString = '';
129+
130+
// Only add the path if it is not default
131+
if (options.client && options.client.path && options.client.path) {
132+
pathString = `&path=${options.client.path}`;
133+
} else if (options.webSocketServer.options.path) {
134+
pathString = `&path=${options.webSocketServer.options.path}`;
135+
}
136+
137+
/** @type {string} */
138+
const logging =
139+
options.client && options.client.logging
140+
? `&logging=${options.client.logging}`
141+
: '';
142+
143+
/** @type {string} */
144+
const clientEntry = `${require.resolve(
145+
'../../client/index.js'
146+
)}?${domain}${hostString}${pathString}${portString}${logging}`;
147+
148+
/** @type {(string[] | string)} */
149+
let hotEntry;
150+
151+
if (options.hot === 'only') {
152+
hotEntry = require.resolve('webpack/hot/only-dev-server');
153+
} else if (options.hot) {
154+
hotEntry = require.resolve('webpack/hot/dev-server');
155+
}
156+
/**
157+
* prependEntry Method for webpack 4
158+
* @param {Entry} originalEntry
159+
* @param {Entry} additionalEntries
160+
* @returns {Entry}
161+
*/
162+
const prependEntry = (originalEntry, additionalEntries) => {
163+
if (typeof originalEntry === 'function') {
164+
return () =>
165+
Promise.resolve(originalEntry()).then((entry) =>
166+
prependEntry(entry, additionalEntries)
167+
);
168+
}
169+
170+
if (
171+
typeof originalEntry === 'object' &&
172+
!Array.isArray(originalEntry)
173+
) {
174+
/** @type {Object<string,string>} */
175+
const clone = {};
176+
177+
Object.keys(originalEntry).forEach((key) => {
178+
// entry[key] should be a string here
179+
const entryDescription = originalEntry[key];
180+
clone[key] = prependEntry(entryDescription, additionalEntries);
181+
});
182+
183+
return clone;
184+
}
185+
186+
// in this case, entry is a string or an array.
187+
// make sure that we do not add duplicates.
188+
/** @type {Entry} */
189+
const entriesClone = additionalEntries.slice(0);
190+
[].concat(originalEntry).forEach((newEntry) => {
191+
if (!entriesClone.includes(newEntry)) {
192+
entriesClone.push(newEntry);
193+
}
194+
});
195+
return entriesClone;
196+
};
197+
198+
/**
199+
*
200+
* Description of the option for checkInject method
201+
* @typedef {Function} checkInjectOptionsParam
202+
* @param {Object} _config - compilerConfig
203+
* @return {Boolean}
204+
*/
205+
206+
/**
207+
*
208+
* @param {Boolean | checkInjectOptionsParam} option - inject(Hot|Client) it is Boolean | fn => Boolean
209+
* @param {Object} _config
210+
* @param {Boolean} defaultValue
211+
* @return {Boolean}
212+
*/
213+
// eslint-disable-next-line no-shadow
214+
const checkInject = (option, _config, defaultValue) => {
215+
if (typeof option === 'boolean') {
216+
return option;
217+
}
218+
219+
if (typeof option === 'function') {
220+
return option(_config);
221+
}
222+
223+
return defaultValue;
224+
};
225+
226+
const compilerOptions = compiler.options;
227+
228+
compilerOptions.plugins = compilerOptions.plugins || [];
229+
230+
/** @type {boolean} */
231+
const isWebTarget = compilerOptions.externalsPresets
232+
? compilerOptions.externalsPresets.web
233+
: [
234+
'web',
235+
'webworker',
236+
'electron-renderer',
237+
'node-webkit',
238+
// eslint-disable-next-line no-undefined
239+
undefined,
240+
null,
241+
].includes(compilerOptions.target);
242+
243+
/** @type {Entry} */
244+
const additionalEntries = checkInject(
245+
options.client ? options.client.needClientEntry : null,
246+
compilerOptions,
247+
isWebTarget
248+
)
249+
? [clientEntry]
250+
: [];
251+
252+
if (
253+
hotEntry &&
254+
checkInject(
255+
options.client ? options.client.hotEntry : null,
256+
compilerOptions,
257+
true
258+
)
259+
) {
260+
additionalEntries.push(hotEntry);
261+
}
262+
263+
// use a hook to add entries if available
264+
if (EntryPlugin) {
265+
for (const additionalEntry of additionalEntries) {
266+
new EntryPlugin(compiler.context, additionalEntry, {
267+
// eslint-disable-next-line no-undefined
268+
name: undefined,
269+
}).apply(compiler);
270+
}
271+
} else {
272+
compilerOptions.entry = prependEntry(
273+
compilerOptions.entry || './src',
274+
additionalEntries
275+
);
276+
compiler.hooks.entryOption.call(
277+
compilerOptions.context,
278+
compilerOptions.entry
279+
);
280+
}
281+
282+
const providePlugin = new ProvidePlugin({
283+
__webpack_dev_server_client__: getSocketClientPath(options),
284+
});
285+
286+
providePlugin.apply(compiler);
287+
288+
if (
289+
hotEntry &&
290+
!compilerOptions.plugins.find(
291+
(p) => p.constructor === HotModuleReplacementPlugin
292+
)
293+
) {
294+
// apply the HMR plugin, if it didn't exist before.
295+
const plugin = new HotModuleReplacementPlugin();
296+
297+
plugin.apply(compiler);
298+
}
95299
});
96300
}
97301

lib/utils/DevServerPlugin.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,23 @@ class DevServerPlugin {
3636
const isSockJSType = options.webSocketServer.type === 'sockjs';
3737

3838
/** @type {string} */
39-
let host = '';
39+
let hostString = '';
4040

4141
if (options.client && options.client.host) {
42-
host = `&host=${options.client.host}`;
42+
hostString = `&host=${options.client.host}`;
4343
} else if (options.webSocketServer.options.host && !isSockJSType) {
44-
host = `&host=${options.webSocketServer.options.host}`;
44+
hostString = `&host=${options.webSocketServer.options.host}`;
4545
}
4646

4747
/** @type {string} */
48-
let port = '';
48+
let portString = '';
4949

5050
if (options.client && options.client.port) {
51-
port = `&port=${options.client.port}`;
51+
portString = `&port=${options.client.port}`;
5252
} else if (options.webSocketServer.options.port && !isSockJSType) {
53-
port = `&port=${options.webSocketServer.options.port}`;
53+
portString = `&port=${options.webSocketServer.options.port}`;
5454
} else {
55-
port = `&port=${options.port}`;
55+
portString = `&port=${options.port}`;
5656
}
5757

5858
/** @type {string} */
@@ -74,7 +74,7 @@ class DevServerPlugin {
7474
/** @type {string} */
7575
const clientEntry = `${require.resolve(
7676
'../../client/index.js'
77-
)}?${domain}${host}${path}${port}${logging}`;
77+
)}?${domain}${hostString}${path}${portString}${logging}`;
7878

7979
/** @type {(string[] | string)} */
8080
let hotEntry;

0 commit comments

Comments
 (0)