workbox icon indicating copy to clipboard operation
workbox copied to clipboard

How to pass variables to RouteMatchCallback?

Open piotr-cz opened this issue 2 years ago • 2 comments

Library Affected: workbox-build

Browser & Platform: all browsers

Issue or Feature Request Description: I'm using the vite-plugin-pwa package with the generateSW mode, which actually uses the generateSW method of workbox-build under the hood with given options (see example)

I'd like to use the Caching resources during runtime Workbox recipe to add a runtimeCaching to CDN assets. In future I'd like to extend the complexity of callback logic, so replacing it by RegExp or string is not an option.

The path to CDN assets is stored in environment variable

My setup is:

// vite.config.js
import { defineConfig, loadEnv } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
import type { RouteMatchCallback } from 'workbox-core'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, '.')
  const assetsRx = new RegExp(`^${env.VITE_ASSETS_CDN}/`)

  const routeMatchCallback: RouteMatchCallback = ({ request }) =>
    assetsRx.test(request.url) &&
    request.destination === 'image'

  return ({
    plugins: [
      workbox: {
        runtimeCaching: [{
          urlPattern: routeMatchCallback,
          handler: 'CacheFirst',
          //..
        }],
      },
    ],
  })
})

The problem is that generated service worker contains reference to assetsRx variable, which is undefined.

Uncaught ReferenceError: assetsRx is not defined
    at Route.match (sw.js:103:78)
    at Router.findMatchingRoute (Router.js:265:39)
    at Router.handleRequest (Router.js:142:40)
    at Router.js:56:42

In the workbox-build source code I found that the urlPattern is being transformed to string and injected in template so doesn't have access to upper scope (assetsRx variable is not defined in the template)

https://github.com/GoogleChrome/workbox/blob/5e69c3f6a74ea0e6b1a0d3261a6cde11d8075859/packages/workbox-build/src/lib/runtime-caching-converter.ts#L207-L209

=> "({ request }) => assetsRx.test(request.url) && request.destination === 'image'"

So my question is: How to pass variables to routeMatchCallback? I'd say it's a quite common scenario to match routes against values coming from outer scope.

A hacky way around this is to overwrite routeMatchCallback.toString method like this:

// Trick workbox
routeMatchCallback.toString = (asString => () => asString)(routeMatchCallback
  .toString()
  .replace(/assetsRx/, `/${assetsRx.source}/`)
)

but this doesn't feel right.

Using Function.prototype.bind is not an option either as function's body becomes a [native code] string

piotr-cz avatar Aug 12 '22 18:08 piotr-cz

I'm facing the same issue, I think it does not make sense to convert a function to a string, it should be evaluated as it is to be able to use variables on it as a programmer would expect.

JasterV avatar Nov 22 '22 11:11 JasterV

so, how to access env value in generated sw scope ?

sacru2red avatar Jul 26 '23 01:07 sacru2red

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

tomayac avatar Apr 25 '24 08:04 tomayac