vuejs-templates / pwa

PWA template for vue-cli based on the webpack template
MIT License
3.04k stars 508 forks source link

Custom service worker #85

Open arnoudebben opened 7 years ago

arnoudebben commented 7 years ago

After diggin into this template I found out the SWPrecache plugin generates the service worker that's used in the build version. I have no clue how to edit this service worker when I want to edit the service worker and add push messaging. I was told maybe change de service-worker-prod.js in the build folder. But I assume files the build folder should generally not be changed? Please add a sw file where I can build my own service worker file instead of auto generating one.

ragingwind commented 7 years ago

You can update service worker source files generated by vue. It is a kind of basic uses

arnoudebben commented 7 years ago

So I should edit my service-worker.js file in the dist folder? Whenever I run the command 'npm run build' it will be overwritten and loose my custom added stuff right?

Radiergummi commented 6 years ago

@ragingwind the PWA template should definitely provide a way to add a user defined service worker, that's the whole point of PWAs. Maybe load the content of src/service-worker.js as a callback in the service worker created by SWPrecache?

Garito commented 6 years ago

To ADD your custom code to the service-worker just import it with

importScripts

from the sw-precache-webpack-plugin

Radiergummi commented 6 years ago

@Garito making that option work with the PWA template is a really frustrating path to go down. Contrary to what you'd expect, it has to be put into static/ instead of src/ because the vendor.js file is not available of course.
All of this is not obvious, so I'd favor a documented and integrated method at best.

ragingwind commented 6 years ago

This template shows how to manage a service worker via sw-precache-webpack-plugin. So, using and extending options in sw-precache-webpack-plugin and sw-precache is that make sense to me. Not only for service worker, also there are configurations in build, like a baseline of configurations which you can maintain or update anything what you want to update to fit your apps. For examples, if you want to copy your sw to specific place then you need to update CopyWebpackPlugin

I totally agree that document should provide more details but It's not easy. That is why we need your help.

Garito commented 6 years ago

There are two things I don't like about how it's done right now: 1.- I want the same service worker for every configuration I have (dev & prod for start but perhaps I would add test or some other) 2.- I would like to have all my important scripts in src and the rest outside it to allow me to copy only this folder and reconstruct my app (I'm thinking in the lack of a proper way to update libraries besides deleting the node_modules folder)

The documentation can be improved, I'm agree, but I manage to build a time tracker offline first without to much problems (besides my lack of knowledge of the thing that has become a monster but supercool to build)

tpraxl commented 6 years ago

Anyone figured out how to solve this?

My current experiments either produce my extension with a webpackJsonp that isn't understood in the context of the service worker or with a complete webpack bootstrap that expects window to be present, which also doesn't work in the context of a service-worker.

I tried importScripts: [{chunkName: 'sw'}], importScripts: [{filename: 'sw.js'}], … with different combinations of

It is darn frustrating. And there should really be a custom-service-worker.js in src with everything setup to just start coding.

If anyone explains to me the steps necessary to get this working, or if you can help me out with tips why my approaches keep failing, I promise to add a PR to this project that integrates it.

tpraxl commented 6 years ago

Here's a dirty workaround for anyone in need of writing an es6 custom service worker as an extension of the predefined service worker:

Add your es6 service worker as src/custom-service-worker.js.

I'll list the necessary changes to webpack.prod.conf.js, just update the relevant sections:

…
const babel = require('babel-core');
…
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      },
      {
        // copy custom service worker
        from: path.resolve(__dirname, '../src/custom-service-worker.js'),
        to: config.build.assetsRoot + '/[name].js',
        transform: (content, path) => {
          // and transpile it while copying
          return babel.transformFileSync(path).code;
        }
      }
    ]),
    // service worker caching
    new SWPrecacheWebpackPlugin({
      cacheId: 'survey',
      filename: 'service-worker.js',
      staticFileGlobs: ['dist/**/*.{js,html,css}'],
      minify: true,
      stripPrefix: 'dist/',
      importScripts: [
        {
          // transformed custom-service-worker (es6 -> js)
          filename: 'custom-service-worker.js'
        }
      ]
    })

Why did I do it that way?

You need Webpack to pick up on the service-worker somehow. If you add it as an entry, then all rules apply, which makes it unusable in the context of a service worker ( like window being expected as part of the bootstrap).

You cannot simply require it, because it would then be part of your other js stuff, which is not what you want.

You only want to make it available, so I made use of CopyWebpackPlugin. However, file loading rules wouldn't be triggered then, so I made use of the transform option. That's where the dirty part comes in, by calling babel.transformFileSync directly, which possibly misses any configurations for babel.

Radiergummi commented 6 years ago

@tpraxl that seems to work quite well, however I'd also like to pass this through WebPack to get access to node modules. Using babel-core only, it will just insert an import directive for the Promise polyfill...

tpraxl commented 6 years ago

@Radiergummi Yeah, that's the drawback, that's why I consider it a dirty workaround. I had problems passing it through webpack and sharing common libs (e.g. wrapper for indexed-db). I used the CopyWebpackPlugin to copy those libs and importScripts(…) in the service worker itself.

I'd love to see a solution for that use case.

luaz commented 6 years ago

There is a hacky solution for React (reference as potential solution) cra-append-sw: Utility tool to append custom code to ServiceWorker created by Create React App

Basically run another command after the build process to append the custom script: react-scripts build && cra-append-sw --mode dev ./custom-sw.js

amaralDaniel commented 6 years ago

Hey @tpraxl, I've implemented your solution but when I'm trying to use IndexedDB API (which is Promise-based), I get the following error

import _Promise from 'babel-runtime/core-js/promise';

Which means that babel-core isn't accessible from the service worker. I don't know if I misunderstood but you stated that it could be used with ES6 capabilities. Have I understood it wrongfully? Am I doing something wrong?

Radiergummi commented 6 years ago

@amaralDaniel there is no build pipeline in place for your service worker file, so the import directive, the node_modules folder (which contains babel-runtime) and other Webpack-based features are not available. You'd need to bootstrap these for yourself.

eeerrrttty commented 6 years ago

@tpraxl I LOVE YOU! <3