Skip to content

Commit 7a39654

Browse files
committed
Adding new enableBuildCache() method for Webpack 5 persistent caching
1 parent b7e25d2 commit 7a39654

8 files changed

+179
-0
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
added to allow the `MiniCssExtractPlugin.loader` and `MiniCssExtractPlugin`
5353
to be configured.
5454

55+
* [enableBuildCache()] Added `enableBuildCache()` to enable the new
56+
Webpack 5 build caching. https://webpack.js.org/blog/2020-10-10-webpack-5-release/
57+
This feature should be considered experimental.
58+
5559
## 0.33.0
5660

5761
* [disableCssExtraction()] Added boolean argument to `disableCssExtraction()`

index.js

+31
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,37 @@ class Encore {
10051005
return this;
10061006
}
10071007

1008+
/**
1009+
* Enables & configures persistent build caching.
1010+
*
1011+
* https://webpack.js.org/blog/2020-10-10-webpack-5-release/#persistent-caching
1012+
*
1013+
* ```
1014+
* Encore.enableBuildCache({
1015+
* // object of "buildDependencies"
1016+
* // https://webpack.js.org/configuration/other-options/#cachebuilddependencies
1017+
* // __filename means that changes to webpack.config.js should invalidate the cache
1018+
* config: [__filename],
1019+
* });
1020+
**
1021+
* // also configure other options the Webpack "cache" key
1022+
* Encore.enableBuildCache({ config: [__filename] }, (cache) => {
1023+
* cache.version: `${process.env.GIT_REV}`;
1024+
*
1025+
* cache.name: `${env.target}`
1026+
* });
1027+
* ```
1028+
*
1029+
* @param {object} buildDependencies
1030+
* @param {function} cacheCallback
1031+
* @returns {Encore}
1032+
*/
1033+
enableBuildCache(buildDependencies, cacheCallback = (cache) => {}) {
1034+
webpackConfig.enableBuildCache(buildDependencies, cacheCallback);
1035+
1036+
return this;
1037+
}
1038+
10081039
/**
10091040
* Configure the mini-css-extract-plugin.
10101041
*

lib/WebpackConfig.js

+22
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class WebpackConfig {
9696
this.useVersioning = false;
9797
this.useSourceMaps = false;
9898
this.cleanupOutput = false;
99+
this.usePersistentCache = false;
99100
this.extractCss = true;
100101
this.imageRuleOptions = {
101102
type: 'asset/resource',
@@ -150,6 +151,7 @@ class WebpackConfig {
150151
this.eslintOptions = {
151152
lintVue: false,
152153
};
154+
this.persistentCacheBuildDependencies = {};
153155

154156
// Features/Loaders options callbacks
155157
this.imageRuleCallback = () => {};
@@ -197,6 +199,7 @@ class WebpackConfig {
197199
this.terserPluginOptionsCallback = () => {};
198200
this.cssMinimizerPluginOptionsCallback = () => {};
199201
this.notifierPluginOptionsCallback = () => {};
202+
this.persistentCacheCallback = () => {};
200203
}
201204

202205
getContext() {
@@ -685,6 +688,25 @@ class WebpackConfig {
685688
this.stimulusOptions.controllersJsonPath = controllerJsonPath;
686689
}
687690

691+
enableBuildCache(buildDependencies, callback = (cache) => {}) {
692+
if (typeof buildDependencies !== 'object') {
693+
throw new Error('Argument 1 to enableBuildCache() must be an object.');
694+
}
695+
696+
if (!buildDependencies.config) {
697+
throw new Error('Argument 1 to enableBuildCache() should contain an object with at least a "config" key. See the documentation for this method.');
698+
}
699+
700+
this.usePersistentCache = true;
701+
this.persistentCacheBuildDependencies = buildDependencies;
702+
703+
if (typeof callback !== 'function') {
704+
throw new Error('Argument 2 to enableBuildCache() must be a callback function.');
705+
}
706+
707+
this.persistentCacheCallback = callback;
708+
}
709+
688710
enableReactPreset() {
689711
this.useReact = true;
690712
}

lib/config-generator.js

+19
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class ConfigGenerator {
6767
},
6868
plugins: this.buildPluginsConfig(),
6969
optimization: this.buildOptimizationConfig(),
70+
cache: this.buildCacheConfig(),
7071
watchOptions: this.buildWatchOptionsConfig(),
7172
devtool: false,
7273
};
@@ -509,6 +510,24 @@ class ConfigGenerator {
509510
return optimization;
510511
}
511512

513+
buildCacheConfig() {
514+
if (!this.webpackConfig.usePersistentCache) {
515+
return false;
516+
}
517+
518+
const cache = {};
519+
520+
cache.type = 'filesystem';
521+
cache.buildDependencies = this.webpackConfig.persistentCacheBuildDependencies;
522+
523+
applyOptionsCallback(
524+
this.webpackConfig.persistentCacheCallback,
525+
cache
526+
);
527+
528+
return cache;
529+
}
530+
512531
buildStatsConfig() {
513532
// try to silence as much as possible: the output is rarely helpful
514533
// this still doesn't remove all output

test/WebpackConfig.js

+34
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,40 @@ describe('WebpackConfig object', () => {
906906
});
907907
});
908908

909+
describe('enableBuildCache', () => {
910+
it('Calling method enables it', () => {
911+
const config = createConfig();
912+
config.enableBuildCache({ config: ['foo.js'] });
913+
914+
expect(config.usePersistentCache).to.be.true;
915+
expect(config.persistentCacheBuildDependencies).to.eql({ config: ['foo.js'] });
916+
});
917+
918+
it('Calling with callback', () => {
919+
const config = createConfig();
920+
const callback = (cache) => {};
921+
config.enableBuildCache({ config: ['foo.js'] }, callback);
922+
923+
expect(config.persistentCacheCallback).to.equal(callback);
924+
});
925+
926+
it('Calling without config key throws an error', () => {
927+
const config = createConfig();
928+
929+
expect(() => {
930+
config.enableBuildCache({});
931+
}).to.throw('should contain an object with at least a "config" key');
932+
});
933+
934+
it('Calling with non-callback throws an error', () => {
935+
const config = createConfig();
936+
937+
expect(() => {
938+
config.enableBuildCache({ config: ['foo.js'] }, 'FOO');
939+
}).to.throw('must be a callback function');
940+
});
941+
});
942+
909943
describe('enablePreactPreset', () => {
910944
it('Without preact-compat', () => {
911945
const config = createConfig();

test/config-generator.js

+32
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,38 @@ describe('The config-generator function', () => {
913913
});
914914
});
915915

916+
describe('Test enableBuildCache()', () => {
917+
it('with full arguments', () => {
918+
const config = createConfig();
919+
config.outputPath = '/tmp/public-path';
920+
config.publicPath = '/public-path';
921+
config.addEntry('main', './main');
922+
config.enableBuildCache({ config: ['foo.js'] }, (cache) => {
923+
cache.version = 5;
924+
});
925+
926+
const actualConfig = configGenerator(config);
927+
expect(actualConfig.cache).to.eql({
928+
type: 'filesystem',
929+
buildDependencies: { config: ['foo.js'] },
930+
version: 5
931+
});
932+
});
933+
934+
it('with sourcemaps', () => {
935+
const config = createConfig();
936+
config.outputPath = '/tmp/public-path';
937+
config.publicPath = '/public-path';
938+
config.addEntry('main', './main');
939+
config.useSourceMaps = true;
940+
941+
const actualConfig = configGenerator(config);
942+
expect(actualConfig.devtool).to.equal('inline-source-map');
943+
944+
expect(JSON.stringify(actualConfig.module.rules)).to.contain('"sourceMap":true');
945+
});
946+
});
947+
916948
describe('Test configureBabel()', () => {
917949
it('without configureBabel()', () => {
918950
const config = createConfig();

test/functional.js

+17
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,23 @@ describe('Functional tests using webpack', function() {
731731
});
732732
});
733733

734+
it('Persistent caching does not cause problems', (done) => {
735+
const config = createWebpackConfig('www/build', 'dev');
736+
config.setPublicPath('/build');
737+
config.addEntry('main', './js/code_splitting');
738+
config.enableBuildCache({ config: [__filename] });
739+
740+
testSetup.runWebpack(config, (webpackAssert) => {
741+
// sanity check
742+
webpackAssert.assertManifestPath(
743+
'build/main.js',
744+
'/build/main.js'
745+
);
746+
747+
done();
748+
});
749+
});
750+
734751
describe('addCacheGroup()', () => {
735752
it('addCacheGroup() to extract a vendor into its own chunk', (done) => {
736753
const config = createWebpackConfig('www/build', 'dev');

test/index.js

+20
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
const expect = require('chai').expect;
1313
const api = require('../index');
14+
const path = require('path');
1415

1516
describe('Public API', () => {
1617
beforeEach(() => {
@@ -416,6 +417,25 @@ describe('Public API', () => {
416417

417418
});
418419

420+
describe('enableStimulusBridge', () => {
421+
422+
it('should return the API object', () => {
423+
const returnedValue = api.enableStimulusBridge(path.resolve(__dirname, '../', 'package.json'));
424+
expect(returnedValue).to.equal(api);
425+
});
426+
427+
});
428+
429+
describe('enableBuildCache', () => {
430+
431+
it('should return the API object', () => {
432+
const returnedValue = api.enableBuildCache({});
433+
expect(returnedValue).to.equal(api);
434+
});
435+
436+
});
437+
438+
419439
describe('configureMiniCssExtractPlugin', () => {
420440

421441
it('should return the API object', () => {

0 commit comments

Comments
 (0)