Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicit GenerateSW or InjectManifest webpack modes #1143

Merged
merged 7 commits into from
Dec 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/workbox-build/src/entry-points/options/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ module.exports = {
globIgnores: ['**/node_modules/**/*'],
globPatterns: ['**/*.{js,css,html}'],
globStrict: true,
// Use a different default for generateSWString.
generateSWStringGlobPatterns: [],
maximumFileSizeToCacheInBytes: 2 * 1024 * 1024,
clientsClaim: false,
navigateFallback: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
const joi = require('joi');

const commonGenerateSchema = require('./common-generate-schema');
const defaults = require('./defaults');

// Define some additional constraints.
module.exports = commonGenerateSchema.keys({
globDirectory: joi.string(),
globPatterns: joi.array().items(joi.string()).default(
defaults.generateSWStringGlobPatterns),
importScripts: joi.array().items(joi.string()).required(),
});
100 changes: 100 additions & 0 deletions packages/workbox-webpack-plugin/src/generate-sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

const {generateSWString} = require('workbox-build');

const convertStringToAsset = require('./lib/convert-string-to-asset');
const getAssetHash = require('./lib/get-asset-hash');
const getManifestEntriesFromCompilation =
require('./lib/get-manifest-entries-from-compilation');
const getWorkboxSWImports = require('./lib/get-workbox-sw-imports');
const sanitizeConfig = require('./lib/sanitize-config');
const stringifyManifest = require('./lib/stringify-manifest');

/**
* This class supports creating a new, ready-to-use service worker file as
* part of the webpack compilation process.
*
* Use an instance of `GenerateSW` in the
* [`plugins` array](https://webpack.js.org/concepts/plugins/#usage) of a
* webpack config.
*
* @module workbox-webpack-plugin
*/
class GenerateSW {
/**
* Creates an instance of GenerateSW.
*
* @param {Object} [config] See the
* [configuration guide](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#configuration)
* for all supported options and defaults.
*/
constructor(config = {}) {
this.config = Object.assign({}, {
chunks: [],
excludeChunks: [],
importScripts: [],
importWorkboxFrom: 'cdn',
swDest: 'service-worker.js',
}, config);
}

/**
* @param {Object} compilation The webpack compilation.
* @private
*/
async handleEmit(compilation) {
const workboxSWImports = getWorkboxSWImports(compilation, this.config);
const entries = getManifestEntriesFromCompilation(compilation, this.config);

const manifestString = stringifyManifest(entries);
const manifestAsset = convertStringToAsset(manifestString);
const manifestHash = getAssetHash(manifestAsset);
const manifestFilename = `precache-manifest.${manifestHash}.js`;
compilation.assets[manifestFilename] = manifestAsset;
this.config.importScripts.push(manifestFilename);

// workboxSWImports might be null if importWorkboxFrom is 'disabled'.
if (workboxSWImports) {
// workboxSWImport is an array, so use concat() rather than push().
this.config.importScripts = this.config.importScripts.concat(
workboxSWImports);
}

const sanitizedConfig = sanitizeConfig.forGenerateSWString(this.config);
// If globPatterns isn't explicitly set, then default to [], instead of
// the workbox-build.generateSWString() default.
sanitizedConfig.globPatterns = sanitizedConfig.globPatterns || [];
const serviceWorker = await generateSWString(sanitizedConfig);
compilation.assets[this.config.swDest] =
convertStringToAsset(serviceWorker);
}

/**
* @param {Object} [compiler] default compiler object passed from webpack
*
* @private
*/
apply(compiler) {
compiler.plugin('emit', (compilation, next) => {
this.handleEmit(compilation)
.then(next)
.catch(next);
});
}
}

module.exports = GenerateSW;
157 changes: 6 additions & 151 deletions packages/workbox-webpack-plugin/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,155 +14,10 @@
limitations under the License.
*/

const path = require('path');
const {getModuleUrl} = require('workbox-build');
const GenerateSW = require('./generate-sw');
const InjectManifest = require('./inject-manifest');

const generateManifest = require('./lib/generate-manifest-with-webpack');
const generateOrCopySW = require('./lib/generate-or-copy-sw');
const getAssetHash = require('./lib/utils/get-asset-hash');
const getEntries = require('./lib/get-manifest-entries-with-webpack');
const formatAsWebpackAsset = require('./lib/utils/format-as-webpack-asset');
const {setReadFile} = require('./lib/utils/read-file');

/**
* This module exports the `WorkboxWebpackPlugin`.
*
* Use an instance of `WorkboxWebpackPlugin` in the
* [`plugins` array](https://webpack.js.org/concepts/plugins/#usage) of a
* webpack config.
*
* @module workbox-webpack-plugin
*/
class WorkboxWebpackPlugin {
/**
* Creates an instance of WorkboxWebpackPlugin.
*
* @param {module:workbox-build.Configuration} [config] All the options as
* passed to {@link module:workbox-build.generateSWString}.
* @param {Array<String>} [config.chunks] Array of chunk names to use for
* generating the asset manifest. All assets belonging to the provided
* chunk names will be included in the asset manifest. Any chunks that
* are not listed or do not have a name will be removed.
* @param {Array<String>} [config.excludeChunks] Array of chunk names to
* exclude from the asset manifest. Any asset beloning to the provided
* chunk names will not be included in the asset manifest. This does
* not affect chunks with no chunk name.
* @param {string} [config.swSrc] Path to an existing service worker file.
* Will be added to the webpack compilation and prepended with
* importScripts('workbox-sw.js', 'file-manifest.js')
*/
constructor(config = {}) {
this.config = Object.assign({}, {
chunks: [],
excludeChunks: [],
importScripts: [],
importWorkboxFrom: 'cdn',
}, config);
}

/**
* @param {Object} compilation The webpack compilation.
* @private
*/
async handleEmit(compilation) {
const {
importWorkboxFrom,
swSrc,
} = this.config;
const serviceWorkerFilename = swSrc ? path.basename(swSrc) : 'sw.js';

let workboxSWImport;
switch (importWorkboxFrom) {
case 'cdn': {
workboxSWImport = getModuleUrl('workbox-sw');
break;
}

case 'local': {
// TODO: Implement.
break;
}

case 'disabled': {
// No-op.
break;
}

default: {
// If importWorkboxFrom is anything else, then treat it as the name of
// a webpack chunk that corresponds to the custom compilation of the
// Workbox code.
for (const chunk of compilation.chunks) {
// Make sure that we actually have a chunk with the appropriate name.
if (chunk.name === importWorkboxFrom) {
workboxSWImport = chunk.files;
this.config.excludeChunks.push(chunk.name);
break;
}
}

// If there's no chunk with the right name, treat it as a fatal error.
if (workboxSWImport === undefined) {
throw Error(`importWorkboxFrom was set to ` +
`'${importWorkboxFrom}', which is not an existing chunk name.`);
}
}
}

const entries = getEntries(compilation, this.config);
const fileManifest = generateManifest(entries);
const fileManifestAsset = formatAsWebpackAsset(fileManifest);
const fileManifestHash = getAssetHash(fileManifestAsset);
const manifestFilename = `precache-manifest.${fileManifestHash}.js`;
compilation.assets[manifestFilename] = fileManifestAsset;

this.config.importScripts.push(manifestFilename);

if (workboxSWImport) {
this.config.importScripts = this.config.importScripts.concat(
workboxSWImport);
}

const serviceWorker = await generateOrCopySW(this.config, swSrc);
compilation.assets[serviceWorkerFilename] =
formatAsWebpackAsset(serviceWorker);
}

/**
* @param {Object} [compiler] default compiler object passed from webpack
*
* @private
*/
apply(compiler) {
/**
* The plugin was instantiated and the webpack compilation has just begun.
* We configure the workbox-webpack-plugin/utils/read-file module to use
* webpack's compilation.inputFileSystem._readFile method for reading files.
*
* TODO: Determine if this is absolutely necessary. It might be possible to
* only do this in development (when the file system is a "memory" file
* system). If that is the case, it might be better to set different values
* for setReadFile using compiler.plugin('run') for production and
* compiler.plugin('watch-run') for development.
*/
setReadFile(compiler.inputFileSystem._readFile);

/**
* During the emit phase of the webpack compilation, we:
* 1. Get the manifest entries.
* 2. Use the entries to generate a file-manifest.
* 3. Generate a service worker with the file-manifest name and workbox-sw
* name, or copy a service worker from the config.swSrc, then prepend
* it with the required importScripts(workbox-sw.js, file-manifest.js).
* 4. Add both the file-manifest and the service worker to the webpack
* assets.
*/
compiler.plugin('emit', (compilation, next) => {
this.handleEmit(compilation)
.then(next)
.catch(next);
});
}
}

module.exports = WorkboxWebpackPlugin;
module.exports = {
GenerateSW,
InjectManifest,
};
Loading