Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I customize my Service Worker based on environment variables?

Edit: This looks like a duplicate of this Unresolved Question. Do I mark this as answered, or delete?

I am using InjectManifest from workbox-webpack-plugin inside a Vue CLI 3 app. The custom service worker that I am injecting into has handling for Firebase Cloud Messaging (FCM). I need to listen for messages from different senders based on my environment (local, staging, and production).

Ideally, service-worker.js would look like this:

importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');

firebase.initializeApp({
    'messagingSenderId': process.env.VUE_APP_FCM_SENDER_ID
});

However, this code doesn't seem to be touched by webpack, as the output service worker still reads process.env.VUE_APP_FCM_SENDER_ID instead of the hardcoded key.

How can I run my service worker through webpack in order to resolve environment variables?

like image 968
Madbarron Avatar asked Jan 24 '19 22:01

Madbarron


People also ask

How do I use process environment in a react service worker?

env file with your variables set in the root of your project, then you can update ./src/serviceWorker. js to include your process. env variables as a query string. In the register function in serviceWorker.

How do I set an environment variable on a server?

In the Settings window, under Related Settings, click Advanced system settings. On the Advanced tab, click Environment Variables. Click New to create a new environment variable. Click Edit to modify an existing environment variable.

How do you manage environment variables?

There are multiple solutions: you ask each developer to set the value in their environment before launching the application. you add some logic at the application's initialization to use the API key environment variable value if it exists, otherwise, fall back to the plain configuration file.


Video Answer


2 Answers

It's probably way too late for you now but for others this is how I went on about getting around this. This process still utilizes .env file for your environment related variables.
The idea is to create a new script that loads the .env file which creates a new file populated with the variables from .env file.
After the build process we simply import the newly generated file in sw.js for it to be used.

Here are the steps.
First create a file called swEnvbuild.js which will be your script that runs after webpack

//swEnvBuild.js - script that is separate from webpack
require('dotenv').config(); // make sure you have '.env' file in pwd
const fs = require('fs');

fs.writeFileSync('./dist/public/swenv.js',
`
const process = {
  env: {
    VUE_APP_FCM_SENDER_ID: conf.VUE_APP_FCM_SENDER_ID
  }
}
`);

Secondly, we import the file that was generated from swEnvBuild.js called swenv.js in our sw.js.

// sw.js
importScripts('swenv.js'); // this file should have all the vars declared
console.log(process.env.VUE_APP_FCM_SENDER_ID);

And lastly, for this to work with one command just add the following in your npm scripts (assuming that you're running either Linux/Mac).

scripts: {
  "start": "webpack && node swEnvBuild.js"
}

Hopefully, that should do the trick. I wish there was much cleaner way to do this so I'd be happy to know how other's solution too.

like image 180
shriek Avatar answered Oct 18 '22 09:10

shriek


I had this same problem and the key is getting the webpack build process to output the env vars that it uses so they can be imported into the service worker. This saves you from having to duplicate your env var definitions into something else that pre-processes your service worker (which is just messy anyway because that file is in source control).

  1. create a new Webpack plugin

    // <project-root>/vue-config/DumpVueEnvVarsWebpackPlugin.js
    const path = require('path')
    const fs = require('fs')
    
    const pluginName = 'DumpVueEnvVarsWebpackPlugin'
    
    /**
     * We to configure the service-worker to cache calls to both the API and the
     * static content server but these are configurable URLs. We already use the env var
     * system that vue-cli offers so implementing something outside the build
     * process that parses the service-worker file would be messy. This lets us
     * dump the env vars as configured for the rest of the app and import them into
     * the service-worker script to use them.
     *
     * We need to do this as the service-worker script is NOT processed by webpack
     * so we can't put any placeholders in it directly.
     */
    
    module.exports = class DumpVueEnvVarsWebpackPlugin {
      constructor(opts) {
        this.filename = opts.filename || 'env-vars-dump.js'
      }
    
      apply(compiler) {
        const fileContent = Object.keys(process.env)
          .filter(k => k.startsWith('VUE_APP_'))
          .reduce((accum, currKey) => {
            const val = process.env[currKey]
            accum += `const ${currKey} = '${val}'\n`
            return accum
          }, '')
        const outputDir = compiler.options.output.path
        if (!fs.existsSync(outputDir)) {
          // TODO ideally we'd let Webpack create it for us, but not sure how to
          // make this run later in the lifecycle
          fs.mkdirSync(outputDir)
        }
        const fullOutputPath = path.join(outputDir, this.filename)
        console.debug(
          `[DumpVueEnvVarsWebpackPlugin] dumping env vars to file=${fullOutputPath}`,
        )
        fs.writeFileSync(fullOutputPath, fileContent)
      }
    }
    
  2. use the plugin in your vue-cli config (vue.config.js or vue-config/config.default.js if your config is split over a few files)

    // import our plugin (change the path to where you saved the plugin script)
    const DumpVueEnvVarsWebpackPlugin = require('./DumpVueEnvVarsWebpackPlugin.js')
    
    module.exports = {
      // other stuff...
      configureWebpack: {
        plugins: [
          // We add our plugin here
          new DumpVueEnvVarsWebpackPlugin({ filename: 'my-env-vars.js' })
        ],
      },
    }
    
  3. in our service worker script, we can now import the file that we wrote with our Webpack plugin (it'll be there after the build has happened and service workers don't run in dev mode so we should be safe)

    importScripts('./my-env-vars.js') // written by DumpVueEnvVarsWebpackPlugin
    const fcmSenderId = VUE_APP_FCM_SENDER_ID // comes from script imported above
    console.debug(`Using sender ID = ${fcmSenderId}`)
    
    // use the variable
    firebase.initializeApp({
        'messagingSenderId': fcmSenderId
    })
    

It's not perfect, but it certainly gets the job done. It's D-R-Y as you only have to define all your env vars in one spot and the whole app uses the same values. Plus, it doesn't process any files that are in source control. I don't like that the plugin runs too early in the Webpack lifecycle so we have to create the dist dir but hopefully someone else smarter than me will have a fix for that.

like image 40
Tom Saleeba Avatar answered Oct 18 '22 09:10

Tom Saleeba