Skip to content

Switch to Workbox's InjectManifest plugin #9141

Closed
@jeffposnick

Description

@jeffposnick

Is your proposal related to a problem?

Many want more control over their service worker. The current c-r-a setup uses Workbox's GenerateSW mode, which uses a declarative webpack configuration to generate the final service worker. Changing the behavior of the service worker therefore requires updating the webpack config, which is not allowed in c-r-a.

Describe the solution you'd like

In Workbox v5, the InjectManifest mode will take an "source" service worker file (either in JavaScript or TypeScript) and run it through a webpack child compilation, which allows you to write your service worker source file just like any other modern JavaScript code, including using ES module imports for the Workbox runtime.

The InjectManifest plugin also takes care of reading the list of assets generated by the webpack compilation and injecting a precache manifest into the service worker file that will precache those assets.

I'd like to switch from GenerateSW to InjectManifest in the c-r-a webpack configuration, and create a new packages/cra-template/template/service-worker.js (and TypeScript equivalent) that roughly contained:

import {clientsClaim} from 'workbox-core';
import {precacheAndRoute, createHandlerBoundToURL} from 'workbox-precaching';
import {registerRoute, NavigationRoute} from 'workbox-routing';

clientsClaim();

precacheAndRoute(self.__WB_MANIFEST);

const handler = createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html');
const navigationRoute = new NavigationRoute(handler, {
  denylist: [
    // Exclude URLs starting with /_, as they're likely an API call
    new RegExp('^/_'),
    // Exclude any URLs whose last part seems to be a file extension
    // as they're likely a resource and not a SPA route.
    // URLs containing a "?" character won't be blacklisted as they're likely
    // a route with query params (e.g. auth callbacks).
    new RegExp('/[^/?]+\\.[^/]+$'),
  ],
});
registerRoute(navigationRoute);

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

This would lead to a service worker that, by default, behaved equivalently to what the previous GenerateSW config created. The benefits are that any part of that service-worker.js could be edited by the end developer, leading to, e.g., custom service worker runtime caching.

I would also like to rename the current packages/cra-template/template/serviceWorker.js file to packages/cra-template/template/serviceWorkerRegistration.js to make it clearer that the file is used in the window context to perform registration, and that it does not include the code for the service worker itself.

In general, giving developers control over their source service worker in this manner should be relatively decoupled from the underlying webpack config. Even if developers made significant changes to their source service worker, by adding in new Workbox modules or other third-party code, the same webpack configuration should still be compatible. The one requirement is that the developer would need to keep the string self.__WB_MANIFEST somewhere in their source service worker file, as InjectManifest checks for this and will fail if it's not present. (It could be inside of a comment like, e.g. /* self.__WB_MANIFEST */ as a workaround for developers who don't want to use precaching.)

Describe alternatives you've considered

#8822 is an alternative that will just update to Workbox v5.1.2 and keep using the GenerateSW mode, which is okay, but I think developers would be happier if they had the ability to control their service worker. Workbox v5 allows that to be done cleanly in a way that wasn't possible with prior releases.

Additional context

See #7966 for a similar proposal.

The difference between the two mode is described in more detail at https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#which_plugin_to_use

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions