Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Laravel Mix and WorkBox?

I'm trying to build a PWA for my app; and have spent almost 48 hours trying to figure out how to make use of Workbox with Laravel Mix. What's ironical is that Google says Workbox is meant to make things easy!

Buh!

Okay, so far I've figured out that -

  1. I will need to use the InjectManifest Plugin because I want to integrate Push notifications service in my Service Worker

  2. I have no clue how to specifiy the paths for swSrc and swDest.

  3. What code should go into my webpack.mix.js and whether I should have a temporary service-worker inside my resources/js folder to create a new service worker inside public/ folder.

Can someone help?

PS: I've read almost every blog and help article; but none talks about reliably using Workbox with Laravel mix. Would really appreciate some help here.

like image 927
TheBigK Avatar asked Aug 06 '19 14:08

TheBigK


People also ask

What is laravel mix and how do you use it?

Laravel Mix is a tool for compiling and optimizing assets in a Laravel app. It's similar to a build tool like gulp, Grunt and such like. it's specific to Laravel but can also be used externally as an npm package. Laravel Mix covered 80% of Webpack's use case to make compiling assets easier.

Can I use laravel mix outside laravel?

Yes, you can use Mix outside of Laravel; it is an NPM package after all.

Does laravel mix use Babel?

Laravel Mix has a few methods for processing Javascript: mix. scripts() , mix. babel() and mix.

What is mix Laravel?

Laravel Mix, a package developed by Laracasts creator Jeffrey Way, provides a fluent API for defining webpack build steps for your Laravel application using several common CSS and JavaScript pre-processors. In other words, Mix makes it a cinch to compile and minify your application's CSS and JavaScript files. ...

Is it possible to use workbox with Laravel-Mix?

All in all, using workbox with laravel-mix has been an extremely painful process with all of the 'minor' tweaks that laravel-mix does breaking the workbox plugin. I'd recommend sticking to plain webpack if possible. Thanks for contributing an answer to Stack Overflow!

How to install mix in Laravel with NodeJS?

You can find this file at the root of the Laravel application. To install Mix in your Laravel application, you will need to have NodeJS installed on you machine. If you have already set up the NodeJs than you can go ahead and start with Mix setup.

How to manage CSS pre-processors in Laravel Mix?

Laravel Mix provides full support for most common CSS pre-processors like SASS, LESS, and Stylus. You can easily manage them in Mix. The less () method is used to compile all less files into a CSS file like below. The sass () method is used to compile sass files into a CSS file.


1 Answers

I have done a lot of research into this recently and whilst this may not be a full answer to your question, it should give you, or anyone else visiting this page, enough guidance to get started...

I will add to this answer as I learn and research more.

For the purposes of this answer, I will assume your service worker is called service-worker.js, however, you can obviously call it whatever you like.

Step 1 - Laravel Mix

Assuming you are using Dynamic Importing in your project (if you aren't, you should be), you will need to downgrade Laravel Mix to version 3. There is an acknowledged bug in Laravel Mix 4 that prevents CSS from bundling correctly and this will not be fixed until Webpack 5 is released.

In addition, the steps outlined in this answer are specifically configured for Laravel Mix 3.

Step 2 - Import or ImportScripts

The second issue to solve is whether to utilise the workbox-webpack-plugin for injecting the workbox global using importScripts or whether you should disable this (using importWorkboxFrom: 'disabled') and just individually import the specific modules you need...

The documentation states:

When using a JavaScript bundler, you don't need (and actually shouldn't use) the workbox global or the workbox-sw module, as you can import the individual package files directly.

This implies that we should be using import instead of injecting the importScripts.

However, there are various issues here:

  • We do not want service-worker.js to be included in the build manifest as this will be injected into the precache manifest
  • We do not want service-worker.js to be versioned in production i.e. the name should always be service-worker.js, not service-worker.123abc.js.
  • InjectManifest will fail to inject the manifest because the service-worker.js file will not exist at the time that it runs.

Therefore, in order to utilise import instead of importScripts, we must have two separate webpack (mix) configurations (see conclusion for guidance on how to do this). I am not 100% certain this is correct, but I will update my answer once I have received an answer to either of the following (please support them to increase chance of receiving an answer):

  • How to use Workbox with a Bundler (Webpack etc.)
  • https://github.com/GoogleChrome/workbox/issues/2207

Step 3 - File Structure

Assuming you are using InjectManifest and not GenerateSW, you will need to write your own service worker which will have the JS manifest injected into it by the webpack plugin on each build. This, quite simply, means you need to create a file in your source directory that will be used as the service worker.

Mine is located at src/js/service-worker.js (this will be different if you are building in a full Laravel project, I am simply using Laravel Mix in a standalone app)

Step 4 - Registering the Service Worker

There are various ways to do this; some like to inject inline JS into the HTML template, but others, myself included, simply register the service worker at the top of their app.js. Either way, the code should look something along the lines of:

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
        navigator.serviceWorker.register('/service-worker.js');
    });
}

Step 5 - Writing your Service Worker; workbox Global, or Module Importing

As mentioned in the previous quote from the documentation, it is encouraged to import the specifically required modules into your service worker, instead of utilising the workbox global or workbox-sw module.

For more information on how to use the individual modules, and how to actually write your service worker, see the following documentation:

https://developers.google.com/web/tools/workbox/guides/using-bundlers

Conclusion

Based on all of my research (which is still ongoing), I have taken the following approach outlined below.

Before reading, please bear in mind that this is configured for a standalone static PWA (i.e. not a full Laravel project).

/src/service-worker.js (the service worker)

When using a bundler such as webpack, it is advised to utlilise import to ensure you include only the necessary workbox modules. This is my service worker skeleton:

import config from '~/config'; // This is where I store project based configurations
import { setCacheNameDetails } from 'workbox-core';
import { precacheAndRoute } from 'workbox-precaching';
import { registerNavigationRoute } from 'workbox-routing';

// Set the cache details
setCacheNameDetails({
    prefix: config.app.name.replace(/\s+/g, '-').toLowerCase(),
    suffix: config.app.version,
    precache: 'precache',
    runtime: 'runtime',
    googleAnalytics: 'ga'
});

// Load the assets to be precached
precacheAndRoute(self.__precacheManifest);

// Ensure all requests are routed to index.html (SPA)
registerNavigationRoute('/index.html');

/package.json

Splitting the Mix configuration

"scripts": {  
  "development": "npm run dev-service-worker && npm run dev-core",  
  "dev": "npm run development",  
  "dev-service-worker": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=service-worker.mix",  
  "dev-core": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=core.mix",  
  "watch": "npm run dev-core -- --watch",  
  "watch-poll": "npm run watch -- --watch-poll",  
  "production": "npm run prod-service-worker && npm run prod-core",  
  "prod": "npm run production",  
  "prod-service-worker": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=service-worker.mix",  
  "prod-core": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --env.mixfile=core.mix"  
}

Command Explanation

  • All standard commands will work in the same way as usual (i.e. npm run dev etc.). See known issue about npm run watch
  • npm run <environment>-service-worker will build just the service worker in the specified environment
  • npm run <environment>-core will build just the core application in the specified environment

Known Issues

  • If you are using an html template that utilises the webpack manifest then you may have issues with npm run watch. I have been unable to get this to work correctly as of yet

Downgrading to Laravel Mix 3

"devDependencies": {  
    "laravel-mix": "^3.0.0"  
}

This can also be achieved by running npm install [email protected]

/static/index.ejs

This HTML template is used to generate the single page application index.html. This template is dependant on the webpack manifest being injected.

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" class="no-js">
    <head>

        <!-- General meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="description" content="<%= config.meta.description %>">
        <meta name="rating" content="General">
        <meta name="author" content="Sine Macula">
        <meta name="robots" content="index, follow">
        <meta name="format-detection" content="telephone=no">

        <!-- Preconnect and prefetch urls -->
        <link rel="preconnect" href="<%= config.api.url %>" crossorigin>
        <link rel="dns-prefetch" href="<%= config.api.url %>">

        <!-- Theme Colour -->
        <meta name="theme-color" content="<%= config.meta.theme %>">

        <!-- General link tags -->
        <link rel="canonical" href="<%= config.app.url %>">

        <!-- Manifest JSON -->
        <link rel="manifest" href="<%= StaticAsset('/manifest.json') %>" crossorigin>


        <!-- ----------------------------------------------------------------------
        ---- Icon Tags
        ---- ----------------------------------------------------------------------
        ----
        ---- The following will set up the favicons and the apple touch icons to be
        ---- used when adding the app to the homescreen of an iPhone, and to
        ---- display in the head of the browser.
        ----
        ---->
        <!--[if IE]>
            <link rel="shortcut icon" href="<%= StaticAsset('/favicon.ico') %>">
        <![endif]-->
        <link rel="apple-touch-icon" sizes="72x72" href="<%= StaticAsset('/apple-touch-icon-72x72.png') %>">
        <link rel="apple-touch-icon" sizes="120x120" href="<%= StaticAsset('/apple-touch-icon-120x120.png') %>">
        <link rel="apple-touch-icon" sizes="180x180" href="<%= StaticAsset('/apple-touch-icon-180x180.png') %>">
        <link rel="icon" type="image/png" sizes="16x16" href="<%= StaticAsset('/favicon-16x16.png') %>">
        <link rel="icon" type="image/png" sizes="32x32" href="<%= StaticAsset('/favicon-32x32.png') %>">
        <link rel="icon" type="image/png" sizes="192x192"  href="<%= StaticAsset('/android-chrome-192x192.png') %>">
        <link rel="icon" type="image/png" sizes="194x194"  href="<%= StaticAsset('/favicon-194x194.png') %>">
        <link rel="mask-icon" href="<%= StaticAsset('/safari-pinned-tab.svg') %>" color="<%= config.meta.theme %>">
        <meta name="msapplication-TileImage" content="<%= StaticAsset('/mstile-144x144.png') %>">
        <meta name="msapplication-TileColor" content="<%= config.meta.theme %>">


        <!-- ----------------------------------------------------------------------
        ---- Launch Images
        ---- ----------------------------------------------------------------------
        ----
        ---- Define the launch 'splash' screen images to be used on iOS.
        ----
        ---->
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-640x1136.png') %>" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-750x1294.png') %>" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-1242x2148.png') %>" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-1125x2436.png') %>" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-1536x2048.png') %>" media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)">
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-1668x2224.png') %>" media="(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)">
        <link rel="apple-touch-startup-image" href="<%= StaticAsset('/assets/images/misc/splash-2048x2732.png') %>" media="(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)">


        <!-- ----------------------------------------------------------------------
        ---- Application Tags
        ---- ----------------------------------------------------------------------
        ----
        ---- Define the application specific tags.
        ----
        ---->
        <meta name="application-name" content="<%= config.app.name %>">
        <meta name="apple-mobile-web-app-title" content="<%= config.app.name %>">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="<%= config.app.status_bar %>">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="full-screen" content="yes">
        <meta name="browsermode" content="application">


        <!-- ----------------------------------------------------------------------
        ---- Social Media and Open Graph Tags
        ---- ----------------------------------------------------------------------
        ----
        ---- The following will create objects for social media sites to read when
        ---- scraping the site.
        ----
        ---->

        <!-- Open Graph -->
        <meta property="og:site_name" content="<%= config.app.name %>">
        <meta property="og:url" content="<%= config.app.url %>">
        <meta property="og:type" content="website">
        <meta property="og:title" content="<%= config.meta.title %>">
        <meta property="og:description" content="<%= config.meta.description %>">
        <meta property="og:image" content="<%= StaticAsset('/assets/images/brand/social-1200x630.jpg') %>">

        <!-- Twitter -->
        <meta name="twitter:card" content="app">
        <meta name="twitter:site" content="<%= config.app.name %>">
        <meta name="twitter:title" content="<%= config.meta.title %>">
        <meta name="twitter:description" content="<%= config.meta.description %>">
        <meta name="twitter:image" content="<%= StaticAsset('/assets/images/brand/social-440x220.jpg') %>">


        <!-- ----------------------------------------------------------------------
        ---- JSON Linked Data
        ---- ----------------------------------------------------------------------
        ----
        ---- This will link the website to its associated social media page. This
        ---- adds to the credibility of the website as it allows search engines to
        ---- determine the following of the company via social media
        ----
        ---->
        <script type="application/ld+json">
            {
                "@context": "http://schema.org",
                "@type": "Organization",
                "name": "<%= config.company.name %>",
                "url": "<%= config.app.url %>",
                "sameAs": [<%= '"' + Object.values(config.company.social).map(x => x.url).join('","') + '"' %>]
            }
        </script>

        <!-- Define the page title -->
        <title><%= config.meta.title %></title>

        <!-- Generate the prefetch/preload links -->
        <% webpack.chunks.slice().reverse().forEach(chunk => { %>
            <% chunk.files.forEach(file => { %>
                <% if (file.match(/\.(js|css)$/)) { %>
                    <link rel="<%= chunk.initial ? 'preload' : 'prefetch' %>" href="<%= StaticAsset(file) %>" as="<%= file.match(/\.css$/) ? 'style' : 'script' %>">
                <% } %>
            <% }) %>
        <% }) %>

        <!-- Include the core styles -->
        <% webpack.chunks.forEach(chunk => { %>
            <% chunk.files.forEach(file => { %>
                <% if (file.match(/\.(css)$/) && chunk.initial) { %>
                    <link rel="stylesheet" href="<%= StaticAsset(file) %>">
                <% } %>
            <% }) %>
        <% }) %>

    </head>
    <body ontouchstart="">

        <!-- No javascript error -->
        <noscript>JavaScript turned off...</noscript>

        <!-- The Vue JS app element -->
        <div id="app"></div>

        <!-- Include the core scripts -->
        <% webpack.chunks.slice().reverse().forEach(chunk => { %>
            <% chunk.files.forEach(file => { %>
                <% if (file.match(/\.(js)$/) && chunk.initial) { %>
                    <script type="text/javascript" src="<%= StaticAsset(file) %>"></script>
                <% } %>
            <% }) %>
        <% }) %>

    </body>
</html>

/service-worker.mix.js (building the service worker)

This mix configuration will build your Service Worker (service-worker.js), and place it into the root of /dist.

Note: I like to clean my dist folder each time I build my project, and as this functionality must be run at this stage of the build process, I have included it in the below configuration.

const mix   = require('laravel-mix');
const path  = require('path');

// Set the public path
mix.setPublicPath('dist/');

// Define all the javascript files to be compiled
mix.js('src/js/service-worker.js', 'dist');

// Load any plugins required to compile the files
const Dotenv = require('dotenv-webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

// Define the required plugins for webpack
const plugins = [

    // Grant access to the environment variables
    new Dotenv,

    // Ensure the dist folder is cleaned for each build
    new CleanWebpackPlugin

];

// Extend the default Laravel Mix webpack configuration
mix.webpackConfig({
    plugins,
    resolve: {
        alias: {
            '~': path.resolve('')
        }
    }
});

// Disable mix-manifest.json (remove this for Laravel projects)
Mix.manifest.refresh = () => void 0;

/core.mix.js (building the application)

This mix configuration will build your main application and place it in /dist/js.

There are various key parts of this mix configuration, each of which has been clearly outlined in the comments within. These are the top-level areas:

  • Code splitting to app.js, manifest.js, and vendor.js (and dynamic importing)
  • Laravel Mix versioning does not work as needed for the HTML template so laravel-mix-versionhash is utilised instead
  • html-webpack-plugin is utilised to generate index.html based on the index.ejs template (see above)
  • webpack-pwa-manifest is utilised to generate a manifest based
  • copy-webpack-plugin is utilised to copy the static files to the /dist directory, and to copy any necessary icons to the site root
  • imagemin-webpack-plugin is used to compress any static images in production
  • workbox-webpack-plugin is used to inject the webpack manifest into the precaching array used in the service worker. InjectManifest is used, not GenerateSW
  • Any necessary manifest transformations are applied once the build process is complete

There may be additions to the above but pretty much everything is described by the comments in the following code:

const config    = require('./config'); // This is where I store project based configurations
const mix       = require('laravel-mix');
const path      = require('path');
const fs        = require('fs');

// Include any laravel mix plugins
// NOTE: not needed in Laravel projects
require('laravel-mix-versionhash');

// Set the public path
mix.setPublicPath('dist/');

// Define all the SASS files to be compiled
mix.sass('src/sass/app.scss', 'dist/css');

// Define all the javascript files to be compiled
mix.js('src/js/app.js', 'dist/js');

// Split the js into bundles
mix.extract([
    // Define the libraries to extract to `vendor`
    // e.g. 'vue'
]);

// Ensure the files are versioned when running in production
// NOTE: This is not needed in Laravel projects, you simply need
// run `mix.version`
if (mix.inProduction()) {
    mix.versionHash({
        length: 8
    });
}

// Set any necessary mix options
mix.options({

    // This doesn't do anything yet, but once the new version
    // of Laravel Mix is released, this 'should' extract the
    // styles from the Vue components and place them in a
    // css file, as opposed to placing them inline
    //extractVueStyles: true,

    // Ensure the urls are not processed
    processCssUrls: false,

    // Apply any postcss plugins
    postCss: [
        require('css-declaration-sorter'),
        require('autoprefixer')
    ]

});

// Disable mix-manifest.json
// NOTE: not needed in Laravel projects
Mix.manifest.refresh = () => void 0;

// Load any plugins required to compile the files
const Dotenv                    = require('dotenv-webpack');
const HtmlWebpackPlugin         = require('html-webpack-plugin');
const WebpackPwaManifest        = require('webpack-pwa-manifest');
const { InjectManifest }        = require('workbox-webpack-plugin');
const CopyWebpackPlugin         = require('copy-webpack-plugin');
const ImageminPlugin            = require('imagemin-webpack-plugin').default;

// Define the required plugins for webpack
const plugins = [

    // Grant access to the environment variables
    new Dotenv,

    // Process and build the html template
    // NOTE: not needed if using Laravel and blade
    new HtmlWebpackPlugin({
        template: path.resolve(__dirname, 'static', 'index.ejs'),
        inject: false,
        minify: !mix.inProduction() ? false : {
            collapseWhitespace: true,
            removeComments: true,
            removeRedundantAttributes: true,
            useShortDoctype: true
        },
        templateParameters: compilation => ({
            webpack: compilation.getStats().toJson(),
            config,
            StaticAsset: (file) => {
                // This will ensure there are no double slashes (bug in Laravel Mix)
                return (config.app.static_url + '/' + file).replace(/([^:]\/)\/+/g, "$1");
            }
        })
    }),

    // Generate the manifest file
    new WebpackPwaManifest({
        publicPath: '',
        filename: 'manifest.json',
        name: config.app.name,
        description: config.meta.description,
        theme_color: config.meta.theme,
        background_color: config.meta.theme,
        orientation: config.app.orientation,
        display: "fullscreen",
        start_url: '/',
        inject: false,
        fingerprints: false,
        related_applications: [
            {
                platform: 'play',
                url: config.app.stores.google.url,
                id: config.app.stores.google.id
            },
            {
                platform: 'itunes',
                url: config.app.stores.apple.url,
                id: config.app.stores.apple.id
            }
        ],
        // TODO: Update this once the application is live
        screenshots: [
            {
                src: config.app.static_url + '/assets/images/misc/screenshot-1-720x1280.png',
                sizes: '1280x720',
                type: 'image/png'
            }
        ],
        icons: [
            {
                src: path.resolve(__dirname, 'static/assets/images/icons/android-chrome-512x512.png'),
                sizes: [72, 96, 128, 144, 152, 192, 384, 512],
                destination: path.join('assets', 'images', 'icons')
            }
        ]
    }),

    // Copy any necessary directories/files
    new CopyWebpackPlugin([
        {
            from: path.resolve(__dirname, 'static'),
            to: path.resolve(__dirname, 'dist'),
            toType: 'dir',
            ignore: ['*.ejs']
        },
        {
            from: path.resolve(__dirname, 'static/assets/images/icons'),
            to: path.resolve(__dirname, 'dist'),
            toType: 'dir'
        }
    ]),

    // Ensure any images are optimised when copied
    new ImageminPlugin({
        disable: process.env.NODE_ENV !== 'production',
        test: /\.(jpe?g|png|gif|svg)$/i
    }),

    new InjectManifest({
        swSrc: path.resolve('dist/service-worker.js'),
        importWorkboxFrom: 'disabled',
        importsDirectory: 'js'
    })

];

// Extend the default Laravel Mix webpack configuration
mix.webpackConfig({
    plugins,
    output: {
        chunkFilename: 'js/[name].js',
    }
}).then(() => {

    // As the precached filename is hashed, we need to read the
    // directory in order to find the filename. Assuming there
    // are no other files called `precache-manifest`, we can assume
    // it is the first value in the filtered array. There is no
    // need to test if [0] has a value because if it doesn't
    // this needs to throw an error
    let filename = fs
        .readdirSync(path.normalize(`${__dirname}/dist/js`))
        .filter(filename => filename.startsWith('precache-manifest'))[0];

    // In order to load the precache manifest file, we need to define
    // self in the global as it is not available in node.
    global['self'] = {};
    require('./dist/js/' + filename);

    let manifest = self.__precacheManifest;

    // Loop through the precache manifest and apply any transformations
    manifest.map(entry => {

        // Remove any double slashes
        entry.url = entry.url.replace(/(\/)\/+/g, "$1");

        // If the filename is hashed then remove the revision
        if (entry.url.match(/\.[0-9a-f]{8}\./)) {
            delete entry.revision;
        }

        // Apply any other transformations or additions here...

    });

    // Filter out any entries that should not be in the manifest
    manifest = manifest.filter(entry => {

        return entry.url.match(/.*\.(css|js|html|json)$/)
            || entry.url.match(/^\/([^\/]+\.(png|ico|svg))$/)
            || entry.url.match(/\/images\/icons\/icon_([^\/]+\.(png))$/)
            || entry.url.match(/\/images\/misc\/splash-([^\/]+\.(png))$/);

    });

    // Concatenate the contents of the precache manifest and then save
    // the file
    const content = 'self.__precacheManifest = (self.__precacheManifest || []).concat(' + JSON.stringify(manifest) + ');';
    fs.writeFileSync('./dist/js/' + filename, content, 'utf8', () => {});

});

/src/js/app.js (the main application)

This is where you register your service worker, and obviously define your application etc...

/**
 * Register the service worker as soon as the page has finished loading.
 */
if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
        // TODO: Broadcast updates of the service worker here...
        navigator.serviceWorker.register('/service-worker.js');
    });
}

// Define the rest of your application here...
// e.g. window.Vue = require('vue');
like image 109
Ben Carey Avatar answered Nov 14 '22 17:11

Ben Carey