Skip to content

Commit

Permalink
Add compileSrc option to InjectManifet webpack plugin (#2412)
Browse files Browse the repository at this point in the history
* WIP

* Support for compileSrc

* Linting
  • Loading branch information
jeffposnick authored Mar 19, 2020
1 parent a284bee commit 1a77f7f
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 18 deletions.
1 change: 1 addition & 0 deletions packages/workbox-build/src/options/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
babelPresetEnvTargets: ['chrome >= 56'],
cleanupOutdatedCaches: false,
clientsClaim: false,
compileSrc: true,
disableDevLogs: false,
exclude: [
/\.map$/,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -22,7 +23,9 @@ const swSrcBasename = (context) => {
swSrcBasename.description = 'derived from the swSrc file name';

const supportedOptions = Object.assign({
webpackCompilationPlugins: joi.array().items(joi.object()),
compileSrc: joi.boolean().default(defaults.compileSrc),
webpackCompilationPlugins: joi.array().items(joi.object()).when(
'compileSrc', {is: false, then: joi.forbidden()}),
}, basePartial, injectPartial, webpackPartial);

module.exports = joi.object().keys(supportedOptions).keys({
Expand Down
61 changes: 48 additions & 13 deletions packages/workbox-webpack-plugin/src/inject-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class InjectManifest {
* @param {Array<string>} [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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -211,6 +206,42 @@ 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();
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.
*
Expand Down Expand Up @@ -240,15 +271,19 @@ 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.`);
}

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);
Expand Down
127 changes: 123 additions & 4 deletions test/workbox-webpack-plugin/node/inject-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -1555,4 +1559,119 @@ describe(`[workbox-webpack-plugin] InjectManifest (End to End)`, function() {
});
});
});

describe(`[workbox-webpack-plugin] Non-compilation scenarios`, function() {
it(`should error when compileSrc is false and webpackCompilationPlugins is used`, 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'),
webpackCompilationPlugins: [{}],
}),
],
};

const compiler = webpack(config);
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);
}
});
});
});
8 changes: 8 additions & 0 deletions test/workbox-webpack-plugin/static/injected-manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +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;
1 change: 1 addition & 0 deletions test/workbox-webpack-plugin/static/injected-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
self.__WB_MANIFEST

0 comments on commit 1a77f7f

Please sign in to comment.