Add PWA support with VitePWA integration, including manifest configuration and service worker registration. Update package dependencies for PWA assets generation. Enhance application metadata for improved PWA experience.

This commit is contained in:
Yoga Pangestu 2026-06-10 19:06:52 +07:00
parent a1c2ab5bfa
commit b05c17de57
14 changed files with 4254 additions and 28 deletions

4100
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,13 +10,15 @@
"format:check": "prettier --check resources/",
"lint": "eslint . --fix",
"lint:check": "eslint .",
"types:check": "vue-tsc --noEmit"
"types:check": "vue-tsc --noEmit",
"pwa-icons": "pwa-assets-generator --preset minimal public/assets/logo.png"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@laravel/vite-plugin-wayfinder": "^0.1.3",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/node": "^22.13.5",
"@vite-pwa/assets-generator": "^1.0.2",
"@vue/eslint-config-typescript": "^14.3.0",
"eslint": "^9.17.0",
"eslint-config-prettier": "^10.0.1",
@ -27,6 +29,7 @@
"prettier-plugin-tailwindcss": "^0.6.11",
"tw-animate-css": "^1.4.0",
"typescript-eslint": "^8.23.0",
"vite-plugin-pwa": "^1.3.0",
"vue-tsc": "^2.2.4"
},
"dependencies": {

View File

@ -1,3 +1,12 @@
<IfModule mod_mime.c>
AddType application/manifest+json .webmanifest
</IfModule>
<IfModule mod_headers.c>
SetEnvIf Request_URI "^/build/sw\.js$" sw_file
Header set Service-Worker-Allowed "/" env=sw_file
</IfModule>
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/assets/pwa-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

View File

@ -1,9 +1,14 @@
/// <reference types="vite-plugin-pwa/client" />
import { createInertiaApp } from '@inertiajs/vue3';
import { registerSW } from 'virtual:pwa-register';
import { createApp, h } from 'vue';
import 'vue-sonner/style.css';
import { Toaster } from '@/components/ui/sonner';
import { useFlashToast } from '@/composables/useFlashToast';
registerSW({ immediate: true });
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
createInertiaApp({

View File

@ -3,10 +3,13 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ config('app.name', 'Laravel') }} - Progressive Web App">
<meta name="theme-color" content="#171717">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="apple-touch-icon" href="/assets/apple-touch-icon-180x180.png" sizes="180x180">
<link rel="manifest" href="/build/manifest.webmanifest">
@fonts

28
server.php Normal file
View File

@ -0,0 +1,28 @@
<?php
/**
* Override Laravel's default server router for `php artisan serve`.
* Serves the service worker with the Service-Worker-Allowed header
* so it can control the entire site scope from /build/sw.js.
*
* @see https://vite-pwa-org.netlify.app/frameworks/laravel.html
*/
$publicPath = getcwd();
$uri = urldecode(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?? ''
);
if ($uri !== '/' && $uri !== '/build/sw.js' && file_exists($publicPath.$uri)) {
return false;
}
if ($uri === '/build/sw.js') {
header('Service-Worker-Allowed: /');
header('Content-Type: text/javascript');
echo file_get_contents(__DIR__.'/public/build/sw.js');
exit;
}
require_once $publicPath.'/index.php';

View File

@ -4,31 +4,111 @@ import tailwindcss from '@tailwindcss/vite';
import vue from '@vitejs/plugin-vue';
import laravel from 'laravel-vite-plugin';
import { bunny } from 'laravel-vite-plugin/fonts';
import { defineConfig } from 'vite';
import { defineConfig, loadEnv } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.ts'],
refresh: true,
fonts: [
bunny('Instrument Sans', {
weights: [400, 500, 600],
}),
],
}),
inertia(),
tailwindcss(),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
const manifestIcons = [
{
src: '/assets/pwa-64x64.png',
sizes: '64x64',
type: 'image/png',
},
{
src: '/assets/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/assets/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any',
},
{
src: '/assets/maskable-icon-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable',
},
] as const;
const publicIcons = [
{ src: '/favicon.ico' },
{ src: '/favicon.svg' },
{ src: '/assets/apple-touch-icon-180x180.png' },
{ src: '/assets/logo.png' },
];
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const appName = env.VITE_APP_NAME || 'DST Collection';
return {
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.ts'],
refresh: true,
fonts: [
bunny('Instrument Sans', {
weights: [400, 500, 600],
}),
],
}),
inertia(),
tailwindcss(),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
},
}),
wayfinder({
formVariants: true,
}),
],
}),
wayfinder({
formVariants: true,
}),
VitePWA({
buildBase: '/build/',
scope: '/',
base: '/',
registerType: 'autoUpdate',
devOptions: {
enabled: false,
},
includeAssets: [],
workbox: {
globPatterns: [
'**/*.{js,css,html,ico,jpg,png,svg,woff,woff2,ttf,eot}',
],
navigateFallback: '/',
navigateFallbackDenylist: [/^\/telescope/],
additionalManifestEntries: [
{ url: '/', revision: `${Date.now()}` },
...manifestIcons.map((icon) => ({
url: icon.src,
revision: `${Date.now()}`,
})),
...publicIcons.map((icon) => ({
url: icon.src,
revision: `${Date.now()}`,
})),
],
maximumFileSizeToCacheInBytes: 3_000_000,
},
manifest: {
name: appName,
short_name: appName,
description: `${appName} - Progressive Web App`,
theme_color: '#171717',
background_color: '#ffffff',
display: 'standalone',
orientation: 'portrait',
scope: '/',
start_url: '/',
id: '/',
icons: [...manifestIcons],
},
}),
],
};
});