From ca0334d15a38424750e5ae393981c3ac06771f08 Mon Sep 17 00:00:00 2001 From: Michael Rawlings Date: Tue, 9 Apr 2019 13:04:26 -0700 Subject: [PATCH] feat: add injectClient and injectHot options --- lib/options.json | 20 +++++++ lib/utils/addEntries.js | 17 +++++- test/Entry.test.js | 125 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 2 deletions(-) diff --git a/lib/options.json b/lib/options.json index e77b6edf06..c704721080 100644 --- a/lib/options.json +++ b/lib/options.json @@ -172,6 +172,26 @@ "inline": { "type": "boolean" }, + "injectClient": { + "anyOf": [ + { + "type": "boolean" + }, + { + "instanceof": "Function" + } + ] + }, + "injectHot": { + "anyOf": [ + { + "type": "boolean" + }, + { + "instanceof": "Function" + } + ] + }, "disableHostCheck": { "type": "boolean" }, diff --git a/lib/utils/addEntries.js b/lib/utils/addEntries.js index 12de4ceb07..9754fbeb00 100644 --- a/lib/utils/addEntries.js +++ b/lib/utils/addEntries.js @@ -59,15 +59,28 @@ function addEntries(config, options, server) { return entriesClone; }; + // eslint-disable-next-line no-shadow + const checkInject = (option, config, defaultValue) => { + if (typeof option === 'boolean') return option; + if (typeof option === 'function') return option(config); + return defaultValue; + }; + // eslint-disable-next-line no-shadow [].concat(config).forEach((config) => { const webTarget = config.target === 'web' || config.target === 'webworker' || config.target == null; - const additionalEntries = webTarget ? [clientEntry] : []; + const additionalEntries = checkInject( + options.injectClient, + config, + webTarget + ) + ? [clientEntry] + : []; - if (hotEntry) { + if (hotEntry && checkInject(options.injectHot, config, true)) { additionalEntries.push(hotEntry); } diff --git a/test/Entry.test.js b/test/Entry.test.js index 0cd67d76ce..7a6f354173 100644 --- a/test/Entry.test.js +++ b/test/Entry.test.js @@ -277,4 +277,129 @@ describe('Entry', () => { expect(typeof webpackOptions.entry === 'function').toBe(true); }); + + it('only prepends devServer entry points to web targets by default', () => { + const webpackOptions = [ + Object.assign({}, config), + Object.assign({ target: 'web' }, config), + Object.assign({ target: 'webworker' }, config), + Object.assign({ target: 'node' }, config) /* index:3 */, + ]; + + const devServerOptions = {}; + + addEntries(webpackOptions, devServerOptions); + + // eslint-disable-next-line no-shadow + webpackOptions.forEach((webpackOptions, index) => { + const expectInline = index !== 3; /* all but the node target */ + + expect(webpackOptions.entry.length).toEqual(expectInline ? 2 : 1); + + if (expectInline) { + expect( + normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + ).toBeTruthy(); + } + + expect(normalize(webpackOptions.entry[expectInline ? 1 : 0])).toEqual( + './foo.js' + ); + }); + }); + + it('allows selecting compilations to inline the client into', () => { + const webpackOptions = [ + Object.assign({}, config), + Object.assign({ target: 'web' }, config), + Object.assign({ name: 'only-include' }, config) /* index:2 */, + Object.assign({ target: 'node' }, config), + ]; + + const devServerOptions = { + injectClient: (compilerConfig) => compilerConfig.name === 'only-include', + }; + + addEntries(webpackOptions, devServerOptions); + + // eslint-disable-next-line no-shadow + webpackOptions.forEach((webpackOptions, index) => { + const expectInline = index === 2; /* only the "only-include" compiler */ + + expect(webpackOptions.entry.length).toEqual(expectInline ? 2 : 1); + + if (expectInline) { + expect( + normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + ).toBeTruthy(); + } + + expect(normalize(webpackOptions.entry[expectInline ? 1 : 0])).toEqual( + './foo.js' + ); + }); + }); + + it('when hot, prepends the hot runtime to all targets by default', () => { + const webpackOptions = [ + Object.assign({ target: 'web' }, config), + Object.assign({ target: 'node' }, config), + ]; + + const devServerOptions = { + // disable inlining the client so entry indexes match up + // and we can use the same assertions for both configs + injectClient: false, + hot: true, + }; + + addEntries(webpackOptions, devServerOptions); + + // eslint-disable-next-line no-shadow + webpackOptions.forEach((webpackOptions) => { + expect(webpackOptions.entry.length).toEqual(2); + + expect( + normalize(webpackOptions.entry[0]).includes('webpack/hot/dev-server') + ).toBeTruthy(); + + expect(normalize(webpackOptions.entry[1])).toEqual('./foo.js'); + }); + }); + + it('allows selecting which compilations to inject the hot runtime into', () => { + const webpackOptions = [ + Object.assign({ target: 'web' }, config), + Object.assign({ target: 'node' }, config), + ]; + + const devServerOptions = { + injectHot: (compilerConfig) => compilerConfig.target === 'node', + hot: true, + }; + + addEntries(webpackOptions, devServerOptions); + + // node target should have the client runtime but not the hot runtime + const webWebpackOptions = webpackOptions[0]; + + expect(webWebpackOptions.entry.length).toEqual(2); + + expect( + normalize(webWebpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + ).toBeTruthy(); + + expect(normalize(webWebpackOptions.entry[1])).toEqual('./foo.js'); + + // node target should have the hot runtime but not the client runtime + const nodeWebpackOptions = webpackOptions[1]; + + expect(nodeWebpackOptions.entry.length).toEqual(2); + + expect( + normalize(nodeWebpackOptions.entry[0]).includes('webpack/hot/dev-server') + ).toBeTruthy(); + + expect(normalize(nodeWebpackOptions.entry[1])).toEqual('./foo.js'); + }); });