Skip to content

Commit 7db0fcb

Browse files
committed
Adding new Encore.enableSingleRuntimeChunk() + BC layer
This removes the BC breaks related to runtime.js, in favor of a deprecation layer instead.
1 parent 20ad1bd commit 7db0fcb

File tree

8 files changed

+161
-20
lines changed

8 files changed

+161
-20
lines changed

CHANGELOG.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@
55
* [BC BREAK] Webpack was upgraded to version 4. This includes a number of major
66
and minor changes. The changes are listed below.
77

8-
* [BC BREAK] A new `runtime.js` file is now *always* output and must be
9-
included in your layout (before all other script tags for files output
10-
by Encore). You can disable this by calling `Encore.disableSingleRuntimeChunk()`.
11-
But note: if you include multiple entries on the same page, when the
12-
runtime is disabled, each entry will *not* share modules (e.g. if they
13-
both require `jquery`, they will receive 2 separate objects).
14-
15-
* [BC BREAK] If you're using `createSharedEntry()`, the name of the
16-
`manifest.js` file is now `runtime.js`. You will need to update
17-
your `<script>` tag to point to the new `runtime.js`.
8+
* [DEPRECATION] You must now call either `Encore.enableSingleRuntimeChunk()`
9+
or `Encore.disableSingleRuntimeChunk()`: not calling either method is
10+
deprecated. The recommended setting is `Encore.enableSingleRuntimeChunk()`.
11+
This will cause a new `runtime.js` file to be created, which must be included
12+
on your page with a script tag (before any other script tags for Encore
13+
JavaScript files). See the documentation above `enableSingleRuntimeChunk()` in
14+
`index.js` for more details.
1815

1916
* [BEHAVIOR CHANGE] Previously, without any config, Babel was
2017
configured to "transpile" (i.e. re-write) your JavaScript so

index.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,16 @@ class Encore {
405405
}
406406

407407
/**
408-
* Tell Webpack to *not* output a separate runtime.js file.
408+
* Tell Webpack to output a separate runtime.js file.
409+
*
410+
* This file must be included via a script tag before all
411+
* other JavaScript files output by Encore.
409412
*
410413
* The runtime.js file is useful when you plan to include
411414
* multiple entry files on the same page (e.g. a layout.js entry
412415
* and a page-specific entry). If you are *not* including
413416
* multiple entries on the same page, you can safely disable
414-
* this and remove the extra script tags.
417+
* this - disableSingleRuntimeChunk() - and remove the extra script tags.
415418
*
416419
* If you *do* include multiple entry files on the same page,
417420
* disabling the runtime.js file has two important consequences:
@@ -426,6 +429,19 @@ class Encore {
426429
*
427430
* @returns {Encore}
428431
*/
432+
enableSingleRuntimeChunk() {
433+
webpackConfig.enableSingleRuntimeChunk();
434+
435+
return this;
436+
}
437+
438+
/**
439+
* Tell Webpack to *not* output a separate runtime.js file.
440+
*
441+
* See enableSingleRuntimeChunk() for more details.
442+
*
443+
* @returns {Encore}
444+
*/
429445
disableSingleRuntimeChunk() {
430446
webpackConfig.disableSingleRuntimeChunk();
431447

lib/WebpackConfig.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class WebpackConfig {
8787
this.stylusLoaderOptionsCallback = () => {};
8888
this.babelConfigurationCallback = () => {};
8989
this.cssLoaderConfigurationCallback = () => {};
90-
this.shouldUseSingleRuntimeChunk = true;
90+
this.shouldUseSingleRuntimeChunk = null;
9191
this.shouldSplitEntryChunks = false;
9292
this.splitChunksConfigurationCallback = () => {};
9393
this.vueLoaderOptionsCallback = () => {};
@@ -321,6 +321,10 @@ class WebpackConfig {
321321
this.cssLoaderConfigurationCallback = callback;
322322
}
323323

324+
enableSingleRuntimeChunk() {
325+
this.shouldUseSingleRuntimeChunk = true;
326+
}
327+
324328
disableSingleRuntimeChunk() {
325329
this.shouldUseSingleRuntimeChunk = false;
326330
}

lib/config-generator.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const fs = require('fs');
4646
const path = require('path');
4747
const stringEscaper = require('./utils/string-escaper');
4848
const crypto = require('crypto');
49+
const logger = require('./logger');
4950

5051
class ConfigGenerator {
5152
/**
@@ -402,12 +403,36 @@ class ConfigGenerator {
402403
splitChunks.cacheGroups = cacheGroups;
403404
}
404405

405-
if (this.webpackConfig.shouldUseSingleRuntimeChunk) {
406-
// causes a runtime.js to be emitted with the Webpack runtime
407-
// this is important as a default because it causes different entry
408-
// files to "share" modules, instead of each module getting their own
409-
// fresh version of each module.
410-
optimization.runtimeChunk = 'single';
406+
switch (this.webpackConfig.shouldUseSingleRuntimeChunk) {
407+
case true:
408+
// causes a runtime.js to be emitted with the Webpack runtime
409+
// this is important as a default because it causes different entry
410+
// files to "share" modules, instead of each module getting their own
411+
// fresh version of each module.
412+
optimization.runtimeChunk = 'single';
413+
break;
414+
case false:
415+
// add no runtimeChunk configuration
416+
break;
417+
case null:
418+
/*
419+
* Not setting this option explicitly is deprecated.
420+
*/
421+
logger.deprecation('Either the Encore.enableSingleRuntimeChunk() or Encore.disableSingleRuntimeChunk() method should be called.');
422+
if (this.webpackConfig.sharedCommonsEntryName) {
423+
logger.deprecation('Because you\'re using createSharedEntry(), the recommended setting is Encore.enableSingleRuntimeChunk().');
424+
logger.deprecation('After calling Encore.enableSingleRuntimeChunk(), the "manifest.js" file will be called "runtime.js": your script tag will need to be updated.');
425+
// output it, but keep the old filename
426+
optimization.runtimeChunk = {
427+
name: 'manifest'
428+
};
429+
} else {
430+
logger.deprecation('The recommended setting is Encore.enableSingleRuntimeChunk().');
431+
logger.deprecation('After calling Encore.enableSingleRuntimeChunk(), a new "runtime.js" will be output and should be included on your page before any other script tags for Encore files.');
432+
// do not output the runtime
433+
}
434+
435+
break;
411436
}
412437

413438
optimization.splitChunks = applyOptionsCallback(

lib/plugins/mini-css-extract.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = function(plugins, webpackConfig) {
2626
// CSS file mysteriously becomes a chunk. In other words, it
2727
// will have a filename like 1.css instead of entry_name.css
2828
// This is related to setting optimization.runtimeChunk = 'single';
29+
// See https://github.com/webpack/webpack/issues/6598
2930
let chunkFilename = webpackConfig.useVersioning ? '[name].[contenthash:8].css' : '[name].css';
3031
if (webpackConfig.configuredFilenames.css) {
3132
filename = webpackConfig.configuredFilenames.css;

test/config-generator.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
1717
const ManifestPlugin = require('webpack-manifest-plugin');
1818
const CleanWebpackPlugin = require('clean-webpack-plugin');
1919
const webpack = require('webpack');
20+
const logger = require('../lib/logger');
2021

2122
const isWindows = (process.platform === 'win32');
2223

@@ -850,4 +851,58 @@ describe('The config-generator function', () => {
850851
expect(actualConfig.optimization.splitChunks.name).to.be.a('function');
851852
});
852853
});
854+
855+
describe('Test shouldUseSingleRuntimeChunk', () => {
856+
before(() => {
857+
logger.reset();
858+
logger.quiet();
859+
});
860+
861+
after(() => {
862+
logger.quiet(false);
863+
});
864+
865+
it('Set to true', () => {
866+
const config = createConfig();
867+
config.outputPath = '/tmp/public/build';
868+
config.setPublicPath('/build/');
869+
config.enableSingleRuntimeChunk();
870+
871+
const actualConfig = configGenerator(config);
872+
expect(actualConfig.optimization.runtimeChunk).to.equal('single');
873+
expect(logger.getMessages().deprecation).to.be.empty;
874+
});
875+
876+
it('Set to false', () => {
877+
const config = createConfig();
878+
config.outputPath = '/tmp/public/build';
879+
config.setPublicPath('/build/');
880+
config.disableSingleRuntimeChunk();
881+
882+
const actualConfig = configGenerator(config);
883+
expect(actualConfig.optimization.runtimeChunk).to.be.undefined;
884+
expect(logger.getMessages().deprecation).to.be.empty;
885+
});
886+
887+
it('Not set + createSharedEntry()', () => {
888+
const config = createConfig();
889+
config.outputPath = '/tmp/public/build';
890+
config.setPublicPath('/build/');
891+
config.createSharedEntry('foo', 'bar.js');
892+
893+
const actualConfig = configGenerator(config);
894+
expect(actualConfig.optimization.runtimeChunk.name).to.equal('manifest');
895+
expect(JSON.stringify(logger.getMessages().deprecation)).to.contain('the recommended setting is Encore.enableSingleRuntimeChunk()');
896+
});
897+
898+
it('Not set without createSharedEntry()', () => {
899+
const config = createConfig();
900+
config.outputPath = '/tmp/public/build';
901+
config.setPublicPath('/build/');
902+
903+
const actualConfig = configGenerator(config);
904+
expect(actualConfig.optimization.runtimeChunk).to.be.undefined;
905+
expect(JSON.stringify(logger.getMessages().deprecation)).to.contain('the recommended setting is Encore.enableSingleRuntimeChunk()');
906+
});
907+
});
853908
});

test/functional.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ const fs = require('fs-extra');
1818
const sharedEntryTmpName = require('../lib/utils/sharedEntryTmpName');
1919

2020
function createWebpackConfig(outputDirName = '', command, argv = {}) {
21-
return testSetup.createWebpackConfig(
21+
const webpackConfig = testSetup.createWebpackConfig(
2222
testSetup.createTestAppDir(),
2323
outputDirName,
2424
command,
2525
argv
2626
);
27+
28+
webpackConfig.enableSingleRuntimeChunk();
29+
30+
return webpackConfig;
2731
}
2832

2933
function convertToManifestPath(assetSrc, webpackConfig) {
@@ -712,6 +716,36 @@ describe('Functional tests using webpack', function() {
712716
});
713717
});
714718

719+
it('createdSharedEntry() with shouldUseSingleRuntimeChunk not set', (done) => {
720+
const config = createWebpackConfig('www/build', 'dev');
721+
// set back to the "not set" value
722+
config.shouldUseSingleRuntimeChunk = null;
723+
config.setPublicPath('/build');
724+
config.addEntry('main', ['./js/no_require', './js/code_splitting', './js/arrow_function', './js/print_to_app']);
725+
config.createSharedEntry('shared', './js/shared_example');
726+
727+
testSetup.runWebpack(config, (webpackAssert) => {
728+
// should be called manifest.js
729+
webpackAssert.assertOutputFileContains(
730+
'manifest.js',
731+
'function __webpack_require__'
732+
);
733+
734+
testSetup.requestTestPage(
735+
path.join(config.getContext(), 'www'),
736+
[
737+
'build/manifest.js',
738+
'build/shared.js',
739+
],
740+
(browser) => {
741+
// assert that the javascript brought into shared is executed
742+
browser.assert.text('#app', 'Welcome to Encore!');
743+
done();
744+
}
745+
);
746+
});
747+
});
748+
715749
it('createdSharedEntry() does not run shared code twice', (done) => {
716750
const config = createWebpackConfig('www/build', 'dev');
717751
config.setPublicPath('/build');

test/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ describe('Public API', () => {
134134

135135
});
136136

137+
describe('enableSingleRuntimeChunk', () => {
138+
139+
it('must return the API object', () => {
140+
const returnedValue = api.enableSingleRuntimeChunk();
141+
expect(returnedValue).to.equal(api);
142+
});
143+
144+
});
145+
137146
describe('disableSingleRuntimeChunk', () => {
138147

139148
it('must return the API object', () => {

0 commit comments

Comments
 (0)