Skip to content

Commit bdf201a

Browse files
committed
A hacky attempt at supporting createSharedEntry()
1 parent cbf478f commit bdf201a

File tree

9 files changed

+146
-9
lines changed

9 files changed

+146
-9
lines changed

fixtures/js/shared_example.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// used in a createdSharedEntry() test
2+
require('./no_require');
3+
require('./requires_arrow_function');
4+
require('./../css/h1_style.css');
5+
require('./print_to_app');

index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,14 +392,14 @@ class Encore {
392392
}
393393

394394
/**
395-
* Add a "commons" file that holds JS shared by multiple chunks.
395+
* Add a "commons" file that holds JS shared by multiple chunks/files.
396396
*
397397
* @param {string} name The chunk name (e.g. vendor to create a vendor.js)
398-
* @param {string|Array} files Array of files to put in the vendor entry
398+
* @param {string} file A file whose code & imports should be put into the shared file.
399399
* @returns {Encore}
400400
*/
401-
createSharedEntry(name, files) {
402-
webpackConfig.createSharedEntry(name, files);
401+
createSharedEntry(name, file) {
402+
webpackConfig.createSharedEntry(name, file);
403403

404404
return this;
405405
}

lib/WebpackConfig.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class WebpackConfig {
4343
this.publicPath = null;
4444
this.manifestKeyPrefix = null;
4545
this.sharedCommonsEntryName = null;
46+
this.sharedCommonsEntryFile = null;
4647
this.providedVariables = {};
4748
this.configuredFilenames = {};
4849
this.aliases = {};
@@ -326,15 +327,20 @@ class WebpackConfig {
326327
this.splitChunksConfigurationCallback = callback;
327328
}
328329

329-
createSharedEntry(name, files) {
330+
createSharedEntry(name, file) {
330331
// don't allow to call this twice
331332
if (this.sharedCommonsEntryName) {
332333
throw new Error('createSharedEntry() cannot be called multiple times: you can only create *one* shared entry.');
333334
}
334335

336+
if (Array.isArray(file)) {
337+
throw new Error('Argument 2 to createSharedEntry() must be a single string file: not an array of files. Try creating one file that requires/imports all the modules that should be included.');
338+
}
339+
335340
this.sharedCommonsEntryName = name;
341+
this.sharedCommonsEntryFile = file;
336342

337-
this.addEntry(name, files);
343+
this.addEntry(name, file);
338344
}
339345

340346
enablePostCssLoader(postCssLoaderOptionsCallback = () => {}) {

lib/config-generator.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ const vuePluginUtil = require('./plugins/vue');
3737
const friendlyErrorPluginUtil = require('./plugins/friendly-errors');
3838
const assetOutputDisplay = require('./plugins/asset-output-display');
3939
const notifierPluginUtil = require('./plugins/notifier');
40+
const sharedEntryConcatPuginUtil = require('./plugins/shared-entry-concat');
4041
const PluginPriorities = require('./plugins/plugin-priorities');
4142
const applyOptionsCallback = require('./utils/apply-options-callback');
43+
const tmp = require('tmp');
44+
const fs = require('fs');
45+
const path = require('path');
4246

4347
class ConfigGenerator {
4448
/**
@@ -114,6 +118,24 @@ class ConfigGenerator {
114118
entry[entryName] = entryChunks;
115119
}
116120

121+
if (this.webpackConfig.sharedCommonsEntryName) {
122+
/*
123+
* This is a hack: we need to create a new "entry"
124+
* file that simply requires the same file that
125+
* the "shared entry" requires.
126+
*
127+
* See shared-entry-concat-plugin.js for more details.
128+
*/
129+
const tmpFileObject = tmp.fileSync();
130+
fs.writeFileSync(
131+
tmpFileObject.name,
132+
// quotes in the filename would cause problems
133+
`require('${path.resolve(this.webpackConfig.getContext(), this.webpackConfig.sharedCommonsEntryFile)}')`
134+
);
135+
136+
entry._tmp_shared = tmpFileObject.name;
137+
}
138+
117139
return entry;
118140
}
119141

@@ -301,6 +323,8 @@ class ConfigGenerator {
301323
assetOutputDisplay(plugins, this.webpackConfig, friendlyErrorPlugin);
302324
}
303325

326+
sharedEntryConcatPuginUtil(plugins, this.webpackConfig);
327+
304328
this.webpackConfig.plugins.forEach(function(plugin) {
305329
plugins.push(plugin);
306330
});

lib/plugins/plugin-priorities.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
DeleteUnusedEntriesJSPlugin: 0,
1515
EntryFilesManifestPlugin: 0,
1616
WebpackManifestPlugin: 0,
17+
SharedEntryContactPlugin: 0,
1718
LoaderOptionsPlugin: 0,
1819
ProvidePlugin: 0,
1920
CleanWebpackPlugin: 0,

lib/plugins/shared-entry-concat.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const SharedEntryConcatPlugin = require('../webpack/shared-entry-concat-plugin');
13+
const PluginPriorities = require('./plugin-priorities');
14+
const path = require('path');
15+
16+
/**
17+
* @param {Array} plugins
18+
* @param {WebpackConfig} webpackConfig
19+
* @return {void}
20+
*/
21+
module.exports = function(plugins, webpackConfig) {
22+
if (!webpackConfig.sharedCommonsEntryName) {
23+
return;
24+
}
25+
26+
plugins.push({
27+
plugin: new SharedEntryConcatPlugin(
28+
webpackConfig.sharedCommonsEntryName,
29+
webpackConfig.outputPath
30+
),
31+
priority: PluginPriorities.SharedEntryContactPlugin
32+
});
33+
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const fs = require('fs');
13+
const path = require('path');
14+
15+
function SharedEntryConcatPlugin(sharedEntryName, buildDir) {
16+
this.sharedEntryName = sharedEntryName;
17+
this.buildDir = buildDir;
18+
}
19+
20+
SharedEntryConcatPlugin.prototype.apply = function(compiler) {
21+
const done = (stats) => {
22+
if (stats.hasErrors()) {
23+
return;
24+
}
25+
26+
/*
27+
* This is a hack. See ConfigGenerator.buildEntryConfig()
28+
* for other details.
29+
*
30+
* Basically, the "_tmp_shared" entry is created automatically
31+
* as a "fake" entry. Internally, it simply requires the same
32+
* file that is the source file of the shared entry.
33+
*
34+
* In this plugin, we literally read the final, compiled _tmp_shared.js
35+
* entry, and put its contents at the bottom of the final, compiled,
36+
* shared commons file. Then, we delete _tmp_shared.js. This
37+
* is because the shared entry is actually "removed" as an entry
38+
* file in SplitChunksPlugin, which means that if it contains
39+
* any code that should be executed, that code is not normally
40+
* executed. This fixes that.
41+
*/
42+
43+
const sharedEntryOutputFile = path.join(this.buildDir, this.sharedEntryName + '.js');
44+
const tmpEntryBootstrapFile = path.join(this.buildDir, '_tmp_shared.js');
45+
46+
if (!fs.existsSync(sharedEntryOutputFile)) {
47+
throw new Error(`Could not find shared entry output file: ${sharedEntryOutputFile}`);
48+
}
49+
50+
if (!fs.existsSync(tmpEntryBootstrapFile)) {
51+
throw new Error(`Could not find temporary shared entry bootstrap file: ${tmpEntryBootstrapFile}`);
52+
}
53+
54+
fs.writeFileSync(
55+
sharedEntryOutputFile,
56+
fs.readFileSync(sharedEntryOutputFile) + fs.readFileSync(tmpEntryBootstrapFile)
57+
);
58+
59+
fs.unlinkSync(tmpEntryBootstrapFile);
60+
};
61+
62+
compiler.hooks.done.tap(
63+
{ name: 'SharedEntryConcatPlugin' },
64+
done
65+
);
66+
};
67+
68+
module.exports = SharedEntryConcatPlugin;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"sinon": "^2.3.4",
8080
"stylus": "^0.54.5",
8181
"stylus-loader": "^3.0.2",
82+
"tmp": "^0.0.33",
8283
"ts-loader": "^4.3.0",
8384
"typescript": "^2.3.4",
8485
"url-loader": "^1.0.1",

test/functional.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ describe('Functional tests using webpack', function() {
610610
config.setPublicPath('/build');
611611
config.addEntry('main', ['./js/no_require', './js/code_splitting', './js/arrow_function', './js/print_to_app']);
612612
config.addEntry('other', ['./js/no_require', './css/h1_style.css']);
613-
config.createSharedEntry('shared', ['./js/no_require', './js/requires_arrow_function', './css/h1_style.css']);
613+
config.createSharedEntry('shared', './js/shared_example');
614614

615615
testSetup.runWebpack(config, (webpackAssert) => {
616616
// check the file is extracted correctly
@@ -650,10 +650,9 @@ describe('Functional tests using webpack', function() {
650650
[
651651
'build/runtime.js',
652652
'build/shared.js',
653-
'build/main.js'
654653
],
655654
(browser) => {
656-
// assert that the javascript executed
655+
// assert that the javascript brought into shared is executed
657656
browser.assert.text('#app', 'Welcome to Encore!');
658657
done();
659658
}

0 commit comments

Comments
 (0)