From 7a39654c1439637426a2abab64685fe3433f80b8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 10 Jan 2021 21:19:53 -0500 Subject: [PATCH 1/3] Adding new enableBuildCache() method for Webpack 5 persistent caching --- CHANGELOG.md | 4 ++++ index.js | 31 +++++++++++++++++++++++++++++++ lib/WebpackConfig.js | 22 ++++++++++++++++++++++ lib/config-generator.js | 19 +++++++++++++++++++ test/WebpackConfig.js | 34 ++++++++++++++++++++++++++++++++++ test/config-generator.js | 32 ++++++++++++++++++++++++++++++++ test/functional.js | 17 +++++++++++++++++ test/index.js | 20 ++++++++++++++++++++ 8 files changed, 179 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff633ca..18be96c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,10 @@ added to allow the `MiniCssExtractPlugin.loader` and `MiniCssExtractPlugin` to be configured. +* [enableBuildCache()] Added `enableBuildCache()` to enable the new + Webpack 5 build caching. https://webpack.js.org/blog/2020-10-10-webpack-5-release/ + This feature should be considered experimental. + ## 0.33.0 * [disableCssExtraction()] Added boolean argument to `disableCssExtraction()` diff --git a/index.js b/index.js index 81385b5d..a6dc59b3 100644 --- a/index.js +++ b/index.js @@ -1005,6 +1005,37 @@ class Encore { return this; } + /** + * Enables & configures persistent build caching. + * + * https://webpack.js.org/blog/2020-10-10-webpack-5-release/#persistent-caching + * + * ``` + * Encore.enableBuildCache({ + * // object of "buildDependencies" + * // https://webpack.js.org/configuration/other-options/#cachebuilddependencies + * // __filename means that changes to webpack.config.js should invalidate the cache + * config: [__filename], + * }); + ** + * // also configure other options the Webpack "cache" key + * Encore.enableBuildCache({ config: [__filename] }, (cache) => { + * cache.version: `${process.env.GIT_REV}`; + * + * cache.name: `${env.target}` + * }); + * ``` + * + * @param {object} buildDependencies + * @param {function} cacheCallback + * @returns {Encore} + */ + enableBuildCache(buildDependencies, cacheCallback = (cache) => {}) { + webpackConfig.enableBuildCache(buildDependencies, cacheCallback); + + return this; + } + /** * Configure the mini-css-extract-plugin. * diff --git a/lib/WebpackConfig.js b/lib/WebpackConfig.js index 183d5835..f51d1e95 100644 --- a/lib/WebpackConfig.js +++ b/lib/WebpackConfig.js @@ -96,6 +96,7 @@ class WebpackConfig { this.useVersioning = false; this.useSourceMaps = false; this.cleanupOutput = false; + this.usePersistentCache = false; this.extractCss = true; this.imageRuleOptions = { type: 'asset/resource', @@ -150,6 +151,7 @@ class WebpackConfig { this.eslintOptions = { lintVue: false, }; + this.persistentCacheBuildDependencies = {}; // Features/Loaders options callbacks this.imageRuleCallback = () => {}; @@ -197,6 +199,7 @@ class WebpackConfig { this.terserPluginOptionsCallback = () => {}; this.cssMinimizerPluginOptionsCallback = () => {}; this.notifierPluginOptionsCallback = () => {}; + this.persistentCacheCallback = () => {}; } getContext() { @@ -685,6 +688,25 @@ class WebpackConfig { this.stimulusOptions.controllersJsonPath = controllerJsonPath; } + enableBuildCache(buildDependencies, callback = (cache) => {}) { + if (typeof buildDependencies !== 'object') { + throw new Error('Argument 1 to enableBuildCache() must be an object.'); + } + + if (!buildDependencies.config) { + throw new Error('Argument 1 to enableBuildCache() should contain an object with at least a "config" key. See the documentation for this method.'); + } + + this.usePersistentCache = true; + this.persistentCacheBuildDependencies = buildDependencies; + + if (typeof callback !== 'function') { + throw new Error('Argument 2 to enableBuildCache() must be a callback function.'); + } + + this.persistentCacheCallback = callback; + } + enableReactPreset() { this.useReact = true; } diff --git a/lib/config-generator.js b/lib/config-generator.js index b3260806..dfcf1f48 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -67,6 +67,7 @@ class ConfigGenerator { }, plugins: this.buildPluginsConfig(), optimization: this.buildOptimizationConfig(), + cache: this.buildCacheConfig(), watchOptions: this.buildWatchOptionsConfig(), devtool: false, }; @@ -509,6 +510,24 @@ class ConfigGenerator { return optimization; } + buildCacheConfig() { + if (!this.webpackConfig.usePersistentCache) { + return false; + } + + const cache = {}; + + cache.type = 'filesystem'; + cache.buildDependencies = this.webpackConfig.persistentCacheBuildDependencies; + + applyOptionsCallback( + this.webpackConfig.persistentCacheCallback, + cache + ); + + return cache; + } + buildStatsConfig() { // try to silence as much as possible: the output is rarely helpful // this still doesn't remove all output diff --git a/test/WebpackConfig.js b/test/WebpackConfig.js index a9166170..756455b4 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.js @@ -906,6 +906,40 @@ describe('WebpackConfig object', () => { }); }); + describe('enableBuildCache', () => { + it('Calling method enables it', () => { + const config = createConfig(); + config.enableBuildCache({ config: ['foo.js'] }); + + expect(config.usePersistentCache).to.be.true; + expect(config.persistentCacheBuildDependencies).to.eql({ config: ['foo.js'] }); + }); + + it('Calling with callback', () => { + const config = createConfig(); + const callback = (cache) => {}; + config.enableBuildCache({ config: ['foo.js'] }, callback); + + expect(config.persistentCacheCallback).to.equal(callback); + }); + + it('Calling without config key throws an error', () => { + const config = createConfig(); + + expect(() => { + config.enableBuildCache({}); + }).to.throw('should contain an object with at least a "config" key'); + }); + + it('Calling with non-callback throws an error', () => { + const config = createConfig(); + + expect(() => { + config.enableBuildCache({ config: ['foo.js'] }, 'FOO'); + }).to.throw('must be a callback function'); + }); + }); + describe('enablePreactPreset', () => { it('Without preact-compat', () => { const config = createConfig(); diff --git a/test/config-generator.js b/test/config-generator.js index cef6da03..fb56a772 100644 --- a/test/config-generator.js +++ b/test/config-generator.js @@ -913,6 +913,38 @@ describe('The config-generator function', () => { }); }); + describe('Test enableBuildCache()', () => { + it('with full arguments', () => { + const config = createConfig(); + config.outputPath = '/tmp/public-path'; + config.publicPath = '/public-path'; + config.addEntry('main', './main'); + config.enableBuildCache({ config: ['foo.js'] }, (cache) => { + cache.version = 5; + }); + + const actualConfig = configGenerator(config); + expect(actualConfig.cache).to.eql({ + type: 'filesystem', + buildDependencies: { config: ['foo.js'] }, + version: 5 + }); + }); + + it('with sourcemaps', () => { + const config = createConfig(); + config.outputPath = '/tmp/public-path'; + config.publicPath = '/public-path'; + config.addEntry('main', './main'); + config.useSourceMaps = true; + + const actualConfig = configGenerator(config); + expect(actualConfig.devtool).to.equal('inline-source-map'); + + expect(JSON.stringify(actualConfig.module.rules)).to.contain('"sourceMap":true'); + }); + }); + describe('Test configureBabel()', () => { it('without configureBabel()', () => { const config = createConfig(); diff --git a/test/functional.js b/test/functional.js index 45ed008f..e3812f4b 100644 --- a/test/functional.js +++ b/test/functional.js @@ -731,6 +731,23 @@ describe('Functional tests using webpack', function() { }); }); + it('Persistent caching does not cause problems', (done) => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/code_splitting'); + config.enableBuildCache({ config: [__filename] }); + + testSetup.runWebpack(config, (webpackAssert) => { + // sanity check + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + done(); + }); + }); + describe('addCacheGroup()', () => { it('addCacheGroup() to extract a vendor into its own chunk', (done) => { const config = createWebpackConfig('www/build', 'dev'); diff --git a/test/index.js b/test/index.js index 6cd0882e..be0824f9 100644 --- a/test/index.js +++ b/test/index.js @@ -11,6 +11,7 @@ const expect = require('chai').expect; const api = require('../index'); +const path = require('path'); describe('Public API', () => { beforeEach(() => { @@ -416,6 +417,25 @@ describe('Public API', () => { }); + describe('enableStimulusBridge', () => { + + it('should return the API object', () => { + const returnedValue = api.enableStimulusBridge(path.resolve(__dirname, '../', 'package.json')); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('enableBuildCache', () => { + + it('should return the API object', () => { + const returnedValue = api.enableBuildCache({}); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('configureMiniCssExtractPlugin', () => { it('should return the API object', () => { From 9f117a43ddbcb9e41387bf76dd3f1ff984f36c59 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 22 Jan 2021 15:24:35 -0500 Subject: [PATCH 2/3] fixing test --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index be0824f9..d81af3bd 100644 --- a/test/index.js +++ b/test/index.js @@ -429,7 +429,7 @@ describe('Public API', () => { describe('enableBuildCache', () => { it('should return the API object', () => { - const returnedValue = api.enableBuildCache({}); + const returnedValue = api.enableBuildCache({config: [__filename]}); expect(returnedValue).to.equal(api); }); From a592c8207be19d55a302170c7d2a1b5570678e6c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 22 Jan 2021 16:45:08 -0500 Subject: [PATCH 3/3] lint --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index d81af3bd..ba99ad7a 100644 --- a/test/index.js +++ b/test/index.js @@ -429,7 +429,7 @@ describe('Public API', () => { describe('enableBuildCache', () => { it('should return the API object', () => { - const returnedValue = api.enableBuildCache({config: [__filename]}); + const returnedValue = api.enableBuildCache({ config: [__filename] }); expect(returnedValue).to.equal(api); });