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

Webpack import workbox from local #1148

Merged
merged 2 commits into from
Jan 2, 2018
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ const commonGenerateSchema = require('./common-generate-schema');
module.exports = commonGenerateSchema.keys({
globDirectory: joi.string(),
importScripts: joi.array().items(joi.string()).required(),
modulePathPrefix: joi.string(),
});
9 changes: 5 additions & 4 deletions packages/workbox-webpack-plugin/src/generate-sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class GenerateSW {
* @private
*/
async handleEmit(compilation) {
const workboxSWImports = getWorkboxSWImports(compilation, this.config);
const workboxSWImports = await getWorkboxSWImports(
compilation, this.config);
const entries = getManifestEntriesFromCompilation(compilation, this.config);

const manifestString = stringifyManifest(entries);
Expand Down Expand Up @@ -89,10 +90,10 @@ class GenerateSW {
* @private
*/
apply(compiler) {
compiler.plugin('emit', (compilation, next) => {
compiler.plugin('emit', (compilation, callback) => {
this.handleEmit(compilation)
.then(next)
.catch(next);
.then(callback)
.catch(callback);
});
}
}
Expand Down
14 changes: 10 additions & 4 deletions packages/workbox-webpack-plugin/src/inject-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@ class InjectManifest {
* @private
*/
async handleEmit(compilation, readFile) {
const workboxSWImports = getWorkboxSWImports(compilation, this.config);
if (this.config.importWorkboxFrom === 'local') {
throw new Error(`importWorkboxFrom can not be set to 'local' when using` +
` InjectManifest. Please use 'cdn' or a chunk name instead.`);
}

const workboxSWImports = await getWorkboxSWImports(
compilation, this.config);
let entries = getManifestEntriesFromCompilation(compilation, this.config);

const sanitizedConfig = sanitizeConfig.forGetManifest(this.config);
Expand Down Expand Up @@ -116,10 +122,10 @@ ${originalSWString}
* @private
*/
apply(compiler) {
compiler.plugin('emit', (compilation, next) => {
compiler.plugin('emit', (compilation, callback) => {
this.handleEmit(compilation, compiler.inputFileSystem._readFile)
.then(next)
.catch(next);
.then(callback)
.catch(callback);
});
}
}
Expand Down
25 changes: 19 additions & 6 deletions packages/workbox-webpack-plugin/src/lib/get-workbox-sw-imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,39 @@
limitations under the License.
*/

const {getModuleUrl} = require('workbox-build');
const {copyWorkboxLibraries, getModuleUrl} = require('workbox-build');

/**
* @param {Object} compilation The webpack compilation.
* @param {Object} config The options passed to the plugin constructor.
* config.excludeChunks may be modified by this function if
* config.importWorkboxFrom is set to a chunk name.
* - config.excludeChunks may be modified by this function if
* config.importWorkboxFrom is set to a chunk name.
* - config.modulePathPrefix may be modified by this function if
* config.importWorkboxFrom is set to 'local'.
* @return {Array<String>|null} A list of URLs to use to import the Workbox
* runtime code, or null if importWorkboxFrom is 'disabled'.
* @private
*/
function getWorkboxSWImport(compilation, config) {
async function getWorkboxSWImport(compilation, config) {
switch (config.importWorkboxFrom) {
case 'cdn': {
return [getModuleUrl('workbox-sw')];
}

case 'local': {
// TODO: Implement.
throw Error(`importWorkboxFrom 'local' is not yet supported.`);
// This will create a local copy of the Workbox runtime libraries in
// the output directory, independent of the webpack build pipeline.
// In general, this should work, but one thing to keep in mind is that
// when using the webpack-dev-server, the output will be created on
// disk, rather than in the in-memory filesystem. (webpack-dev-server will
// still be able to serve the runtime libraries from disk.)
const wbDir = await copyWorkboxLibraries(compilation.options.output.path);
const workboxSWImport = compilation.options.output.publicPath || ''
+ wbDir + '/workbox-sw.js';
// We need to set this extra option in the config to ensure that the
// workbox library loader knows where to get the local libraries from.
config.modulePathPrefix = wbDir;
return [workboxSWImport];
}

case 'disabled': {
Expand Down
114 changes: 114 additions & 0 deletions test/workbox-webpack-plugin/node/generate-sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const expect = require('chai').expect;
const fse = require('fs-extra');
const glob = require('glob');
const path = require('path');
const tempy = require('tempy');
const vm = require('vm');
Expand All @@ -13,6 +14,7 @@ const {getModuleUrl} = require('../../../packages/workbox-build/src/lib/cdn-util

describe(`[workbox-webpack-plugin] GenerateSW (End to End)`, function() {
const WEBPACK_ENTRY_FILENAME = 'webpackEntry.js';
const WORKBOX_DIRECTORY_PREFIX = 'workbox-';
const WORKBOX_SW_FILE_NAME = getModuleUrl('workbox-sw');
const SRC_DIR = path.join(__dirname, '..', 'static', 'example-project-1');

Expand Down Expand Up @@ -159,6 +161,118 @@ describe(`[workbox-webpack-plugin] GenerateSW (End to End)`, function() {
});
});

it(`should support setting importWorkboxFrom to 'local'`, function(done) {
const FILE_MANIFEST_NAME = 'precache-manifest.b6f6b1b151c4f027ee1e1aa3061eeaf7.js';
const outputDir = tempy.directory();
const config = {
entry: {
entry1: path.join(SRC_DIR, WEBPACK_ENTRY_FILENAME),
entry2: path.join(SRC_DIR, WEBPACK_ENTRY_FILENAME),
},
output: {
filename: '[name]-[chunkhash].js',
path: outputDir,
},
plugins: [
new GenerateSW({importWorkboxFrom: 'local'}),
],
};

const compiler = webpack(config);
compiler.run(async (webpackError) => {
if (webpackError) {
return done(webpackError);
}

const swFile = path.join(outputDir, 'service-worker.js');
try {
// Validate the copied library files.
const libraryFiles = glob.sync(`${WORKBOX_DIRECTORY_PREFIX}*/*.js*`,
{cwd: outputDir});

const modulePathPrefix = path.dirname(libraryFiles[0]);

const basenames = libraryFiles.map((file) => path.basename(file));
expect(basenames).to.eql([
'workbox-background-sync.dev.js',
'workbox-background-sync.dev.js.map',
'workbox-background-sync.prod.js',
'workbox-background-sync.prod.js.map',
'workbox-broadcast-cache-update.dev.js',
'workbox-broadcast-cache-update.dev.js.map',
'workbox-broadcast-cache-update.prod.js',
'workbox-broadcast-cache-update.prod.js.map',
'workbox-cache-expiration.dev.js',
'workbox-cache-expiration.dev.js.map',
'workbox-cache-expiration.prod.js',
'workbox-cache-expiration.prod.js.map',
'workbox-cacheable-response.dev.js',
'workbox-cacheable-response.dev.js.map',
'workbox-cacheable-response.prod.js',
'workbox-cacheable-response.prod.js.map',
'workbox-core.dev.js',
'workbox-core.dev.js.map',
'workbox-core.prod.js',
'workbox-core.prod.js.map',
'workbox-google-analytics.dev.js',
'workbox-google-analytics.dev.js.map',
'workbox-google-analytics.prod.js',
'workbox-google-analytics.prod.js.map',
'workbox-precaching.dev.js',
'workbox-precaching.dev.js.map',
'workbox-precaching.prod.js',
'workbox-precaching.prod.js.map',
'workbox-routing.dev.js',
'workbox-routing.dev.js.map',
'workbox-routing.prod.js',
'workbox-routing.prod.js.map',
'workbox-strategies.dev.js',
'workbox-strategies.dev.js.map',
'workbox-strategies.prod.js',
'workbox-strategies.prod.js.map',
'workbox-sw.js',
'workbox-sw.js.map',
]);


// The correct importScripts path should use the versioned name of the
// parent workbox libraries directory. We don't know that version ahead
// of time, so we ensure that there's a match based on what actually
// got copied over.
const workboxSWImport = libraryFiles.filter(
(file) => file.endsWith('workbox-sw.js'))[0];

// First, validate that the generated service-worker.js meets some basic assumptions.
await validateServiceWorkerRuntime({swFile, expectedMethodCalls: {
importScripts: [[
FILE_MANIFEST_NAME,
workboxSWImport,
]],
setConfig: [[{modulePathPrefix}]],
suppressWarnings: [[]],
precacheAndRoute: [[[], {}]],
}});

// Next, test the generated manifest to ensure that it contains
// exactly the entries that we expect.
const manifestFileContents = await fse.readFile(path.join(outputDir, FILE_MANIFEST_NAME), 'utf-8');
const context = {self: {}};
vm.runInNewContext(manifestFileContents, context);

const expectedEntries = [{
url: 'entry2-17c2a1b5c94290899539.js',
}, {
url: 'entry1-d7f4e7088b64a9896b23.js',
}];
expect(context.self.__precacheManifest).to.eql(expectedEntries);

done();
} catch (error) {
done(error);
}
});
});

it(`should honor the 'chunks' whitelist config`, function(done) {
const FILE_MANIFEST_NAME = 'precache-manifest.77e720b5deee9dafb4df79d5e4c2f2e0.js';
const outputDir = tempy.directory();
Expand Down
29 changes: 29 additions & 0 deletions test/workbox-webpack-plugin/node/inject-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,35 @@ describe(`[workbox-webpack-plugin] InjectManifest (End to End)`, function() {
}
});
});

it(`should throw when importWorkboxFrom is set to 'local'`, function(done) {
const outputDir = tempy.directory();
const config = {
entry: {
entry1: path.join(SRC_DIR, WEBPACK_ENTRY_FILENAME),
},
output: {
filename: '[name]-[chunkhash].js',
path: outputDir,
},
plugins: [
new InjectManifest({
importWorkboxFrom: 'local',
swSrc: SW_SRC,
}),
],
};

const compiler = webpack(config);
compiler.run((webpackError) => {
if (webpackError) {
expect(webpackError.message.includes('importWorkboxFrom'));
done();
} else {
done('Unexpected success.');
}
});
});
});

describe(`[workbox-webpack-plugin] multiple chunks`, function() {
Expand Down