What is the correct way to use vite-plugin-pwa in a laravel project?
Asked Answered
M

1

6

I have a laravel (inertia/vue) app (that used laravel-mix until recently) with a pwa feature that I built up using pwa builder and workbox.

Following the change from laravel-mix to vite in the laravel framework, I migrated to vite and its features work as expected, the js and css are injected into the head element of the entry page of the app, and vue pages work as expected, nothing is compiled and injected into the public folder in development, as was the case with laravel-mix/webpack.

I'm now having trouble getting the pwa feature to work as it did before. Since I'm no longer using webpack/laravel-mix, I replaced the webpack-workbox plugin I was using with this plugin: vite-plugin-pwa.

When I was using laravel-mix and webpack-workbox plugin, I simply point to my source service worker file in the webpack config file and use my desired workbox strategy to build up my final service worker file which then gets compiled and placed in my public folder along with other compiled css and js files.

With this vite plugin, my final service worker does not get compiled and placed in the public directory as such no service worker is discoverable for the app. I get a 404 error with the message: 'Failed to register a service worker for scope...', because no service worker file is available.

I suspect that I'm not configuring or using this plugin the correct way and would appreciate help with how to do this or otherwise resolve this.

This is my vite.config.js file:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.js',
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
                compilerOptions: {
                    isCustomElement: tag => tag.startsWith('pwa-')
                    || tag.startsWith('font-')
                },
            },
        }),
        VitePWA({
            strategies: 'injectManifest',
            swSrc: './public/sw.js',
            swDest: './public/pwabuilder-sw.js',
            devOptions: {
                enabled: true,
                type: 'module'
            }
        }),
    ],
});

This is my source service worker file in my public folder:

// This is the "Offline copy of assets" service worker
import {BackgroundSyncPlugin} from 'workbox-background-sync'
import {registerRoute} from 'workbox-routing'
import {StaleWhileRevalidate} from 'workbox-strategies'
import {ExpirationPlugin} from 'workbox-expiration'

const CACHE = "pwabuilder-offline"
const QUEUE_NAME = "bgSyncQueue"

self.__WB_DISABLE_DEV_LOGS = true

self.addEventListener("message", (event) => {
  if (event.data && event.data.type === "SKIP_WAITING") {
    self.skipWaiting()
  }
})

const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
  maxRetentionTime: 24 * 60 // Retry for max of 24 Hours (specified in minutes)
})

const expPlugin = new ExpirationPlugin({
  maxEntries: 5,
  maxAgeSeconds: 1 * 24 * 60 * 60,
  purgeOnQuotaError: true,
  matchOptions: {
    ignoreVary: true,
  }
})

registerRoute(
  new RegExp('/*'),
  new StaleWhileRevalidate({
    cacheName: CACHE,
    plugins: [
      bgSyncPlugin,
      expPlugin
    ]
  })
)

self.addEventListener('push', function (e) {
  if (!(self.Notification && self.Notification.permission === 'granted')) {
      return;
  }

  if (e.data) {
    const msg = e.data.json()
    clients.matchAll().then(function(c) {
        e.waitUntil(self.registration.showNotification('SmartWealth Push Notification', {
          body: msg.notification.body,
          icon: msg.notification.icon,
          actions: msg.notification.actions,
          deep_link: msg.notification.deep_link
        }))

    })
  }
})

UPDATE This is my current SW using workbox CDN:

// This is the "Offline copy of assets" service worker
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.5.4/workbox-sw.js')

const {BackgroundSyncPlugin} =  workbox.backgroundSync
const {registerRoute} =  workbox.routing
const {StaleWhileRevalidate} =  workbox.strategies
const {ExpirationPlugin} =  workbox.expiration

const CACHE = "offlineAssets"
const QUEUE_NAME = "bgSyncQueue"

self.__WB_DISABLE_DEV_LOGS = true

self.addEventListener('install', () => self.skipWaiting())

self.addEventListener('activate', () => self.clients.claim())

const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
  maxRetentionTime: 24 * 60 // Retry for max of 24 Hours (specified in minutes)
})

const expPlugin = new ExpirationPlugin({
  maxEntries: 5,
  maxAgeSeconds: 1 * 24 * 60 * 60,
  purgeOnQuotaError: true,
  matchOptions: {
    ignoreVary: true,
  }
})

registerRoute(
  new RegExp('/*'),
  new StaleWhileRevalidate({
    cacheName: CACHE,
    plugins: [
      bgSyncPlugin,
      expPlugin
    ]
  })
)

self.addEventListener('push', (e) => {
  if (!(self.Notification && self.Notification.permission === 'granted')) return
  if (!e.data) return
   
  const msg = e.data.json()

  e.waitUntil(clients.matchAll()
    .then((clients) => {
    // console.log(msg, clients)
    clients.forEach((client) => {
      const url = client.url
      const id = url.slice(url.lastIndexOf('/') + 1)
      if (!client.url.includes(`chat/messages/${id}`)) { //send only if not on chat url for particular sender.
        self.registration.showNotification('SmartWealth Push Notification', {
          body: msg.notification.body,
          icon: msg.notification.icon,
          actions: msg.notification.actions,
          deep_link: msg.notification.deep_link
        })
      }
    })
  }))
})

self.addEventListener('notificationclick', () => {}) //to do

I no longer require vite to inject my SW.

Monolayer answered 17/8, 2022 at 13:28 Comment(10)
Nice spot @andres_gcarmona but no difference.Monolayer
Sorry I deleted my previous comment because it wasn't very readable. What I was trying to say is this: I think you have a typo in file extension here: swSrc: './public/sw.sj'Stonefish
I am in the same situation, are you using sail?Stonefish
Hi @andres_gcarmona, I saw the typo and corrected it but it didn't resolve the issue for me. I'm not using sail...Monolayer
Hello @Monolayer did you find a solution for this?Urita
Hi @UğurArıcı, I haven't found a solution for using this plugin. I currently use a different workbox approach that doesn't require bundling.Monolayer
HI @Monolayer what is the approach you are using, same issue here!Galenism
@JamieBurton, I use workbox CDN rather than pulling the modules into my app.Monolayer
@Monolayer can you share you solution?Migrant
@Froxz, please see my update in response to your request.Monolayer
W
3

The correct way to use vite-plugin-pwa in a Laravel project is made complex by a number of issues, such as:

  • Laravel has its own public dir
  • So vite then builds to public/build/assets which is different to the usual frontend layout
  • there is not an immediately apparent static HTML entrypoint for the PWA
  • Laravel will put other things (like Telescope) in the public dir that you do not want offline

To make it work, you need to configure vite-plugin-pwa to work around these issues:

  • configure buildBase and outDir in vite.config.ts for your preferred directory structure
  • create a Blade file to act as an HTML entrypoint and add config for this to vite.config.ts
  • Add a Service-Worker-Allowed header to your web server to work around the restrictions imposed by the build directory
  • Configuring caching in vite.config.ts to work around other assets in the public dir

There's a detailed GitHub issue exploring this problem here:

https://github.com/vite-pwa/vite-plugin-pwa/issues/431

I worked though this thread to get my own Laravel, Vite, Vue3 and TypeScript app working as a PWA with offline support and app install prompts. The original issue was asking from a demonstration repository, so I set that up based on my experience in case it can help others. You can find it here:

https://github.com/sfreytag/laravel-vite-pwa

Wellbred answered 13/12, 2023 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.