Workbox: Uncaught ReferenceError: regeneratorRuntime is not defined

Created on 4 May 2020  路  14Comments  路  Source: GoogleChrome/workbox

Library Affected:
workbox-webpack-plugin

Browser & Platform:
Was found on Chrome v51

Bug:
I am using this plugin like so:

new GenerateSW({
    babelPresetEnvTargets: ["last 4 versions", "safari >= 7"],
    cacheId: 'sw',
    cleanupOutdatedCaches: true,
    clientsClaim: true,
    exclude: [/\.map$/, /stats\.json$/],
    inlineWorkboxRuntime: true,
    runtimeCaching: [
        {
            urlPattern: /^https:\/\/fonts\.googleapis\.com.*/,
            handler: 'StaleWhileRevalidate',
        },
    ],
    skipWaiting: true,
    sourcemap: false,
    swDest: 'sw.js',
}),

This error is coming through into the browser console:
Uncaught ReferenceError: regeneratorRuntime is not defined

Bug workbox-webpack-plugin

Most helpful comment

When you set babelPresetEnvTargets, there's a single transpilation performed, and a single service worker file output. You can think of it as meeting the requirements of the "lowest common denominator" based on your babelPresetEnvTargets configuration.

If your babelPresetEnvTargets configuration includes a browser that doesn't have support for generators, then the single service worker file that's output will include code that references regeneratorRuntime. If regeneratorRuntime isn't available (which it wouldn't be if you're using a transpilation configured by Workbox), then the code will fail to run on any browser鈥攊t's failing on modern browsers that have generator support because there's now a dependency on the missing regeneratorRuntime.

As suggested, you can either perform your own transpilation after Workbox produces a service worker file, and in that transpilation, add in support for the regeneratorRuntime (check the babel documentation for exactly how you'd do that). Alternatively, you could ensure that you set Workbox's babelPresetEnvTargets to only include browsers that have generator support, and the service worker file that Workbox creates should work across all of those browsers.

I'm leaving this issue open because it's definitely something that we could help prevent from happening鈥擨 think Workbox could conditionally add the regeneratorRuntime to the service workers it transpiles without incurring a larger bundle size when it's not needed. But what I'm offering in the meantime is some workarounds that should address your issue.

All 14 comments

As a workaround, I'd suggest omitting babelPresetEnvTargets from your configuration, and explicitly running your babel configuration against your generated service worker file after the webpack compilation completes.

Alternatively, you can switch to InjectManifest mode and webpack will compile your swSrc file using the options you have configured for your main compilation, including the babel config.

(FWIW, the earliest version of Safari to support service workers is Safari 11.1, so targeting safari >= 7 in your transpilation might not be what you want.)

Could you please explain why removing babelPresetEnvTargets and transpiling sw.js after webpack has created it are different from each other - they appear to be doing the exact same thing?

If you run your own babel transpilation process after Workbox generates a service worker, you have control over adding in additional configuration options, like including the https://babeljs.io/docs/en/babel-plugin-transform-runtime or https://babeljs.io/docs/en/babel-plugin-transform-regenerator plugins for compatibility with browsers that support service workers but don't support other modern JavaScript features.

As per #2506, I'm surprised that adding this babelPresetEnvTargets property with those values would prevent it from working in Chrome 81 (Desktop) because this browser is modern and supports Service Workers and generators?
I'm not quite understanding what is not happening during the transpilation process that I need to add in to make it work.
Is it a case that this workbox-webpack-plugin needs to instead use the babel config file as a whole, rather than just have its presets set using babelPresetEnvTargets?
It also feels like i just need to be importing regenerator-runtime/runtime somewhere, but there is no easy way to do this - can this plugin simply include it by default?

When you set babelPresetEnvTargets, there's a single transpilation performed, and a single service worker file output. You can think of it as meeting the requirements of the "lowest common denominator" based on your babelPresetEnvTargets configuration.

If your babelPresetEnvTargets configuration includes a browser that doesn't have support for generators, then the single service worker file that's output will include code that references regeneratorRuntime. If regeneratorRuntime isn't available (which it wouldn't be if you're using a transpilation configured by Workbox), then the code will fail to run on any browser鈥攊t's failing on modern browsers that have generator support because there's now a dependency on the missing regeneratorRuntime.

As suggested, you can either perform your own transpilation after Workbox produces a service worker file, and in that transpilation, add in support for the regeneratorRuntime (check the babel documentation for exactly how you'd do that). Alternatively, you could ensure that you set Workbox's babelPresetEnvTargets to only include browsers that have generator support, and the service worker file that Workbox creates should work across all of those browsers.

I'm leaving this issue open because it's definitely something that we could help prevent from happening鈥擨 think Workbox could conditionally add the regeneratorRuntime to the service workers it transpiles without incurring a larger bundle size when it's not needed. But what I'm offering in the meantime is some workarounds that should address your issue.

I have now switched to the InjectManifest method:

new InjectManifest({
            dontCacheBustURLsMatching: /^\/static\/modern\/(components\/([a-z0-9-]+\/)*|scenes\/([a-z0-9-]+\/)*)?[0-9a-z-]+\.[0-9a-z-]+\.js$/,
            exclude: [/\.map$/, /stats\.json$/],
            swDest: 'sw.js',
            swSrc: './src/utilities/service-worker/sw-source.js',
        }),

Our webpack babel config states:

"last 4 versions", "safari >= 7"

But when I run es-check against this (checking for es5) it fails with:

路 erroring file: ./dist/static/legacy/sw.js
路 error: SyntaxError: The keyword 'const' is reserved (1:1414)

Am I correct in thinking that Webpack should have transpiled sw-source.js and sw.js?

When I then run the following command on the sw.js file, it transpiles to es5 (note that prod-client-legacy also states last 4 versions", "safari >= 7):

shell.exec(
    './node_modules/.bin/babel --config-file ../../babel.config.js --env-name "prod-client-legacy" ./dist/static/legacy/sw.js --out-file ./dist/static/legacy/sw.js'
);

InjectManifest mode will create a childCompiler instance under the hood, and a lot of of the webpack configuration is inherited from the main, "parent" compilation. I've heard that the plugins from the parent compilation aren't applied consistently, though, so InjectManifest supports a webpackCompilationPlugins option that you could use to add in, e.g., your plugin to perform the transpilation:

https://github.com/GoogleChrome/workbox/blob/04ba6442c466d2e8197fe586672143d201af3a61/packages/workbox-webpack-plugin/src/inject-manifest.js#L118-L119

Alternatively, you can switch to InjectManifest mode and webpack will compile your swSrc file using the options you have configured for your main compilation, including the babel config.

Is this true? I can't get passed the error in the OP with using what you've suggested in this thread. Here's my stuff: https://github.com/JeremiGendron/react-typescript-pwa-poc/tree/master/lib/frontend

Check config/webpack.config.js, run yarn build, open with serve and see error.

(I'm aware my service worker is not very well formed, but shouldn't be getting this error anyway)

LMK if I missed something

EDIT: almost certain it's just not being run through babel? Would make sense since it's a loader and not a plugin.

@jeffposnick I'm trying to utilize the babel loader configuration from within my webpack config file. Given this is a loader and not a plugin, should this be expected to work?

On my end, seems like it's a general misconfiguration issue. Using async functions at all (which were previously only in my SW entry) causes this error to throw. So it's a problem with by webpack/babel config and not the plugin.

EDIT: adding the following to my .babelrc fixed the thing:

"plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": false,
        "helpers": true,
        "regenerator": true, // important line
        "useESModules": false
      }
    ]
  ]

@JeremiGendron Isn't "regenerator": true the default though - how could this have made the difference?
https://babeljs.io/docs/en/babel-plugin-transform-runtime

Alternatively, you can switch to InjectManifest mode and webpack will compile your swSrc file using the options you have configured for your main compilation, including the babel config.

I am using injectManifest and facing the below issue.

service-worker.js:158 Uncaught (in promise) TypeError: Cannot read property 'async' of undefined
    at Object.navigationHandler [as handle] (service-worker.js:158)
    at Router.handleRequest (Router.js:196)
    at eval (Router.js:63)

Webpack plugin

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

Relevant service worker code

navigationPreload.enable();
const networkOnly = new NetworkOnly();
const navigationHandler = async (params) => {
  try {
    // Attempt a network request.
    return await networkOnly.handle(params);
  } catch (error) {
    // If it fails, return the cached HTML.
    return caches.match(FALLBACK_HTML_URL, {
      cacheName: CACHE_NAME,
    });
  }
};

// Register this strategy to handle all navigations.
registerRoute(
  new NavigationRoute(navigationHandler)
);

this code from workbox documentation and I am not able to run it.

An alternative workaround is to ignore babel or use an alternative babel.config

Example (for service worker file sw.js)

webpack.config.js

// normal babel config (with preset env)
const babelLoader= {
    loader: 'babel-loader',
    options: {configFile: path.resolve(__dirname, "./babel.config.js")}
}

// babel config for work box (without preset env)
const workboxBabelLoader = {
    loader: 'babel-loader',
    options: {configFile: path.resolve(__dirname, "./babel.workbox.config.js")}
}


// Normal Babel Rule
const babelRule = {
    test: /\.jsx?$/,
    // ignore anything with workbox and sw.js within path
    exclude: /(workbox|sw.js)/,
    use: [babelLoader]
};

// WorkBox Babel Rule
const workboxBabelRule = {
     // may need to tweak this using a function instead of just regex
     test: /(workbox|sw.js)/,
     use: [workboxBabelLoader]
}

Use workboxBabelRule and babelRule

export default = {
   ...
   module: {
       rules: {
             ....
             // add the rules here
       }
   }
}

I got the same error when using workbox-webpack-plugin 5.1.4 and webpack 5.2.0 with InjectManifest(). I have fixed the problem by setting the target browser in my .babelrc to a more recent browser (Chrome 58). I'd be interested to know what the minimum value I can set this to so as to cover the greatest number of browsers.

I'm a bit confused by the answer given above about regeneratorRuntime not being available after compilation:

If regeneratorRuntime isn't available (which it wouldn't be if you're using a transpilation configured by Workbox)

since regeneratorRuntime is a dependency of both Babel and of Workbox?

If I understand correctly, regeneratorRuntime is a kind of polyfill for generator functions, so why would a compilation process targeting older browsers leave it out?

Was this page helpful?
0 / 5 - 0 ratings