Workbox: InjectManifest Plugin doesn't compile the serviceworker with webpack

Created on 2 Jun 2018  路  40Comments  路  Source: GoogleChrome/workbox

Library Affected:
workbox-webpack-plugin

Browser & Platform:
all browsers

Issue or Feature Request Description:
Ideally, the SW would go through the normal webpack compilation, so that the code gets transformed. Right now, the code seems to not be transformed by it, and as a result it creates a divide in our project as the transforms that apply are different. It also makes it difficult to use something like TypeScript or Flow in the ServiceWorker.

Feature Request P2 workbox-webpack-plugin

Most helpful comment

That鈥檚 one way to do it, but I think that鈥檚 working around the plugin, vs having the plugin work in the more ideal way.

All 40 comments

I have the same problem, in my custom sw I want to import some files from node_modules but webpack do not compile the swSrc.
Does anyone know how to do that?

If I understand the problem correctly, one way to solve this would be to first build the service worker file only and then build the rest of the app and specify swSrc to be the previously built service worker file.

To achieve this simply export multiple configs from webpack config file. In the first config, specify the service worker body file as the single entry point and do any transformations you want with it in the rest of the build. In the second build just use the file. Is this clear enough or should I give a complete example?

That鈥檚 one way to do it, but I think that鈥檚 working around the plugin, vs having the plugin work in the more ideal way.

@edgaraskazlauskas If you could provide an example that would be very helpful. I've never run through a webpack build process twice.

I'd like to use InejctManifestPlugin as follows, without the two-pass hack suggested above:

// webpack.config.js
module.exports = {
  entry : {
    sw: './src/sw.js'
  }, 
  output: {
    filename: '[name].js'
  },
  target: 'webworker',
  plugins: [
    new InjectManifestPlugin()
  ]
}

The way it currently works is not intuitive at all. I expect webpack to compile my service worker and expect the InjectManifestPlugin to inject the manifest in a single pass. Also, both the source and the destination of my service worker are already present.

Is this clear enough or should I give a complete example?

@edgaraskazlauskas I'd much appreciate an example. Thanks!

If you allow templating in the swSrc parameter, you could allow it to be defined as swSrc: './dist/js/sw.[contenthash].js' -- right?

If I am reading this thread correctly an example still has not been provided and it is still a problem. I think I am running into the same issue expressed by the OP. I have a service worker with module imports. I wanted to use the workbox Webpack plugin to inject a precache manifest how the benefit of the injected dynamic manifest comes at the cost of modules not being transpiled.

// sw.js (original)
import {precacheAndRoute} from 'workbox-precaching/precacheAndRoute.mjs'

precacheAndRoute(self.__precacheManifest)
// webpack.config.js
const {InjectManifest} = require('workbox-webpack-plugin')
const path = require('path')

module.exports = {
    entry: {
        sw: './src/sw.js',
    },
    plugins: [
        new InjectManifest({
            swSrc: path.resolve(__dirname, 'src/sw.js'),
            importWorkboxFrom: 'disabled',
        }),
    ],
}
// sw.js (bundled... not)
importScripts("precache-manifest.47565e861a50e4b271a0ce24cc9d8095.js");

import {precacheAndRoute} from 'workbox-precaching/precacheAndRoute.mjs'

precacheAndRoute(self.__precacheManifest)

As you can the bundled sw.js file does not include the webpackBootstrap and the module import is remains unchanged.

The inability to run the service worker through Webpacks transpiler pretty much renders the plugin useless for me.

Hoping to find some solutions.

Does the new guidance at https://developers.google.com/web/tools/workbox/guides/using-bundlers help any?

CC: @philipwalton

Does the new guidance at https://developers.google.com/web/tools/workbox/guides/using-bundlers help any?

Not much. I have the same struggles. I write everything in TypeScript and Webpack is using ts-loader to combile the TypeScript code. But the plugin totally ignores this. If I set swSrc to be a TypeScript file and swDest to be a JavaScript file, the plugin simply copies the TypeScript file and renames it, but doesn't run it through the defined webpack chain.

Does the new guidance at https://developers.google.com/web/tools/workbox/guides/using-bundlers help any?

I'm not sure which bits in particular were 'new' (I've only just started looking at this!)
I can see:

If you also want to use webpack to generate your service worker file (as described in this article), beware that the workbox-webpack-plugin's injectManifest config accepts a swSrc option that it will update with your precache manifest.
This can be problematic if you're using the same webpack configuration to generate your service worker file, since it may not exist by the time the workbox-webpack-plugin code runs.

This does acknowledge the problem, but doesn't seem to suggest a workaround.

I'm trying to migrate from the old sw-precache module, and have now had to generate my own root serviceworker.js file (rather than just additional files to include), which requires me to use calls to things like registerRoute directly, rather than it being defined via the webpack configuration. I'm trying to use imports so that my IDE actually picks up what exists, as I was getting lots of 'workbox' is not defined errors in my linter.

I tried to do a two-pass method where one pass will build from my serviceworker code to a transpiled one, and then run the injectManifest on the transpiled class, but it has issues with the filenames. Now some part of the manifest is trying to load https://app/my_main_app_file.js as in the manifest generated it has an additional slash at the front of the URL: "url": "//app/built-service-worker.js"

Is there a way to do this with workbox + webpack, or should I just stick with the sw-precache plugin instead which worked, but is deprecated?

I copy handleMake from serviceworker-webpack-plugin. And it's work for me. I'm new to webpack so It's maybe not perfectly solve this issue.

The code:
https://github.com/ALiangLiang/workbox/blob/feat/%231513/packages/workbox-webpack-plugin/src/inject-manifest.js

  plugins: [
    ...,
    new workboxPlugin.InjectManifest({
      swSrc: 'sw.js',
      entry: path.join(__dirname, '..', 'src/sw.js'),
    })
  ]

@ALiangLiang I can't quite get your workaround to work - In your case what does swSrc refer to? If I both let entry and swSrc refer to the same TypeScript file, the raw TS Code gets copied over to the Service Worker, if it is an empty dummy JS file the service worker is empty. (in addition to the importScripts(...))

I'd even argue that a swSrc is unneeded in combination with entry. I want to write the service worker by myself, since there is a lot of stuff workbox cannot possibly handle for me(session data, live updates, ...).
But I don't want to do this in a JS file that just gets copied over, but instead write it in proper TypeScript.
The only thing I need is to (quite literally) have the generated manifest injected into the compiled service worker so that I don't have to manually hunt down static assets.

@ALiangLiang I can't quite get your workaround to work - In your case what does swSrc refer to? If I both let entry and swSrc refer to the same TypeScript file, the raw TS Code gets copied over to the Service Worker, if it is an empty dummy JS file the service worker is empty.

I see your point. Maybe it is a typo. Did you try swDest: 'sw.js' ? That would make much more sense..

@Patrick-Sachs see
image

@ALiangLiang Why not propose a PR ?

@pungggi
As I said, I am a new to webpack. I just copied and tried this workaround. The mechanism is still not very clear. I can make a PR, but I don't think it's suitable for using it directly in the project. I need somebody help me to adjust the interface(options).

https://github.com/GoogleChrome/workbox/pull/2060

I can confirm ALiangLiang solution works:

  1. set up output as usual and set it also under swSrc
  2. set where to find your workbox template
  3. set the final worker filename
    image

to test quickly I ve set up an npm package called 'wrkbx' do not use in production

what is the final solution for this?

https://stackoverflow.com/questions/56710951/how-to-get-workboxplugin-injectmanifest-to-work

I also got stuck, please update the doc and provide a solution? Thanks!

I'm also having the exact same problem mentioned above by @henrylearn2rock in his stackoverflow post. I would've thought this to have been a standard thing that the injectManifest plugin would take care of. Makes the whole thing kinda pointless to not transform this code through webpack first 馃
@jeffposnick please help 鈽猴笍 (I've read through the entire guide that you posted and there's nothing in there about this and none of the steps resolve it)

We are actively working on a rewrite for the InjectManifest plugin that will perform a webpack child compilation on swSrc, in addition to populate the precache manifest.

It's still too early to use, but you can see a sneak-preview of the changes at https://github.com/GoogleChrome/workbox/compare/v5...wepback-v5?expand=1

We'll be rolling out the first alphas of v5 with this functionality following some additional tests and cleanup work鈥攍ikely in the order of a couple of weeks from now.

(I'm not 100% sure what the ergonomics of this will be if the swSrc is a TypeScript file, so I'm hoping for feedback from folks with that use case once we release our first alphas.)

This should be addressed by the current Workbox v5.0.0 alpha.

I'm trying to figure out how to make webpack and workbox plugin play together (compile and inject in one flow), but a bit confused about the correct setup.
Is this already supported in the latest version of the plugin (or will be only in v5) ? do you have some examples ?
Thanks!

In v5, if you list a service worker file as the swSrc in InjectManifest mode, webpack will automatically perform a (child) compilation on that file, in addition to replacing self.__WB_MANIFEST with the precache manifest that was derived from the main compilation's assets.

That was not the case in v4, so if you need that functionality, please try out the v5 beta.

Thanks! Finally it worked for me with plugin v4.3.1 and customized sw-src.js (with "import-from" statements instead of importScripts()),
1) entry: path.resolve('sw-src.js'),
output: {
path: SOME_OUTPUT_PATH,
filename: 'bundle.js',
},
2) new InjectManifest({
swSrc: 'bundle.js',
swDest: ${SOME_OUTPUT_PATH}/sw.js,
importWorkboxFrom: 'local',
globDirectory: SOME_OUTPUT_PATH,
globPatterns: [ '*/.{html,js,css}' ],

@jeffposnick , I'm trying the beta and was wondering if you plan on dropping the importsDirectory and precacheManifestFilename params when its no longer in beta

@ray-kim-12 yes, neither of those options should be supported in the current beta 1 release, as they are no longer relevant.

I've just tried v5.beta-1 because I also wanted a webpack/typescript build on my worker file. I got exactly that and saw the typical webpack generated code without any single config modification (I am using InjectPlugin). However when moving from v4 to v5 I noticed the 'workbox' global no longer exists and there is no mention in the changelogs.

Is there doc/example about how to access 'workbox' in v5?

workbox corresponded to an instance of the workbox-sw module, which is effectively a proxy that mapped a namespace of other Workbox modules with code to import the corresponding files from the CDN.

Because you're using a local bundle of all the Workbox code that you actually need now, the idea is that workbox-sw (and therefore the workbox global symbol) should no longer be needed.

If you're trying to accomplish something specific with the workbox symbol that you can't figure out how to do in v5, could you open a new GitHub issue (or Stack Overflow question) with the details? This is a closed GitHub issue and it would be best to move the discussion elsewhere.

Tried to use v5.0.0-beta.1, but can't get it to compile. I get an error: Can't find self.__WB_MANIFEST in your SW source.

// service-worker.js
precacheAndRoute(self.__WB_MANIFEST);
// webpack.config.js
...
config.plugins.push(
  new WorkboxWebpackPlugin.InjectManifest({
    // Use custom service worker implementation
    swSrc: path.resolve(projectConfig.srcPath, 'service-worker.js'),
    // Don't precache sourcemaps (they're large) and build asset manifest:
    exclude: [/\.map$/, /asset-manifest\.json$/],
  })
);

Oh, found the problem, it was because I was importing another file named service-worker.js from another directory. Renaming the file fixed the problem.

I have the same problem, in my custom sw I want to import some files from node_modules but webpack do not compile the swSrc.
Does anyone know how to do that?

Did you solve this issue?

Have you figured this out guys?

My problem seems the following:

i got service-worker in my src directory:

'use strict';
const utils = require('./utils')
const path = require('path')
const DotEnvWebpack = require('dotenv-webpack');
const WorkboxPlugin        = require('workbox-webpack-plugin');

console.log(path.resolve(__dirname, '../src/js/service-worker.js'));
module.exports = {
    mode: process.env.NODE_ENV !== 'development' ? 'production' : 'development',
    entry: {
        main: './src/js/service-worker.js'
    },

    output: {
        path: utils.resolve('dist'),
        publicPath: '/',
        filename: 'service-worker.js',
    },
    resolve: {
        alias: {
            '~': path.resolve('')
        }
    },

    plugins: [
        new DotEnvWebpack({systemvars: true}),
        new  WorkboxPlugin.InjectManifest({
            swSrc: path.resolve(__dirname, '../src/js/service-worker.js'),
            include:[
                /\.html$/,
                /\.js$/,
                /\.css$/,
                /\.woff2$/,
                /\.jpg$/,
                /\.png$/
            ]
        })
    ]
};

import { skipWaiting, clientsClaim, setCacheNameDetails } from 'workbox-core';
import { precacheAndRoute } from 'workbox-precaching';
import { registerNavigationRoute } from 'workbox-routing';

skipWaiting();
clientsClaim();

console.log(self.__precacheManifest, " hoops1");
console.log(self.__WB_MANIFEST, " hooops2");
precacheAndRoute(self.__precacheManifest);

When i run the above config of webpack, it makes service-worker.js file in dist, but doesn't contain anything such as importScripts('manifest') and also when running in browser,

console.log(self.__WB_MANIFEST, " hooops2"); //prints empty array
console.log(self.__precacheManifest, " hoops1"); //prints undefined

What do I do?

@novaknole the workbox-webpack-plugin is not intended to be used if your webpack entry point is your service worker file itself (e.g. in your case, ./src/js/service-worker.js).

Instead, it's intended to be used where your application's JavaScript code is the entry point, and then it will _also_ create a service worker file that will precache the JavaScript chunks generated by webpack after bundling your app's main JavaScript code. Does that make sense?

If you're looking specifically for how to use webpack to bundle a service worker file, I'd recommending taking a look at this guide: Using Bundlers (webpack/Rollup) with Workbox

@philipwalton Thanks man. I made it work.

@philipwalton

@novaknole the workbox-webpack-plugin is not intended to be used if your webpack entry point is your service worker file itself (e.g. in your case, ./src/js/service-worker.js).

Instead, it's intended to be used where your application's JavaScript code is the entry point, and then it will _also_ create a service worker file that will precache the JavaScript chunks generated by webpack after bundling your app's main JavaScript code. Does that make sense?

If you're looking specifically for how to use webpack to bundle a service worker file, I'd recommending taking a look at this guide: Using Bundlers (webpack/Rollup) with Workbox

ServiceWorkers are not constrained to just caching assets. It is quite handy to be able to import ES6 modules into a ServiceWorker without worrying about Webpack configuration.

There are a lot of repos that have workbox-webpack-plugin as a dependency. Many projects that have the workbox-webpack-plugin as a dependency just blackbox most of the Webpack plugins they use (because it's the easier thing).

If the defaults remain like this many people will have to spend tons of time just on creating alternative Webpack configurations.

if you are saying that ES5 should be a default in 2020 I cannot agree less and the sense you are talking about is lacking.

@philipwalton Thanks man. I made it work.

@novaknole How did you make it work :(

@changyeamoon still a problem or need help?

@novaknole It is resolved, turned out to be a different kind of issue. Thanks for checking up 馃槃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Fillah picture Fillah  路  3Comments

rafzan picture rafzan  路  3Comments

alexander-theijs picture alexander-theijs  路  3Comments

angelinaqj picture angelinaqj  路  4Comments

kaycebasques picture kaycebasques  路  4Comments