15

After executing the create-react-app and enabling the service workers in the index.js, all relevant files from the src folder are cached. However some of my resources reside in the public directory. When I run npm run build, the asset-manifest.json and precache-manifest.HASH.js only contain the HTML, mangled JS and CSS (all stuff from the src folder).

How can I tell the service worker to additionally cache specific files from the public folder?

Here is the actually generated precache-manifest.e431838417905ad548a58141f8dc754b.js

self.__precacheManifest = [
  {
    "revision": "cb0ea38f65ed9eddcc91",
    "url": "/grafiti/static/js/runtime~main.cb0ea38f.js"
  },
  {
    "revision": "2c226d1577937984bf58",
    "url": "/grafiti/static/js/main.2c226d15.chunk.js"
  },
  {
    "revision": "c88c70e5f4ff8bea6fac",
    "url": "/grafiti/static/js/2.c88c70e5.chunk.js"
  },
  {
    "revision": "2c226d1577937984bf58",
    "url": "/grafiti/static/css/main.7a6fc926.chunk.css"
  },
  {
    "revision": "980026e33c706b23b041891757cd51be",
    "url": "/grafiti/index.html"
  }
];

But I want it to also contain entries for these urls:

  • /grafiti/icon-192.png
  • /grafiti/icon-512.png

They come from the public folder.

Alternatively: How can I add my icons for the manifest.webmanifest file in the src folder in a way such that I can reference them from the web manifest?

Felix K.
  • 14,171
  • 9
  • 58
  • 72
Constantin Berhard
  • 1,271
  • 10
  • 12

2 Answers2

4

I assume that you search for a solution (like me) which works without ejecting. This approach worked for me:

  1. Install the module react-app-rewired via yarn add -D react-app-rewired
  2. Replace react-scripts in your npm scripts (not needed for eject) inside your package.json

    /* package.json */
    
    "scripts": {
    -   "start": "react-scripts start",
    +   "start": "react-app-rewired start",
    -   "build": "react-scripts build",
    +   "build": "react-app-rewired build",
    -   "test": "react-scripts test --env=jsdom",
    +   "test": "react-app-rewired test --env=jsdom",
        "eject": "react-scripts eject"
    
  3. Create a config-overrides.js file in the same level as your package.json

My file looks like this (because I also want to add an own service worker with more sophisticated caching options for my apis) but globPatterns and the globDirectory is what your looking for. After adding this patterns, the matching files (in my case images, music and sounds) were added to the generated precache-manifest-{hash}.

const {InjectManifest} = require('workbox-webpack-plugin');
const path = require('path');

module.exports = {
  webpack: function(config, env) {
    config.plugins.push(
      new InjectManifest({
        globPatterns: [
          'images/*.png',
          'models/*.glb',
          'music/*.mp3',
          'sound/*.mp3'
        ],
        globDirectory: 'build',
        swSrc: path.join('src', 'custom-service-worker.js'),
        swDest: 'service-worker.js'
      })
    );

    return config;
  }
}

P.S.: if you need a custom-service.worker.js file to get it work, workbox.precaching.precacheAndRoute([])should be sufficient as content for this file, but the workbox docs are also very interesting for those caching stuff

Siya Mzam
  • 4,655
  • 1
  • 26
  • 44
Fabian
  • 3,139
  • 2
  • 23
  • 49
  • Hi @Fabian, what if I don't have a custom worker file? how to use the default one by create-react-app? thanks. – dli Dec 19 '19 at 04:37
  • Hi @dli, exactly for this cache and precache stuff I added my custom service worker. :) As far as I know it is not possible to modify the generated service worker of cra (without eject). This is why I suggested react-app-rewired + InjectManifest so you do not need to run eject. – Fabian Dec 19 '19 at 06:17
  • Thank you @Fabian, I will try. – dli Dec 20 '19 at 16:48
1

If you want to go for a very basic solution without ejecting or rewiring, you could also do the following:

Assuming you bootstrapped your project using something like npx create-react-app app --template cra-template-pwa-typescript, then in your service-worker.ts file replace this line...

precacheAndRoute(self.__WB_MANIFEST);

...with the following:

// default static file URLs generated during the build process
const staticBuildFiles = self.__WB_MANIFEST as PrecacheEntry[];

// extra file URLs you want to be precached
const extraPrecachedFiles: PrecacheEntry[] = [
  "/cats.f137634a.png",
  "/dogs.1d77f71c.jpg",
];

// register all assets to be precached by the service worker
precacheAndRoute([
  ...staticBuildFiles,
  ...essentialMediaFiles.map((url) => ({ url, revision: null }))
]);

For the sake of "cache-busting", the images in the solution above already include the revision information in form of hash numbers that were previously (manually) added to their filenames. As mentioned in the workbox docs:

To have the revisioning information in the URL itself, is generally a best practice for static assets.

However, if for various reasons you do not want to include the revision hashes in the filenames in your source code, you could also pass a unique string as environment variable to your build command that you can pick up later to let workbox use it as a revision string to bust your cache. E.g. you could use the unix timestamp of the build process (or go for something fancier like passing the current git commit hash information)...

REACT_APP_BUILD_VERSION=$(date +'%s') npm run build

... which you can then pick up in your service-worker.ts:

precacheAndRoute([
  ...staticBuildFiles,
  ...essentialMediaFiles.map((url) => ({ url, revision: process.env.REACT_APP_BUILD_VERSION }))
]);

The above code would result in all files using the REACT_APP_BUILD_VERSION as revision being invalidated on each build.

Felix K.
  • 14,171
  • 9
  • 58
  • 72