javascriptprogressive-web-appsviteworkboxweb-push

Vite-PWA-plugin how to add webpush (notifications)


I had the sw.js which receive webpush notifications. But recently I intalled vite-PWA-plugin and now i can't add notifications by default config.

How can i configure this vite.config.ts to add to generated serviceWorker.js webpush implementation?

vite.config.ts:

import {defineConfig} from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';

import path from 'path';
import {VitePWA} from "vite-plugin-pwa";

const manifest = {
    "theme_color"     : "#2B2B2B",
    "background_color": "#2B2B2B",
    "display"         : "standalone",
    "scope"           : "/",
    "start_url"       : "/farm",
    "name"            : "ColorBit",
    "short_name"      : "Mining",
    "description"     : "...",
    "icons"           : [
        {
            "src"  : "icons/icon-192x192.png",
            "sizes": "192x192",
            "type" : "image/png"
        },
        // ...
        {
            "src"    : "icons/maskable_icon.png",
            "sizes"  : "682x682",
            "type"   : "image/png",
            "purpose": "maskable"
        }
    ]
};

const getCache = ({ name, pattern, strategy = "CacheFirst" }: any) => ({
    urlPattern: pattern,
    handler: strategy,
    options: {
        cacheName: name,
        expiration: {
            maxEntries: 500,
            maxAgeSeconds: 60 * 60 * 24 * 60 // 2 months
        },
        cacheableResponse: {
            statuses: [0, 200]
        }
    }
});

export default defineConfig({
    plugins: [
        laravel({
            input  : [ 'resources/js/app.tsx',],
            refresh: true,
        }),
        react({
            fastRefresh: false
        }),
        VitePWA({
            registerType: 'autoUpdate',
            outDir      : path.resolve(__dirname, 'public'),
            manifest    : manifest,
            manifestFilename: 'manifest.webmanifest', // Change name for app manifest
            injectRegister  : false, // I register SW in app.ts, disable auto registration

            workbox         : {
                globDirectory: path.resolve(__dirname, 'public'), // Directory for caching
                globPatterns : [
                    '{build,images,sounds,icons}/**/*.{js,css,html,ico,png,jpg,mp4,svg}'
                ],
                navigateFallback: null, // Say that we don't need to cache index.html
                swDest       : 'public/serviceWorker.js',
                runtimeCaching: [
                    // Google fonts cache
                    getCache({
                        pattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
                        name: "google-fonts-cache",
                    }),
                    // Google fonts api cache
                    getCache({
                        pattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
                        name: "gstatic-fonts-cache"
                    }),
                    // Dynamic cache for assets in storage folder
                    getCache({
                        pattern: /.*storage.*/,
                        name: "dynamic-images-cache",
                    }),

                ]
            }
        })
    ],
    resolve: {
        alias     : {
            '@'          : path.resolve(__dirname, 'resources/js'),
            '@hooks'     : path.resolve(__dirname, 'resources/js/hooks'),
            '@assets'    : path.resolve(__dirname, 'resources/js/assets/'),
            '@components': path.resolve(__dirname, 'resources/js/components')
        },
        extensions: ['.js', '.ts', '.tsx', '.jsx'],
    },
});

Old webpush implementation in sw.js:

// ^^^ Activate, Install, Fetch... ^^^

/* Webpush Notifications */

// Receive push notifications
self.addEventListener('push', function (e) {
    if (!(
        self.Notification &&
        self.Notification.permission === 'granted'
    )) {
        //notifications aren't supported or permission not granted!
        return;
    }

    if (e.data) {
        let message = e.data.json();
        e.waitUntil(self.registration.showNotification(message.title, {
            body: message.body,
            icon: message.icon,
            actions: message.actions
        }));
    }
});

// Click and open notification
self.addEventListener('notificationclick', function(event) {
    event.notification.close();

    if (event.action === 'farm') clients.openWindow("/farm");
    else if (event.action === 'home') clients.openWindow("/");
    else if (event.action === 'training') clients.openWindow("/mining-training");
    else if (event.action === 'dns') clients.openWindow("/shops/dns");
    else if (event.action === 'ali') clients.openWindow("/shops/aliexpress");
    else clients.openWindow("/farm");
}, false);


Solution

  • Should use the inject manifest parametr and write custom serviceWorker by workbox prepared methods (workbox documentation is very bad, i think so. You can use some methods from my config)

    vite.config.ts:

    export default defineConfig({
      plugins: [
        laravel({
          input: ['resources/js/app.tsx', ],
          refresh: true,
        }),
        react({
          fastRefresh: false
        }),
        VitePWA({
          registerType: 'autoUpdate',
          outDir: path.resolve(__dirname, 'public'),
          manifest: manifest,
          manifestFilename: 'manifest.webmanifest', // Change name for app manifest
          injectRegister: false, // I register SW in app.ts, disable auto registration
    
          // HERE! For custom service worker
          srcDir: path.resolve(__dirname, 'resources/js/'),
          filename: 'serviceWorker.js',
          strategies: 'injectManifest',
    
          workbox: {
            globDirectory: path.resolve(__dirname, 'public'),
            globPatterns: [
              '{build,images,sounds,icons}/**/*.{js,css,html,ico,png,jpg,mp4,svg}'
            ],
          },
        })
      ],
      resolve: {
        alias: {
          '@': path.resolve(__dirname, 'resources/js'),
          '@hooks': path.resolve(__dirname, 'resources/js/hooks'),
          '@assets': path.resolve(__dirname, 'resources/js/assets/'),
          '@components': path.resolve(__dirname, 'resources/js/components')
        },
        extensions: ['.js', '.ts', '.tsx', '.jsx'],
      },
    
      // define: {
      //     // By default, Vite doesn't include shims for NodeJS/
      //     // necessary for React-joyride. And probably for another libs
      //     global: {},
      // },
    });

    /resouces/js/serviceWorker.js:

    import {ExpirationPlugin} from 'workbox-expiration';
    import {createHandlerBoundToURL, precacheAndRoute, cleanupOutdatedCaches} from 'workbox-precaching';
    import {registerRoute} from 'workbox-routing';
    import {CacheFirst} from 'workbox-strategies';
    import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
    
    // Register precache routes (static cache)
    precacheAndRoute(self.__WB_MANIFEST || []);
    
    // Clean up old cache
    cleanupOutdatedCaches();
    
    // Google fonts dynamic cache
    registerRoute(
        /^https:\/\/fonts\.googleapis\.com\/.*/i,
        new CacheFirst({
            cacheName: "google-fonts-cache",
            plugins: [
                new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
                new CacheableResponsePlugin({statuses: [0, 200]})
            ]
        }), "GET");
    
    // Google fonts dynamic cache
    registerRoute(
        /^https:\/\/fonts\.gstatic\.com\/.*/i, new CacheFirst({
            cacheName: "gstatic-fonts-cache",
            plugins: [
                new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
                new CacheableResponsePlugin({statuses: [0, 200]})
            ]
        }), "GET");
    
    // Dynamic cache for images from `/storage/`
    registerRoute(
        /.*storage.*/, new CacheFirst({
            cacheName: "dynamic-images-cache",
            plugins: [
                new ExpirationPlugin({maxEntries: 500, maxAgeSeconds: 5184e3}),
                new CacheableResponsePlugin({statuses: [0, 200]})
            ]
        }), "GET");
    
    // Install and activate service worker
    self.addEventListener('install', () => self.skipWaiting());
    self.addEventListener('activate', () => self.clients.claim());
    
    // Receive push notifications
    self.addEventListener('push', function (e) {
        if (!(
            self.Notification &&
            self.Notification.permission === 'granted'
        )) {
            //notifications aren't supported or permission not granted!
            console.log('nononono')
            return;
        }
    
        if (e.data) {
            let message = e.data.json();
            e.waitUntil(self.registration.showNotification(message.title, {
                body: message.body,
                icon: message.icon,
                actions: message.actions
            }));
        }
    });
    
    // Click and open notification
    self.addEventListener('notificationclick', function(event) {
        event.notification.close();
    
        if (event.action === 'farm') clients.openWindow("/farm");
        else if (event.action === 'home') clients.openWindow("/");
        else if (event.action === 'training') clients.openWindow("/mining-training");
        else if (event.action === 'dns') clients.openWindow("/shops/dns");
        else if (event.action === 'ali') clients.openWindow("/shops/aliexpress");
        else if (event.action === 'avito') clients.openWindow("/avito");
        else if (event.action === 'friends') clients.openWindow("/friends");
        else if (event.action === 'locations') clients.openWindow("/locations");
        else if (event.action === 'vk-chat') clients.openWindow("https://vk.me/join/au1/k0nOTjLasxMO6wX50QuyPfYosyWdPEI=");
        else clients.openWindow(event.action); // Open link from action
    }, false);

    vite-pwa-plugin has only some info about opportunity to create webpush - documentation

    I found some code for service-worker in this repo and copy some code from old default generated by vite.config.ts config