vite-pwa / vite-plugin-pwa

Zero-config PWA for Vite
https://vite-pwa-org.netlify.app/
MIT License
3.13k stars 205 forks source link

Possiblity to make it work on fresh laravel build or a working sample? #431

Open msonowal opened 1 year ago

msonowal commented 1 year ago

I use the fresh default laravel vite config which can be re-produced by creating a new project with laravel

and install jetstream with inertia stack.

Now try to configure the service worker and plugin, As per my tries and findings it generates the paths incorrectly which refers to the build folder.

This is my current vite config

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',
        'resources/js/user/user.js'
      ],
    }),
    vue({
      template: {
        transformAssetUrls: {
          base: null,
          includeAbsolute: false
        }
      }
    }),
    VitePWA({
      srcDir: 'resources/js/service-worker',
      outDir: 'public',
      filename: 'sw.ts',
      strategies: 'injectManifest',
      injectRegister: 'inline',
      registerType: 'autoUpdate',
      workbox: {
        globPatterns: ['**/*.{ico,png,svg,ttf}']
      },
      devOptions: {
        enabled: true
      }
    })
  ],
  resolve: {
    alias: { './runtimeConfig': './runtimeConfig.browser' } 
},
})
bursteri commented 1 year ago

I have same issue trying to convert a Vite + Vue + Laravel project to PWA

-Following

avesgit commented 1 year ago

same problem what to do?

userquin commented 1 year ago

@msonowal you can use globDirectory on injectManifest to change the build folder: you also need to move globPatterns from workbox to injectManifest, check workbox-build documentation

It seems you are using TypeScript in your custom service worker, add type: 'module' to your pwa devOptions.

userquin commented 1 year ago

Here the reference: https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-RequiredGlobDirectoryPartial

You'll need to also add outDir on pwa options.

anburocky3 commented 1 year ago

@userquin How about an fork with laravel + inertiajs integration with Vite-PWA support?

fredpedersen commented 1 year ago

Has anyone got this working? For me, I'm not getting anything injected into the html - no service workers, no manifest...

VitePWA({
            registerType: 'autoUpdate',
            injectRegister: 'auto',
            devOptions: {
                enabled: true,
                type: 'module',
            },
            manifest: {
                name: 'My Awesome App',
                short_name: 'MyApp',
                description: 'My Awesome App description',
                theme_color: '#ffffff',
                icons: [
                    {
                        src: 'pwa-192x192.png',
                        sizes: '192x192',
                        type: 'image/png'
                    },
                    {
                        src: 'pwa-512x512.png',
                        sizes: '512x512',
                        type: 'image/png'
                    }
                ]
            }
userquin commented 1 year ago

I'm sorry folks, I dont use laravel. can you provide folder structure?

fredpedersen commented 1 year ago

Laravel projects are based on this and use the same folder structure https://github.com/laravel/laravel

Static files including js, vue files ect. are all put in the resources directory and then generated into the public/build/ folder by vite.

userquin commented 1 year ago

You can try to add this to pwa options:

plugins: [
  VitePWA({
    outDir: 'public/build',
  })
],
fredpedersen commented 1 year ago

You're kidding! Thank you, that's saved a lot of time.

For anyone else wondering, even though some files get generated in public/build you also need registerSW.js and workbox to be generated there - adding outDir: 'public/build', does that.

It's still not auto injecting the script and manifest for me, but I've gotten around that by manually including them in the blade layout:

<link rel="manifest" href="/build/manifest.webmanifest">
 <script src="/build/registerSW.js"></script>
userquin commented 1 year ago

@fredpedersen provide these pwa options:

plugins: [
  VitePWA({
    outDir: 'public/build',
    scope: '/',
    base: '/',
    /* other options*/
    manifest: {
      id: '/',
      scope: '/',
      /* other options*/
    }
  })
],
userquin commented 1 year ago

@fredpedersen so the root/base for the app is /build? can you change the build/dist folder in laravel?

anburocky3 commented 1 year ago

@userquin the root of the app is /public

If the directory is like SERVER://public_html/public the starting point of app will be in /public only. So all favicons, will be located there.

userquin commented 1 year ago

@anburocky3 @fredpedersen can you try using this?

plugins: [
  VitePWA({
    outDir: 'public',
  })
],

EDIT: this way you don't need to switch to manual injection, and you will be able to use VanillaJS virtual module for auto update and auto page reload.

anburocky3 commented 1 year ago

You're kidding! Thank you, that's saved a lot of time.

For anyone else wondering, even though some files get generated in public/build you also need registerSW.js and workbox to be generated there - adding outDir: 'public/build', does that.

It's still not auto injecting the script and manifest for me, but I've gotten around that by manually including them in the blade layout:

<link rel="manifest" href="/build/manifest.webmanifest">
 <script src="/build/registerSW.js"></script>

Is there any way that, we can inject the script imports directly to our blade layout?

anburocky3 commented 1 year ago

@anburocky3 @fredpedersen can you try using this?

plugins: [
  VitePWA({
    outDir: 'public',
  })
],

I think, it is generating correctly, but it doesn't get inserted into app.blade.php file. We have some directives like @vite() which will import the js for us.

It will be cool, if it auto-imports the necessary scripts in our app.

FYI, this is the layout file: app.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title inertia>{{ config('app.name', 'Laravel') }}</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <!-- Scripts -->
    @routes
    @vite('resources/js/app.ts')
    @inertiaHead
</head>
<body class="antialiased">
    @inertia
</body>
</html>
userquin commented 1 year ago

Check my previous comment, I add EDIT entry: then in your layour you can just use in resources/js/app.ts:

import { registerSW } from 'virtual:pwa-register'
/*other code*/

registerSW({ immediate: true })
userquin commented 1 year ago

For the web manifest you can use this another virtual virtual:pwa-info:

import { pwaInfo } from 'virtual:pwa-info'

if (pwaInfo) {
  const webManifest = pwaInfo.webManifest.linkTag
  console.log(pwaInfo)
  /* add link to head: webManifest  is the link */
}
userquin commented 1 year ago

for example, we use it in SvelteKit layout: https://github.com/vite-pwa/sveltekit/blob/main/examples/sveltekit-ts/src/routes/%2Blayout.svelte

anburocky3 commented 1 year ago

Based on suggestion, tried it. Here is the testing URL: https://jdskitchen.menuworkflow.com/

My Config:

VitePWA({
            registerType: 'autoUpdate',
            devOptions: {
                enabled: true
            },
            // outDir: 'public/build',
            outDir: 'public',
            scope: '/',
            base: '/',
            includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'],
            manifest: {
                id: '/',
                scope: '/',
                name: 'MenuWorkflow',
                short_name: 'MenuWorkflow',
                description: 'Make order workflow ease.',
                theme_color: '#ffffff',
                icons: [
                    {
                        "src": "/img/icons/icon-72x72.png",
                        "sizes": "72x72",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-96x96.png",
                        "sizes": "96x96",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-128x128.png",
                        "sizes": "128x128",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-144x144.png",
                        "sizes": "144x144",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-152x152.png",
                        "sizes": "152x152",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-192x192.png",
                        "sizes": "192x192",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-384x384.png",
                        "sizes": "384x384",
                        "type": "image/png",
                        "purpose": "maskable any"
                    },
                    {
                        "src": "/img/icons/icon-512x512.png",
                        "sizes": "512x512",
                        "type": "image/png",
                        "purpose": "maskable any"
                    }
                ]
            }
        })

Issues found:

  1. Manifest not found.
  2. Can find the service workers here like this. image

I mainly use PWA, to add this app in all native platforms. Is there any way, we can show This app is installable, is there any view inbuild into this package?

userquin commented 1 year ago

@anburocky3 where is index.html? if you are using / instead, just add workbox.navigateFallback = '/' to pwa options

fredpedersen commented 1 year ago

From some testing: import { pwaInfo } from 'virtual:pwa-info' causes the service worker to get stuck on 'trying to install' and it never gets past this.

 id: '/',
scope: '/',

also breaks it, saying trying to install regardless of manual or automatic injection

I now have a running and active service worker with manual injection and with:

outDir: 'public/build',
registerSW: true,
devOptions: {
    enabled: true,
},

But it's throwing the error Uncaught (in promise) non-precached-url: non-precached-url :: [{"url":"index.html"}]

EDIT: Scratch that it's working with workbox.navigateFallback = '/' as above

anburocky3 commented 1 year ago

@anburocky3 where is index.html? if you are using / instead, just add workbox.navigateFallback = '/' to pwa options

I'm using Laravel app, as discussed above, the entry point of the app is in /public

From some testing: import { pwaInfo } from 'virtual:pwa-info' causes the service worker to get stuck on 'trying to install' and it never gets past this.

 id: '/',
scope: '/',

also breaks it, saying trying to install regardless of manual or automatic injection

I now have a running and active service worker with manual injection and with:

outDir: 'public/build',
registerSW: true,
devOptions: {
    enabled: true,
},

But it's throwing the error Uncaught (in promise) non-precached-url: non-precached-url :: [{"url":"index.html"}]

EDIT: Scratch that it's working with workbox.navigateFallback = '/' as above

Can you share the entire config files, so it will be easy to understand.

userquin commented 1 year ago

@anburocky3 add it also on devOptions

userquin commented 1 year ago

@anburocky3 check the build folder and remove public/build/sw.js and public/build/registerSW.js

userquin commented 1 year ago

virtual:pwa-info dts: https://github.com/vite-pwa/vite-plugin-pwa/blob/main/info.d.ts

userquin commented 1 year ago

about virtual:pwa-info you can check if present, if so, you can create the link manually, it was created to use it in meta framework integrations (SvelteKit and Astro)

https://github.com/vite-pwa/astro/blob/main/examples/pwa-prompt/src/layouts/DefaultLayout.astro#L31

fredpedersen commented 1 year ago

Here's the full config at the moment:

VitePWA({
            outDir: 'public',
            scope: '/',
            id: '/',
            registerSW: true,
            devOptions: {
                enabled: true,
            },
            workbox: {
                navigateFallback: '/',
            },
            manifest: {
                name: "Codex",
                short_name: "Codex",
                theme_color: '#ffffff',
                start_url: '/',
                scope: '/',
                id: '/',
                icons: [
                    {
                        src: '/img/icons/android-chrome-192x192.png',
                        sizes: '192x192',
                        type: 'image/png',
                    },
                    {
                        src: '/img/icons/android-chrome-512x512.png',
                        sizes: '512x512',
                        type: 'image/png',
                    },
                    {
                        src: '/img/icons/android-chrome-512x512.png',
                        sizes: '512x512',
                        type: 'image/png',
                        purpose: 'any maskable'
                    }
                ],
            },
        })

The current issue with that config is that it seems to be generating sw.js and workbox at /public but registerSW.js and manifest at public/build. The registerSW.js file references /public/build/sw.js which doesn't exist

userquin commented 1 year ago

so Vite build is changed to public/build folder, revert outDir to public/build and remove sw.js, registerSW.js and workbox-*.js from public folder

anburocky3 commented 1 year ago

@fredpedersen type error on this. How did you get rid of it? image

With respect, @userquin if we register the registerSW here, we don't need to have registerSW({immediate: true}) in resources/js/app.ts right?

userquin commented 1 year ago

it is injectRegister: true, and yes you can remove virtual:pwa-register, but you're app will no auto reload page on auto update

fredpedersen commented 1 year ago

so Vite build is changed to public/build folder, revert outDir to public/build and remove sw.js, registerSW.js and workbox-*.js from public folder

This semi works, however the root folder of laravel is /public not public/build which means all the files are at http://example.com/build/manifest.json and therefore the scope is limited to /build too. Ideally we could get everything generated in the public dir

fredpedersen commented 1 year ago

@anburocky3 sorry haven't seen that yet

userquin commented 1 year ago

that's a problem of mixing source and build folders in Vite, I'll check what we can do here to solve the problem: as I mention in some previous comment, the sw is inside build folder and the scope is /.

Maybe you can collect some info, I'll check later required changes to solve the problem:

fredpedersen commented 1 year ago

I'm pretty new to vite so I'm not sure how to do that. It looks like it's laravel-vite-plugin that's moving everything to the /public/build folder however.

From the laravel vite plugin code:

/**
     * The public subdirectory where compiled assets should be written.
     *
     * @default 'build'
     */
    buildDirectory?: string;

It throws an error if it's manually set to '' or '/'.

anburocky3 commented 1 year ago

Here's the full config at the moment:

VitePWA({
            outDir: 'public',
            scope: '/',
            id: '/',
            registerSW: true,
            devOptions: {
                enabled: true,
            },
            workbox: {
                navigateFallback: '/',
            },
            manifest: {
                name: "Codex",
                short_name: "Codex",
                theme_color: '#ffffff',
                start_url: '/',
                scope: '/',
                id: '/',
                icons: [
                    {
                        src: '/img/icons/android-chrome-192x192.png',
                        sizes: '192x192',
                        type: 'image/png',
                    },
                    {
                        src: '/img/icons/android-chrome-512x512.png',
                        sizes: '512x512',
                        type: 'image/png',
                    },
                    {
                        src: '/img/icons/android-chrome-512x512.png',
                        sizes: '512x512',
                        type: 'image/png',
                        purpose: 'any maskable'
                    }
                ],
            },
        })

The current issue with that config is that it seems to be generating sw.js and workbox at /public but registerSW.js and manifest at public/build. The registerSW.js file references /public/build/sw.js which doesn't exist

@fredpedersen Are you importing these manually or using script to automate this process?

   <link rel="manifest" href="/build/manifest.webmanifest">
    <script src="/build/registerSW.js"></script>
userquin commented 1 year ago

just try .

try setting outDir: '..' in pwa options and injectRegister: false, then add again virtual:register-pwa

userquin commented 1 year ago

@fredpedersen laravel will serve sw.js and workbox-*.js from public folder, no?

userquin commented 1 year ago

@anburocky3 is working for you? I mean, with your configuration

fredpedersen commented 1 year ago

outDir appears to only effect the placement of sw.js and workbox-*.js. '..' just puts it in the parent directory of the main laravel app. These are in the right place with outDir:'public'. The issue is the location of registerSW and manifest.

Yep, laravel with serve from the public folder as /

anburocky3 commented 1 year ago

@userquin I have couple of issues:

  1. Can't have injectRegister: true is not available and i have to use auto or script.
  2. registerSW can't be found. image
  3. I need to autoimport the manifest.webmanifest file. Been struck there. @fredpedersen are you importing manually or automatically?
  4. @userquin Can i use DOM to insert the script tag inside the head?
    
    import { pwaInfo } from 'virtual:pwa-info'

if (pwaInfo) { const webManifest = pwaInfo.webManifest.linkTag console.log(pwaInfo) / add link to head: webManifest is the link / }

anburocky3 commented 1 year ago

m/vite-pwa/vite-plugin-pwa/blob/main/info.d.ts How to reference the types? Do we have inbuilt or should we create it and import it ourselves? image

Update: Fixed with adding types "vite-plugin-pwa/info" to tsconfig.json

userquin commented 1 year ago

I have found the problem: the web manifest is not being generate, we use generateBundle hook and so Vite will copy to the build folder (it is the default in vite-plugin-laravel), I'll try to check if we can fix this, maybe adding a new option or just detecting laravel plugin and changing the path.

Since laravel is mixing source and build folders vite base is not the same as laravel base: this plugin assuming root build dir is the same as base/scope path, and in that case this is not true.

userquin commented 1 year ago

m/vite-pwa/vite-plugin-pwa/blob/main/info.d.ts How to reference the types? Do we have inbuilt or should we create it and import it ourselves?

Add this line at top: /// <reference types="vite-plugin-pwa/info" />

and/or to your tsconfig.json file in types:

"compilerOptions": {
   /*other options*/
  "types": ["vite/client", "vite-plugin-pwa/client", "vite-plugin-pwa/info"]
}

can you confirm serving sw.js from /build/sw.js is working when registering it (confirm there is no errors in dev tools)?

userquin commented 1 year ago

4. @userquin Can i use DOM to insert the script tag inside the head?

yes

anburocky3 commented 1 year ago
  1. @userquin Can i use DOM to insert the script tag inside the head?

yes

I tried to add it like this

if (pwaInfo) {
    const webManifest = pwaInfo.webManifest.linkTag
    /* add link to head: webManifest  is the link */
    document.head.appendChild(webManifest);
}

But since pwaInfo.webManifest returns as string, can't able to use it directly on .appendChild() which accepts the Node type. image

userquin commented 1 year ago

you should create a link then use link.innerHtml = webManifest

userquin commented 1 year ago

@msonowal @fredpedersen @anburocky3

imagen

anburocky3 commented 1 year ago

you should create a link the use innerHtml = webManifest

I tried this, and its working cool

if (pwaInfo) {
    const webManifest = pwaInfo.webManifest.href
    /* add link to head: webManifest  is the link */
    const linkElement = document.createElement("link");
    linkElement.setAttribute("rel", "manifest");
    linkElement.setAttribute("href", webManifest);
    document.head.appendChild(linkElement);
}

How about, we add an docs for future devs.?

userquin commented 1 year ago

@anburocky3 LGTM I'll add a new section, Integrations in docs repo: I'll create a new branch and so you can contribute.