Skip to content

Commit cf4d0d0

Browse files
mlrawlingsevilebottnawi
authored andcommittedApr 17, 2019
fix: only add client entry to web targets (#1775)
1 parent 58d1682 commit cf4d0d0

File tree

7 files changed

+266
-14
lines changed

7 files changed

+266
-14
lines changed
 

Diff for: ‎lib/options.json

+20
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,26 @@
172172
"inline": {
173173
"type": "boolean"
174174
},
175+
"injectClient": {
176+
"anyOf": [
177+
{
178+
"type": "boolean"
179+
},
180+
{
181+
"instanceof": "Function"
182+
}
183+
]
184+
},
185+
"injectHot": {
186+
"anyOf": [
187+
{
188+
"type": "boolean"
189+
},
190+
{
191+
"instanceof": "Function"
192+
}
193+
]
194+
},
175195
"disableHostCheck": {
176196
"type": "boolean"
177197
},

Diff for: ‎lib/utils/addEntries.js

+42-14
Original file line numberDiff line numberDiff line change
@@ -18,45 +18,73 @@ function addEntries(config, options, server) {
1818
const domain = createDomain(options, app);
1919
const sockPath = options.sockPath ? `&sockPath=${options.sockPath}` : '';
2020
const sockPort = options.sockPort ? `&sockPort=${options.sockPort}` : '';
21-
const entries = [
22-
`${require.resolve('../../client/')}?${domain}${sockPath}${sockPort}`,
23-
];
21+
const clientEntry = `${require.resolve(
22+
'../../client/'
23+
)}?${domain}${sockPath}${sockPort}`;
24+
let hotEntry;
25+
2426
if (options.hotOnly) {
25-
entries.push(require.resolve('webpack/hot/only-dev-server'));
27+
hotEntry = require.resolve('webpack/hot/only-dev-server');
2628
} else if (options.hot) {
27-
entries.push(require.resolve('webpack/hot/dev-server'));
29+
hotEntry = require.resolve('webpack/hot/dev-server');
2830
}
2931

30-
const prependEntry = (entry) => {
31-
if (typeof entry === 'function') {
32-
return () => Promise.resolve(entry()).then(prependEntry);
32+
const prependEntry = (originalEntry, additionalEntries) => {
33+
if (typeof originalEntry === 'function') {
34+
return () =>
35+
Promise.resolve(originalEntry()).then((entry) =>
36+
prependEntry(entry, additionalEntries)
37+
);
3338
}
3439

35-
if (typeof entry === 'object' && !Array.isArray(entry)) {
40+
if (typeof originalEntry === 'object' && !Array.isArray(originalEntry)) {
3641
const clone = {};
3742

38-
Object.keys(entry).forEach((key) => {
43+
Object.keys(originalEntry).forEach((key) => {
3944
// entry[key] should be a string here
40-
clone[key] = prependEntry(entry[key]);
45+
clone[key] = prependEntry(originalEntry[key], additionalEntries);
4146
});
4247

4348
return clone;
4449
}
4550

4651
// in this case, entry is a string or an array.
4752
// make sure that we do not add duplicates.
48-
const entriesClone = entries.slice(0);
49-
[].concat(entry).forEach((newEntry) => {
53+
const entriesClone = additionalEntries.slice(0);
54+
[].concat(originalEntry).forEach((newEntry) => {
5055
if (!entriesClone.includes(newEntry)) {
5156
entriesClone.push(newEntry);
5257
}
5358
});
5459
return entriesClone;
5560
};
5661

62+
// eslint-disable-next-line no-shadow
63+
const checkInject = (option, config, defaultValue) => {
64+
if (typeof option === 'boolean') return option;
65+
if (typeof option === 'function') return option(config);
66+
return defaultValue;
67+
};
68+
5769
// eslint-disable-next-line no-shadow
5870
[].concat(config).forEach((config) => {
59-
config.entry = prependEntry(config.entry || './src');
71+
const webTarget =
72+
config.target === 'web' ||
73+
config.target === 'webworker' ||
74+
config.target == null;
75+
const additionalEntries = checkInject(
76+
options.injectClient,
77+
config,
78+
webTarget
79+
)
80+
? [clientEntry]
81+
: [];
82+
83+
if (hotEntry && checkInject(options.injectHot, config, true)) {
84+
additionalEntries.push(hotEntry);
85+
}
86+
87+
config.entry = prependEntry(config.entry || './src', additionalEntries);
6088

6189
if (options.hot || options.hotOnly) {
6290
config.plugins = config.plugins || [];

Diff for: ‎test/Entry.test.js

+125
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,129 @@ describe('Entry', () => {
277277

278278
expect(typeof webpackOptions.entry === 'function').toBe(true);
279279
});
280+
281+
it('only prepends devServer entry points to web targets by default', () => {
282+
const webpackOptions = [
283+
Object.assign({}, config),
284+
Object.assign({ target: 'web' }, config),
285+
Object.assign({ target: 'webworker' }, config),
286+
Object.assign({ target: 'node' }, config) /* index:3 */,
287+
];
288+
289+
const devServerOptions = {};
290+
291+
addEntries(webpackOptions, devServerOptions);
292+
293+
// eslint-disable-next-line no-shadow
294+
webpackOptions.forEach((webpackOptions, index) => {
295+
const expectInline = index !== 3; /* all but the node target */
296+
297+
expect(webpackOptions.entry.length).toEqual(expectInline ? 2 : 1);
298+
299+
if (expectInline) {
300+
expect(
301+
normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1
302+
).toBeTruthy();
303+
}
304+
305+
expect(normalize(webpackOptions.entry[expectInline ? 1 : 0])).toEqual(
306+
'./foo.js'
307+
);
308+
});
309+
});
310+
311+
it('allows selecting compilations to inline the client into', () => {
312+
const webpackOptions = [
313+
Object.assign({}, config),
314+
Object.assign({ target: 'web' }, config),
315+
Object.assign({ name: 'only-include' }, config) /* index:2 */,
316+
Object.assign({ target: 'node' }, config),
317+
];
318+
319+
const devServerOptions = {
320+
injectClient: (compilerConfig) => compilerConfig.name === 'only-include',
321+
};
322+
323+
addEntries(webpackOptions, devServerOptions);
324+
325+
// eslint-disable-next-line no-shadow
326+
webpackOptions.forEach((webpackOptions, index) => {
327+
const expectInline = index === 2; /* only the "only-include" compiler */
328+
329+
expect(webpackOptions.entry.length).toEqual(expectInline ? 2 : 1);
330+
331+
if (expectInline) {
332+
expect(
333+
normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1
334+
).toBeTruthy();
335+
}
336+
337+
expect(normalize(webpackOptions.entry[expectInline ? 1 : 0])).toEqual(
338+
'./foo.js'
339+
);
340+
});
341+
});
342+
343+
it('when hot, prepends the hot runtime to all targets by default', () => {
344+
const webpackOptions = [
345+
Object.assign({ target: 'web' }, config),
346+
Object.assign({ target: 'node' }, config),
347+
];
348+
349+
const devServerOptions = {
350+
// disable inlining the client so entry indexes match up
351+
// and we can use the same assertions for both configs
352+
injectClient: false,
353+
hot: true,
354+
};
355+
356+
addEntries(webpackOptions, devServerOptions);
357+
358+
// eslint-disable-next-line no-shadow
359+
webpackOptions.forEach((webpackOptions) => {
360+
expect(webpackOptions.entry.length).toEqual(2);
361+
362+
expect(
363+
normalize(webpackOptions.entry[0]).includes('webpack/hot/dev-server')
364+
).toBeTruthy();
365+
366+
expect(normalize(webpackOptions.entry[1])).toEqual('./foo.js');
367+
});
368+
});
369+
370+
it('allows selecting which compilations to inject the hot runtime into', () => {
371+
const webpackOptions = [
372+
Object.assign({ target: 'web' }, config),
373+
Object.assign({ target: 'node' }, config),
374+
];
375+
376+
const devServerOptions = {
377+
injectHot: (compilerConfig) => compilerConfig.target === 'node',
378+
hot: true,
379+
};
380+
381+
addEntries(webpackOptions, devServerOptions);
382+
383+
// node target should have the client runtime but not the hot runtime
384+
const webWebpackOptions = webpackOptions[0];
385+
386+
expect(webWebpackOptions.entry.length).toEqual(2);
387+
388+
expect(
389+
normalize(webWebpackOptions.entry[0]).indexOf('client/index.js?') !== -1
390+
).toBeTruthy();
391+
392+
expect(normalize(webWebpackOptions.entry[1])).toEqual('./foo.js');
393+
394+
// node target should have the hot runtime but not the client runtime
395+
const nodeWebpackOptions = webpackOptions[1];
396+
397+
expect(nodeWebpackOptions.entry.length).toEqual(2);
398+
399+
expect(
400+
normalize(nodeWebpackOptions.entry[0]).includes('webpack/hot/dev-server')
401+
).toBeTruthy();
402+
403+
expect(normalize(nodeWebpackOptions.entry[1])).toEqual('./foo.js');
404+
});
280405
});

Diff for: ‎test/UniversalCompiler.test.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const request = require('supertest');
4+
const helper = require('./helper');
5+
const config = require('./fixtures/universal-compiler-config/webpack.config');
6+
7+
describe('UniversalCompiler', () => {
8+
let server;
9+
let req;
10+
beforeAll((done) => {
11+
server = helper.start(config, { inline: true }, done);
12+
req = request(server.app);
13+
});
14+
15+
afterAll(helper.close);
16+
17+
it('client bundle should have the inlined the client runtime', (done) => {
18+
req
19+
.get('/client.js')
20+
.expect('Content-Type', 'application/javascript; charset=UTF-8')
21+
.expect(200)
22+
.end((err, res) => {
23+
if (err) {
24+
return done(err);
25+
}
26+
expect(res.text).toContain('Hello from the client');
27+
expect(res.text).toContain('webpack-dev-server/client');
28+
done();
29+
});
30+
});
31+
32+
it('server bundle should NOT have the inlined the client runtime', (done) => {
33+
// we wouldn't normally request a server bundle
34+
// but we'll do it here to check the contents
35+
req
36+
.get('/server.js')
37+
.expect('Content-Type', 'application/javascript; charset=UTF-8')
38+
.expect(200)
39+
.end((err, res) => {
40+
if (err) {
41+
return done(err);
42+
}
43+
expect(res.text).toContain('Hello from the server');
44+
expect(res.text).not.toContain('webpack-dev-server/client');
45+
done();
46+
});
47+
});
48+
});

Diff for: ‎test/fixtures/universal-compiler-config/client.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
console.log('Hello from the client');

Diff for: ‎test/fixtures/universal-compiler-config/server.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
console.log('Hello from the server');
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
module.exports = [
4+
{
5+
mode: 'development',
6+
context: __dirname,
7+
entry: './client.js',
8+
output: {
9+
path: '/',
10+
filename: 'client.js',
11+
},
12+
node: false,
13+
},
14+
{
15+
mode: 'development',
16+
context: __dirname,
17+
target: 'node',
18+
entry: './server.js',
19+
output: {
20+
path: '/',
21+
filename: 'server.js',
22+
},
23+
node: false,
24+
},
25+
];

0 commit comments

Comments
 (0)
Please sign in to comment.