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

Guidlines for Using Workbox #5673

Open
Nick-t-go opened this Issue Nov 1, 2018 · 15 comments

Comments

Projects
None yet
8 participants
@Nick-t-go
Copy link

Nick-t-go commented Nov 1, 2018

I am on React-Scripts 2.0.5. I am eager to use the new Workbox functionality that is mentioned in the release notes for 2.0. I can't find any directions on how to do it though.

There are posts on Medium, Freecodecamp and such that are really just hacks to the 1.x versions of React-scripts.

I had thought since it has just been added as a dependency we could get some assistance on how and where to add configurations. When I include Workbox in my registerServiceWorker.js and attempt to set some strategies on assets it crashes the entire app once built.

Unable to import module 'workbox-routing' from 'WORKBOX_CDN_ROOT_URL/workbox-routing.prod.js'.

I understand serviceworkers are now opt-in.
I understand that in the latest version of create react app the service worker register file is now just called serviceworker.js.

I compared the old registerServiceWorker.js to the new serviceworker.js and they are the same. I also am able to build with the default service worker file and I do see Workbox in the application tab in Chrome dev tools. I know its working out of the box. I just want to be able to edit it. Guidance would be greatly appreciated.

@Rainson12

This comment has been minimized.

Copy link

Rainson12 commented Nov 13, 2018

Im stuck in the same boat..

@Rainson12

This comment has been minimized.

Copy link

Rainson12 commented Nov 13, 2018

After struggeling with how the magic works i finally figured out what needs to be done to be able to "customize" the service worker routes:

First run npm run eject
Than go into /config/webpack.config.prod.js
Replace
new WorkboxWebpackPlugin.GenerateSW({ clientsClaim: true, exclude: [/\.map$/, /asset-manifest\.json$/], importWorkboxFrom: 'cdn', navigateFallback: publicUrl + '/index.html', navigateFallbackBlacklist: [ // Exclude URLs starting with /_, as they're likely an API call new RegExp('^/_'), // Exclude URLs containing a dot, as they're likely a resource in // public/ and not a SPA route new RegExp('/[^/]+\\.[^/]+$'), ], }),

with

new WorkboxWebpackPlugin.InjectManifest({ swSrc: './public/service-worker.js', }),

where as swSrc point to your custom service-worker.

My Service-Worker.js looks like this:
if (workbox) {
console.log(Yay! Workbox is loaded 🎉);
} else {
console.log(Boo! Workbox didn't load 😬);
}
workbox.clientsClaim();
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

workbox.routing.registerRoute(
'http://localhost:3000',
workbox.strategies.networkFirst()
)

workbox.routing.registerNavigationRoute("/index.html", {
blacklist: [/^/_/, //[^/]+.[^/]+$/],
});

@Nick-t-go

This comment has been minimized.

Copy link

Nick-t-go commented Nov 13, 2018

As mentioned, @Rainson12 , there have already been a few articles about ejecting and doing something similar to what you have done. I believe the official way to customize using Workbox w/o ejecting is in the works. I personally don't want to eject. This issue here documents the progress of this new feature(I believe), #5369

@Rainson12

This comment has been minimized.

Copy link

Rainson12 commented Nov 14, 2018

@Nick-t-go from what i have seen in the other blog posts they all dont work with the current v2 create react app scripts as they all try to get rid of sw-precache and use workbox instead. Now with v2 you dont have to get rid of sw-precache anymore and because of that, all guides i have seen are obsolete and do not work anymore

@karannagupta

This comment has been minimized.

Copy link

karannagupta commented Nov 14, 2018

Here's how I've done it with CRA and without ejecting. It uses the workbox build workflow. (cra 2.x)

TL;DR - This involves not using the SW generated by create-react-app but generating your own SW with injectManifest mode using the Workbox build workflow. Build commands will be added to react-build script in package.json

NOTE:
Keep an eye on #5369 I think it will allow configuring Workbox using the Webpack workflow using a custom configuration file.

Step1 : Make sure you register for a SW in the main index.js file. i.e. change serviceWorker.unregister(); to serviceWorker.register();

Step2: Inside src/serviceWorker.js in window.addEventListener('load', () => { change the swURL to point to a new file (this is what will be created in the build step)
so, change this - const swUrl = ``${process.env.PUBLIC_URL}/service-worker.js``;
to const swUrl = ``${process.env.PUBLIC_URL}/sw.js``;

Step3: Create two files under src/ - sw-build.js and sw.js

Step4: In sw-build.js add the code as provided by Google's injectManifest example here -

const workboxBuild = require('workbox-build');
// NOTE: This should be run *AFTER* all your assets are built
const buildSW = () => {
  // This will return a Promise
  return workboxBuild.injectManifest({
    swSrc: 'src/sw.js', // this is your sw template file
    swDest: 'build/sw.js', // this will be created in the build step
    globDirectory: 'build',
    globPatterns: [
      '**\/*.{js,css,html,png}',
    ]
  }).then(({count, size, warnings}) => {
    // Optionally, log any warnings and details.
    warnings.forEach(console.warn);
    console.log(`${count} files will be precached, totaling ${size} bytes.`);
  });
}
buildSW();

Step 5: Inside your custom service worker template file i.e. src/sw.js add the injection point and your cache rules. e.g - in the file src/sw.js -

if ('function' === typeof importScripts) {
  importScripts(
    'https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js'
  );
  /* global workbox */
  if (workbox) {
    console.log('Workbox is loaded');

    /* injection point for manifest files.  */
    workbox.precaching.precacheAndRoute([]);

/* custom cache rules*/
workbox.routing.registerNavigationRoute('/index.html', {
      blacklist: [/^\/_/, /\/[^\/]+\.[^\/]+$/],
    });

workbox.routing.registerRoute(
      /\.(?:png|gif|jpg|jpeg)$/,
      workbox.strategies.cacheFirst({
        cacheName: 'images',
        plugins: [
          new workbox.expiration.Plugin({
            maxEntries: 60,
            maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
          }),
        ],
      })
    );

} else {
    console.log('Workbox could not be loaded. No Offline support');
  }
}

step 6: npm install workbox-build --save-dev
I think workbox-build is already included in node_modules, but good idea to make an entry in package.json

step 7: in package.json add the workbox build step
so, in package.json, under scripts add a new entry - "build-sw": "node ./src/sw-build.js"
and then change: "build": "react-scripts build
to "build": "react-scripts build && npm run build-sw

When your run npm run build you will see the the file sw.js inside your build folder. The CRA generated service-worker.js will continue to be there. You can run a build command to clear it, but it won't be used because we change the swURL in step2.

Again, all of this may not be relevant if #5369 provides a way to use the webpack flow by providing a custom configuration file.

@Nick-t-go

This comment has been minimized.

Copy link

Nick-t-go commented Nov 15, 2018

What if:

  1. We made our own custom copy of webpack.config.prod.js
    Which is currently hosting the WorkboxWebpackPlugin and is setting the config at line 515:

new WorkboxWebpack.GenerateSW... .

  1. Then in our build script include a script that replaces the default webpack.config.prod.js with our own custom copy of webpack.config.prod.js
  2. In our custom copy we can configure what we would like using the API set here.

Anyone have any ideas on whether or not this will work?

@karannagupta

This comment has been minimized.

Copy link

karannagupta commented Nov 16, 2018

Not sure if it will work because, in an un-ejecting CRA, we don't have control over the webpack.cofig. #5369 will address the same problem. I think they will provide a workbox.config that will be read by the CRA's webpack.config to allow customizations ...

Also discussed here - #5359

@Nick-t-go

This comment has been minimized.

Copy link

Nick-t-go commented Dec 7, 2018

@karannagupta finally circling back to this. going to give your suggestion a whirl. Is it possible for you to edit your post and fix the formatting. If it does work I think others will find is useful but more so if it is actually formatted properly to enhance readability.

@Nick-t-go

This comment has been minimized.

Copy link

Nick-t-go commented Dec 10, 2018

@karannagupta Hey that worked! Thanks for sharing. Anyone who stumbles upon this looking for a way to edit the config for using Workbox with CRA without ejecting please check out the comment above, looks like the official fix is on the slow burn.

@maciejsimka

This comment has been minimized.

Copy link

maciejsimka commented Dec 12, 2018

another way to get this to work is to use react-app-rewired, this override in config-overrides.js seems to be working:

const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

module.exports = function override(config, env) {
  config.plugins = config.plugins.map(plugin => {
    if (plugin.constructor.name === 'GenerateSW') {
      return new WorkboxWebpackPlugin.InjectManifest({
        swSrc: './src/sw.js',
        swDest: 'service-worker.js',
      });
    }

    return plugin;
  });

  return config;
};
@zwl1619

This comment has been minimized.

Copy link

zwl1619 commented Dec 16, 2018

I generated a project with npx create-react-app my-app-ts --typescript,
then I created two files, sw-build.js and sw.js under src/,

There is an error:

Cannot compile namespaces when the '--isolatedModules' flag is provided.

image

What should I do?

@karannagupta

This comment has been minimized.

Copy link

karannagupta commented Dec 16, 2018

I've excluded the SW files in tsconfig.json like this:

{
 "compilerOptions": {
  ...
  },
  "include": [
    "src/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts",
    "src/sw-*.js",
    "src/idb.js"
  ]
}
@sandys

This comment has been minimized.

Copy link

sandys commented Dec 20, 2018

@karannagupta - could you make a gist or a repo describing your workaround please? It's getting a bit hard following it.

@zhammer

This comment has been minimized.

Copy link

zhammer commented Jan 3, 2019

@karannagupta & others: i think #5369 would allow you to pass custom config to the GenerateSW plugin but wouldn’t allow you to use the InjectManifest plugin.

@bscaspar

This comment has been minimized.

Copy link

bscaspar commented Jan 18, 2019

Has anyone else tried @karannagupta's method and run into problems debugging locally? When I deploy with the ootb service worker, it is hooked up and running no problem. When I use the example here, and validate that the correct files are ouput into my /build directory (I've run through the directions multiple times at this point), I start running into errors - specifically it looks like CRA may be looking for "service-worker.js" when it compiles and deploys. When my service worker is named sw.js, it does not register successfully. If I change the name of my generated sw.js file to service-worker.js, a service worker is registered but it is the "no-op" version that CRA inserts when debugging locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment