GithubHelp home page GithubHelp logo

fusionjs / fusion-plugin-service-worker Goto Github PK

View Code? Open in Web Editor NEW
6.0 7.0 8.0 865 KB

Migrated to https://github.com/fusionjs/fusionjs

License: MIT License

JavaScript 98.73% Dockerfile 0.28% Shell 0.99%
fusion fusionjs

fusion-plugin-service-worker's Introduction

fusion-plugin-service-worker

Build status

The Fusion plugin for Service Workers.

Out of the box provides default service worker for basic asset caching. Can be fully customized.


Installation

yarn add fusion-plugin-service-worker

Usage

Default Service Worker

To use the default service worker, your src/sw.js should probably look like this: (you can take advantage of the handlers module to reduce boilerplate)

import {getHandlers} from 'fusion-plugin-service-worker';
import type {AssetInfo} from 'fusion-plugin-service-worker';

export default (assetInfo) => {
  const {onFetch, onInstall, onActivate} = getHandlers(assetInfo);
  self.addEventListener("install", onInstall);
  self.addEventListener('activate', onActivate);
  self.addEventListener("fetch", onFetch);
}

Custom Service Worker

Customize the ServiceWorker by editing src/sw.js in your app. It shares the same transpile logic as regular fusion bundles.

Setup

// src/main.js
import App from 'fusion-react';

import {swTemplate as swTemplateFunction} from 'fusion-cli/sw';
import SwPlugin, {SWTemplateFunctionToken, SWRegisterToken} from 'fusion-plugin-service-worker';

app.register(SwPlugin);
if (__BROWSER__) {
  // optional (default true).
  // If false will unregister existing service workers and clear cache
  app.register(SWRegisterToken, true);
}
if (__NODE__) {
  app.register(SWTemplateFunctionToken, swTemplateFunction);
  // optional (default 24 hours)
  // The time (in ms) before the service worker cache will automatically expire
  app.register(SWcacheDuration, expiry);
}

The browser will automatically register the service worker on page load.

Options

The SWOptionsToken accepts an object with several optional configuration properties:

type Options = {
  cacheableRoutePatterns?: Array<RegExp>, // default null
  cacheBustingPatterns?: Array<RegExp>, // default null
  cacheDuration?: number, // default 24 hours
};

cacheableRoutePatterns Array<RegExp>

If this option is supplied the Service Worker will only cache HTML responses from requests whose URL matches at least one of these regular expressions. If the cacheableRoutePatterns is not supplied, all HTML content will be cached.

  app.register(SWOptionsToken, {
    cacheableRoutePatterns: [/\/airports$/],
  });

cacheBustingPatterns Array<RegExp>

If this option is supplied the Service Worker will empty its cache when it encounters a request (for HTML or other resource) which matches at least one of these regular expressions.

  app.register(SWOptionsToken, {
    cacheBustingPatterns: [/\?logout=true/, /\/display-error/],
  });

cacheDuration number

By deafult html caches expire after 24 hours. This expiry period can be overwrriten via this option. See also Cache Expiry.

  app.register(SWOptionsToken, {
    cacheDuration: 60*60*1000, // one hour
  });

Cache Expiry

Because Service Workers typically cache the HTML there is a possibility that an error or unexpected response will lead to apps being perpetually stuck behind a cache wall and cut off from the network. The Service Worker plugin includes several safeguards that significantly reduce this probability. These include deleting all caches when an HTML request returns a non-200 or non HTML response and backgraound-refreshing the cache from the network after every fetch.

As a last-resort protection, we assign a built-in expiry time to html caches. By default this is 24 hours, but you can override via the SWOptionsToken token (see cacheDuration option above). This is recommended when shipping a Service Worker for the first time, so as to prevent network isolation until the app owner is confident the Service Worker Plugin is working as expected.

Messaging

The Service Worker sends status updates to the browser client in the form of postMessages. These messages take the form:

{
  type: string,
  text: string
}

Your app can listen to these post messages and filter by type:

if ('serviceWorker' in window.navigator) {
  window.navigator.serviceWorker.addEventListener('message', event => {
    if (existingSW && event.data.type === 'upgrade-available') {
      // prompt user to reload for new build
      logger.log(event.data.text);
    }
  });
}

Message types include:
upgrade-available: A new build has occured and the user should reload the page to get the latest version. The service worker plugin will console.log this message by default

cache-expired: The Service Worker cache wan not been updated for a period exceeding the cache expiry period (see above) and so has been auto-refreshed.

Unregistering the Service Worker

If you need all users to unregister the Service Worker, you can register SWRegisterToken with the value false

if (__BROWSER__) {
  app.register(SWRegisterToken, false);
}

Service Worker Plugin Guide

For more information on how to use the Fusion Service Worker Plugin and an explanation of Service Workers in general, please see the Service Workers section in the Fusion.js Guide

fusion-plugin-service-worker's People

Contributors

alexmsmithca avatar angus-c avatar chrisdothtml avatar ganemone avatar kevingrandon avatar lhorie avatar nadiia avatar ratson avatar renovate[bot] avatar rtsao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

fusion-plugin-service-worker's Issues

Browser field not respected in sw bundle when es2015 and es2017 fields also used

Update webpack config so that module imports from src/sw.js point to the browser and not node.

We want to be populate src/sw.js in apps like this:

import {getHandlers} from "fusion-plugin-service-worker";

export default (assetInfo) => {
  const {onFetch, onInstall} = getHandlers(assetInfo);
  self.addEventListener("install", onInstall);
  self.addEventListener("fetch", onFetch);
}

Currently it has to be long hand like this

// @flow

/* global self, location, fetch, caches, URL */

const cacheName = '0.0.0'; // we don't expect this to change

export default assetInfo => {
  const {cacheablePaths, precachePaths} = assetInfo;
  self.addEventListener('install', event => {
    self.skipWaiting();
    event.waitUntil(
      // remove old cache
      caches
        .open(cacheName)
        .then(cache => {
          return cache
            .addAll(precachePaths)
            .then(() =>
              getOutdatedKeys(cache, cacheablePaths).then(outdatedKeys =>
                removeKeys(cache, outdatedKeys)
              )
            );
        })
        .catch(e => {
          throw new Error('sw: error updating cache' + cacheName + e);
        })
    );
  });

  self.addEventListener('fetch', event => {
    const HTML_TTL = 1 * 24 * 60 * 60 * 1001; // 1 day
    const expectsHtml = requestExpectsHtml(event.request);
    if (
      !expectsHtml &&
      !cacheablePaths.includes(new URL(event.request.url).pathname)
    ) {
      // bypass service worker, use network
      return;
    }
    event.waitUntil(
      event.respondWith(
        caches.match(event.request).then(cachedResponse => {
          if (cachedResponse) {
            if (expectsHtml) {
              const responseCreated = new Date(
                cachedResponse.headers.get('date')
              ).valueOf();
              if (Date.now() - responseCreated > HTML_TTL) {
                // html expired: use the cache, but refresh cache for next time
                self.fetchNCache(event.request, expectsHtml);
              }
            }
            return cachedResponse;
          }
          return fetchNCache(event.request, expectsHtml);
        })
      )
    );
  });

  function getOutdatedKeys(cache) {
    return cache.keys().then(requests =>
      requests.filter(request => {
        return !cacheablePaths.find(key => {
          return location.origin + key === request.url;
        });
      })
    );
  }

  function removeKeys(cache, keys) {
    return Promise.all(keys.map(key => cache.delete(key)));
  }

  function fetchNCache(request, expectsHtml) {
    return fetch(request).then(resp => {
      if (resp.status !== 200) {
        return Promise.resolve(resp);
      }
      const clonedResponse = resp.clone();
      caches.open(cacheName).then(cache => {
        if (expectsHtml) {
          // check we got html before caching
          if (!responseIsHtml(clonedResponse)) {
            return Promise.resolve(resp);
          }
        }
        cache.put(request.url, clonedResponse);
      });
      return Promise.resolve(resp);
    });
  }

  function requestExpectsHtml(request) {
    if (!request || !request.headers) {
      return false;
    }
    const acceptHeader = request.headers.get('Accept');
    return acceptHeader && acceptHeader.indexOf('html') > -1;
  }

  function responseIsHtml(response) {
    if (!response || !response.headers) {
      return false;
    }
    const contentType = response.headers.get('content-type');
    return contentType && contentType.indexOf('html') > -1;
  }
};

Update README to reflect webpack issue

Type of issue

Description

Current behavior

Expected behavior

Steps to reproduce

Your environment

  • fusion-plugin-service-worker version:

  • Node.js version (node --version):

  • npm version (npm --version):

  • Operating System:

A config to specify service-workers only on particular routes

Type of issue

Feature

Description

Servicer workers are the new hotness. However, introducing them to an entire web app can be too spicy. It would be great if to test the SW, we could apply it only on particular routes, instead of an entire app. This feature could be through a configuration when registering the service worker. Maybe, the configuration could accept a whitelist of certain routes that the webapp has.

Use template to generate sw.js

Type of issue

Description

Current behavior

Expected behavior

Steps to reproduce

Your environment

  • fusion-plugin-service-worker version:

  • Node.js version (node --version):

  • npm version (npm --version):

  • Operating System:

Assign precacehePaths and cacheablePaths to ctx

  • precachePaths is an array of core asset paths (usually main.js and vendor.js)
  • cacheablePaths is a concatenation of precachePaths and all other static assets (could just be JS bundles for phase 1)

Make sw.js no-cache

We want browser to default to downloading sw.js each time

Edit: looks life default has changed so SW is no cache by default. Will manually add ctx.set('Cache-Control', 'max-age=0') in case any non-compliant implementations still out there.

Dependency deprecation warning: @types/redux (npm)

On registry https://registry.yarnpkg.com/, the "latest" version (v3.6.0) of dependency @types/redux has the following deprecation notice:

This is a stub types definition for Redux (https://github.com/reactjs/redux). Redux provides its own type definitions, so you don't need @types/redux installed!

Marking the latest version of an npm package as deprecated results in the entire package being considered deprecated, so contact the package author you think this is a mistake.

Affected package file(s): package.json

If you don't care about this, you can close this issue and not be warned about @types/redux's deprecation again. If you would like to completely disable all future deprecation warnings then add the following to your config:

"suppressNotifications": ["deprecationWarningIssues"]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.