GoogleChrome / workbox

📦 Workbox: JavaScript libraries for Progressive Web Apps
https://developers.google.com/web/tools/workbox/
MIT License
12.27k stars 806 forks source link

Uncaught ReferenceError: regeneratorRuntime is not defined #2493

Closed joetidee closed 4 months ago

joetidee commented 4 years ago

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

jeffposnick commented 4 years ago

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.)

joetidee commented 4 years ago

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?

jeffposnick commented 4 years ago

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.

joetidee commented 4 years ago

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?

jeffposnick commented 4 years ago

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—it'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—I 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.

joetidee commented 4 years ago

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'
);
jeffposnick commented 4 years ago

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

tpscrpt commented 4 years ago

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.

joetidee commented 4 years ago

@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?

tpscrpt commented 4 years ago

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
      }
    ]
  ]
joetidee commented 4 years ago

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

imbdb commented 4 years ago

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.

kirinnee commented 4 years ago

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
       }
   }
}
Richardinho commented 3 years ago

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?

mycolaos commented 3 years ago

Any news on this? I had this issue using Next.js and Next-PWA which uses Workbox. Because it's a third-party lib (next-pwa) that uses another third-party lib (workbox) inside a framework with its own webpack/babel setup (next.js) it's really crazy to go and try to understand why the bug appears and where and how it should be handled. I mean, this issue creates a domino effect for the third party libs and it should be handled somehow in Workbox either by including the polyfill and/or documenting this clearly so that everybody must know about it and handle it.

phil-w commented 3 years ago

When you set babelPresetEnvTargets ... 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.

Where would that setting be, precisely?

I'm trying to use the WorkboxPlugin.InjectManifest webpack 5 plugin and I can't get past this error. I just need to make this go away. Where exactly is the setting for " Workbox's babelPresetEnvTargets" ? What do I set it to in order to remove the error? I need to know what the incantation is, and where to put it. Anyone?

jeffposnick commented 3 years ago

Hey all. Sorry for not updating this for a while.

@phil-w, the babelPresetEnvTargets setting is available when you're using the GenerateSW plugin.

If you're using the InjectManifest plugin, then any Babel plugins from the parent compilation is applied to the child compilation that outputs your service worker.

@mycolaos, if you could point me to a GitHub project or some other code archive that includes a full setup which reproduces the problem you're seeing, I'll dive into it and tell you what needs to be configured where, and potentially see how those changes could be upstreamed to the source projects.

In general, one option that wasn't mentioned yet is that if you want to pull in the regenerator runtime into your service worker, it can be explicitly loaded from, e.g., the unpkg CDN (or using a local copy that you maintain). The mechanism for loading it varies depending on which webpack plugin you're using:

Again, you can replace those URLs with a copy of the regenerator-runtime code served from a local URL if you'd rather not rely on a CDN.

tomayac commented 4 months ago

Hi there,

Workbox is moving to a new engineering team within Google. As part of this move, we're declaring a partial bug bankruptcy to allow the new team to start fresh. We realize this isn't optimal, but realistically, this is the only way we see it working. For transparency, here're the criteria we applied:

Thanks, and we hope for your understanding! The Workbox team