From 327cb9aa11e54b30064520e5a551e325025446b1 Mon Sep 17 00:00:00 2001 From: Jeff Posnick Date: Tue, 17 Mar 2020 11:26:19 -0400 Subject: [PATCH 1/3] WIP --- .../workbox-build/src/options/defaults.js | 1 + .../options/schema/webpack-inject-manifest.js | 2 ++ .../node/inject-manifest.js | 27 +++++++++++++++++++ .../static/injected-manifest.json | 1 + 4 files changed, 31 insertions(+) create mode 100644 test/workbox-webpack-plugin/static/injected-manifest.json diff --git a/packages/workbox-build/src/options/defaults.js b/packages/workbox-build/src/options/defaults.js index 550c87fc2..0f22a83e8 100644 --- a/packages/workbox-build/src/options/defaults.js +++ b/packages/workbox-build/src/options/defaults.js @@ -10,6 +10,7 @@ module.exports = { babelPresetEnvTargets: ['chrome >= 56'], cleanupOutdatedCaches: false, clientsClaim: false, + compileSrc: true, disableDevLogs: false, exclude: [ /\.map$/, diff --git a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js index 7f7373c5e..2af206030 100644 --- a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js +++ b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js @@ -10,6 +10,7 @@ const joi = require('@hapi/joi'); const upath = require('upath'); const basePartial = require('../partials/base'); +const defaults = require('../defaults'); const injectPartial = require('../partials/inject'); const webpackPartial = require('../partials/webpack'); @@ -22,6 +23,7 @@ const swSrcBasename = (context) => { swSrcBasename.description = 'derived from the swSrc file name'; const supportedOptions = Object.assign({ + compileSrc: joi.boolean().default(defaults.compileSrc), webpackCompilationPlugins: joi.array().items(joi.object()), }, basePartial, injectPartial, webpackPartial); diff --git a/test/workbox-webpack-plugin/node/inject-manifest.js b/test/workbox-webpack-plugin/node/inject-manifest.js index e23dc32f2..0e74a1d55 100644 --- a/test/workbox-webpack-plugin/node/inject-manifest.js +++ b/test/workbox-webpack-plugin/node/inject-manifest.js @@ -1555,4 +1555,31 @@ describe(`[workbox-webpack-plugin] InjectManifest (End to End)`, function() { }); }); }); + + describe(`[workbox-webpack-plugin] Non-compilation scenarios`, function() { + it(`•should support injecting a valid JSON manifest`, function(done) { + const outputDir = tempy.directory(); + console.log(outputDir); + const config = { + mode: 'production', + entry: upath.join(SRC_DIR, WEBPACK_ENTRY_FILENAME), + output: { + filename: '[name].[hash:20].js', + path: outputDir, + }, + plugins: [ + new InjectManifest({ + compileSrc: false, + swDest: 'injected-manifest.json', + swSrc: upath.join(__dirname, '..', 'static', 'injected-manifest.json'), + }), + ], + }; + + const compiler = webpack(config); + compiler.run(async (webpackError, stats) => { + done(); + }); + }); + }); }); diff --git a/test/workbox-webpack-plugin/static/injected-manifest.json b/test/workbox-webpack-plugin/static/injected-manifest.json new file mode 100644 index 000000000..c18355c0a --- /dev/null +++ b/test/workbox-webpack-plugin/static/injected-manifest.json @@ -0,0 +1 @@ +self.__WB_MANIFEST From 1a5c442e05e3b63834c90055d96ec6b7fadadfa2 Mon Sep 17 00:00:00 2001 From: Jeff Posnick Date: Wed, 18 Mar 2020 16:24:44 -0400 Subject: [PATCH 2/3] Support for compileSrc --- .../options/schema/webpack-inject-manifest.js | 3 +- .../src/inject-manifest.js | 57 +++++++--- .../node/inject-manifest.js | 106 ++++++++++++++++-- .../static/injected-manifest.js | 1 + 4 files changed, 145 insertions(+), 22 deletions(-) create mode 100644 test/workbox-webpack-plugin/static/injected-manifest.js diff --git a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js index 2af206030..d9ea21397 100644 --- a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js +++ b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js @@ -24,7 +24,8 @@ swSrcBasename.description = 'derived from the swSrc file name'; const supportedOptions = Object.assign({ compileSrc: joi.boolean().default(defaults.compileSrc), - webpackCompilationPlugins: joi.array().items(joi.object()), + webpackCompilationPlugins: joi.array().items(joi.object()).when( + 'compileSrc', {is: false, then: joi.forbidden()}), }, basePartial, injectPartial, webpackPartial); module.exports = joi.object().keys(supportedOptions).keys({ diff --git a/packages/workbox-webpack-plugin/src/inject-manifest.js b/packages/workbox-webpack-plugin/src/inject-manifest.js index fee1af5c9..79035cd78 100644 --- a/packages/workbox-webpack-plugin/src/inject-manifest.js +++ b/packages/workbox-webpack-plugin/src/inject-manifest.js @@ -49,10 +49,15 @@ class InjectManifest { * @param {Array} [config.additionalManifestEntries] * A list of entries to be precached, in addition to any entries that are * generated as part of the build configuration. - * + * * @param {Array} [config.chunks] One or more chunk names whose corresponding * output files should be included in the precache manifest. * + * @param {boolean} [config.compileSrc=true] When `true` (the default), the + * `swSrc` file will be compiled by webpack. When `false`, compilation will + * not occur (and `webpackCompilationPlugins` can't be used.) Set to `false` + * if you want to inject the manifest into, e.g., a JSON file. + * * @param {RegExp} [config.dontCacheBustURLsMatching] Assets that match this will be * assumed to be uniquely versioned via their URL, and exempted from the normal * HTTP cache-busting that's done when populating the precache. While not @@ -158,17 +163,7 @@ class InjectManifest { * * @private */ - async handleMake(compilation, parentCompiler) { - try { - this.config = validate(this.config, webpackInjectManifestSchema); - } catch (error) { - throw new Error(`Please check your ${this.constructor.name} plugin ` + - `configuration:\n${error.message}`); - } - - this.config.swDest = relativeToOutputPath(compilation, this.config.swDest); - _generatedAssetNames.add(this.config.swDest); - + async performChildCompilation(compilation, parentCompiler) { const outputOptions = { path: parentCompiler.options.output.path, filename: this.config.swDest, @@ -211,6 +206,36 @@ class InjectManifest { }); } + addSrcToAssets(compilation, parentCompiler) { + const source = parentCompiler.inputFileSystem.readFileSync( + this.config.swSrc).toString(); + compilation.assets[this.config.swDest] = new RawSource(source); + } + + /** + * @param {Object} compilation The webpack compilation. + * @param {Object} parentCompiler The webpack parent compiler. + * + * @private + */ + async handleMake(compilation, parentCompiler) { + try { + this.config = validate(this.config, webpackInjectManifestSchema); + } catch (error) { + throw new Error(`Please check your ${this.constructor.name} plugin ` + + `configuration:\n${error.message}`); + } + + this.config.swDest = relativeToOutputPath(compilation, this.config.swDest); + _generatedAssetNames.add(this.config.swDest); + + if (this.config.compileSrc) { + await this.performChildCompilation(compilation, parentCompiler); + } else { + this.addSrcToAssets(compilation, parentCompiler); + } + } + /** * @param {Object} compilation The webpack compilation. * @@ -240,6 +265,7 @@ class InjectManifest { const swAsset = compilation.assets[config.swDest]; const initialSWAssetString = swAsset.source(); + if (!initialSWAssetString.includes(config.injectionPoint)) { throw new Error(`Can't find ${config.injectionPoint} in your SW source.`); } @@ -247,8 +273,11 @@ class InjectManifest { const manifestEntries = await getManifestEntriesFromCompilation( compilation, config); - // See https://github.com/GoogleChrome/workbox/issues/2263 - const manifestString = stringify(manifestEntries).replace(/"/g, `'`); + let manifestString = stringify(manifestEntries); + if (this.config.compileSrc) { + // See https://github.com/GoogleChrome/workbox/issues/2263 + manifestString = manifestString.replace(/"/g, `'`); + } const sourcemapAssetName = getSourcemapAssetName( compilation, initialSWAssetString, config.swDest); diff --git a/test/workbox-webpack-plugin/node/inject-manifest.js b/test/workbox-webpack-plugin/node/inject-manifest.js index 0e74a1d55..865fa1a66 100644 --- a/test/workbox-webpack-plugin/node/inject-manifest.js +++ b/test/workbox-webpack-plugin/node/inject-manifest.js @@ -6,21 +6,25 @@ https://opensource.org/licenses/MIT. */ +const chai = require('chai'); +const chaiMatchPattern = require('chai-match-pattern'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const WorkerPlugin = require('worker-plugin'); -const expect = require('chai').expect; const fse = require('fs-extra'); const globby = require('globby'); -const upath = require('upath'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); const tempy = require('tempy'); +const upath = require('upath'); const webpack = require('webpack'); +const WorkerPlugin = require('worker-plugin'); const CreateWebpackAssetPlugin = require('../../../infra/testing/create-webpack-asset-plugin'); const validateServiceWorkerRuntime = require('../../../infra/testing/validator/service-worker-runtime'); const webpackBuildCheck = require('../../../infra/testing/webpack-build-check'); const {InjectManifest} = require('../../../packages/workbox-webpack-plugin/src/index'); +chai.use(chaiMatchPattern); +const {expect} = chai; + describe(`[workbox-webpack-plugin] InjectManifest (End to End)`, function() { const WEBPACK_ENTRY_FILENAME = 'webpackEntry.js'; const SRC_DIR = upath.join(__dirname, '..', 'static', 'example-project-1'); @@ -1557,9 +1561,9 @@ describe(`[workbox-webpack-plugin] InjectManifest (End to End)`, function() { }); describe(`[workbox-webpack-plugin] Non-compilation scenarios`, function() { - it(`•should support injecting a valid JSON manifest`, function(done) { + it(`should error when compileSrc is false and webpackCompilationPlugins is used`, function(done) { const outputDir = tempy.directory(); - console.log(outputDir); + const config = { mode: 'production', entry: upath.join(SRC_DIR, WEBPACK_ENTRY_FILENAME), @@ -1572,14 +1576,102 @@ describe(`[workbox-webpack-plugin] InjectManifest (End to End)`, function() { compileSrc: false, swDest: 'injected-manifest.json', swSrc: upath.join(__dirname, '..', 'static', 'injected-manifest.json'), + webpackCompilationPlugins: [{}], }), ], }; const compiler = webpack(config); - compiler.run(async (webpackError, stats) => { + compiler.run((webpackError, stats) => { + expect(webpackError).not.to.exist; + const statsJson = stats.toJson(); + expect(statsJson.warnings).to.be.empty; + expect(statsJson.errors).to.have.members([ + `Please check your InjectManifest plugin configuration:\nchild "webpackCompilationPlugins" fails because ["webpackCompilationPlugins" is not allowed]`, + ]); + done(); }); }); + + it(`should support injecting a manifest into a JSON file`, function(done) { + const outputDir = tempy.directory(); + + const config = { + mode: 'production', + entry: upath.join(SRC_DIR, WEBPACK_ENTRY_FILENAME), + output: { + filename: '[name].[hash:20].js', + path: outputDir, + }, + plugins: [ + new InjectManifest({ + compileSrc: false, + swDest: 'injected-manifest.json', + swSrc: upath.join(__dirname, '..', 'static', 'injected-manifest.json'), + }), + ], + }; + + const compiler = webpack(config); + compiler.run(async (webpackError, stats) => { + try { + webpackBuildCheck(webpackError, stats); + + const files = await globby('**', {cwd: outputDir}); + expect(files).to.have.length(2); + + const manifest = await fse.readJSON(upath.join(outputDir, 'injected-manifest.json')); + expect(manifest).to.matchPattern([{ + revision: /^[0-9a-f]{32}$/, + url: /^main\.[0-9a-f]{20}\.js$/, + }]); + + done(); + } catch (error) { + done(error); + } + }); + }); + }); + + it(`should support injecting a manifest into a CJS module`, function(done) { + const outputDir = tempy.directory(); + + const config = { + mode: 'production', + entry: upath.join(SRC_DIR, WEBPACK_ENTRY_FILENAME), + output: { + filename: '[name].[hash:20].js', + path: outputDir, + }, + plugins: [ + new InjectManifest({ + compileSrc: false, + swDest: 'injected-manifest.js', + swSrc: upath.join(__dirname, '..', 'static', 'injected-manifest.js'), + }), + ], + }; + + const compiler = webpack(config); + compiler.run(async (webpackError, stats) => { + try { + webpackBuildCheck(webpackError, stats); + + const files = await globby('**', {cwd: outputDir}); + expect(files).to.have.length(2); + + const manifest = require(upath.join(outputDir, 'injected-manifest.js')); + expect(manifest).to.matchPattern([{ + revision: /^[0-9a-f]{32}$/, + url: /^main\.[0-9a-f]{20}\.js$/, + }]); + + done(); + } catch (error) { + done(error); + } + }); }); }); diff --git a/test/workbox-webpack-plugin/static/injected-manifest.js b/test/workbox-webpack-plugin/static/injected-manifest.js new file mode 100644 index 000000000..8cb27bc98 --- /dev/null +++ b/test/workbox-webpack-plugin/static/injected-manifest.js @@ -0,0 +1 @@ +module.exports = self.__WB_MANIFEST; From 0ba25f8ca90eb551e0e2be4d064dde4ff4053e6a Mon Sep 17 00:00:00 2001 From: Jeff Posnick Date: Wed, 18 Mar 2020 16:26:54 -0400 Subject: [PATCH 3/3] Linting --- .../src/options/schema/webpack-inject-manifest.js | 2 +- packages/workbox-webpack-plugin/src/inject-manifest.js | 8 +++++++- test/workbox-webpack-plugin/static/injected-manifest.js | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js index d9ea21397..9f91171c4 100644 --- a/packages/workbox-build/src/options/schema/webpack-inject-manifest.js +++ b/packages/workbox-build/src/options/schema/webpack-inject-manifest.js @@ -25,7 +25,7 @@ swSrcBasename.description = 'derived from the swSrc file name'; const supportedOptions = Object.assign({ compileSrc: joi.boolean().default(defaults.compileSrc), webpackCompilationPlugins: joi.array().items(joi.object()).when( - 'compileSrc', {is: false, then: joi.forbidden()}), + 'compileSrc', {is: false, then: joi.forbidden()}), }, basePartial, injectPartial, webpackPartial); module.exports = joi.object().keys(supportedOptions).keys({ diff --git a/packages/workbox-webpack-plugin/src/inject-manifest.js b/packages/workbox-webpack-plugin/src/inject-manifest.js index 79035cd78..b866898fa 100644 --- a/packages/workbox-webpack-plugin/src/inject-manifest.js +++ b/packages/workbox-webpack-plugin/src/inject-manifest.js @@ -49,7 +49,7 @@ class InjectManifest { * @param {Array} [config.additionalManifestEntries] * A list of entries to be precached, in addition to any entries that are * generated as part of the build configuration. - * + * * @param {Array} [config.chunks] One or more chunk names whose corresponding * output files should be included in the precache manifest. * @@ -206,6 +206,12 @@ class InjectManifest { }); } + /** + * @param {Object} compilation The webpack compilation. + * @param {Object} parentCompiler The webpack parent compiler. + * + * @private + */ addSrcToAssets(compilation, parentCompiler) { const source = parentCompiler.inputFileSystem.readFileSync( this.config.swSrc).toString(); diff --git a/test/workbox-webpack-plugin/static/injected-manifest.js b/test/workbox-webpack-plugin/static/injected-manifest.js index 8cb27bc98..965ab2503 100644 --- a/test/workbox-webpack-plugin/static/injected-manifest.js +++ b/test/workbox-webpack-plugin/static/injected-manifest.js @@ -1 +1,8 @@ +/* + Copyright 2020 Google LLC + Use of this source code is governed by an MIT-style + license that can be found in the LICENSE file or at + https://opensource.org/licenses/MIT. +*/ + module.exports = self.__WB_MANIFEST;