233 lines
9.2 KiB
JavaScript
233 lines
9.2 KiB
JavaScript
import path from 'node:path'
|
|
import { defineConfig } from 'vite'
|
|
import vue from '@vitejs/plugin-vue'
|
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
|
import { viteSingleFile } from 'vite-plugin-singlefile'
|
|
import { VitePWA } from 'vite-plugin-pwa'
|
|
import VueJsx from '@vitejs/plugin-vue-jsx'
|
|
import { execSync } from 'node:child_process'
|
|
|
|
const BUEFY_CSS_REPLACEMENTS = new Map([
|
|
[
|
|
'@media screen and (max-width: calc(var(--bulma-sidebar-mobile-breakpoint) - 1px))',
|
|
'@media screen and (max-width: 768px)'
|
|
],
|
|
[
|
|
'@media screen and (max-width: var(--bulma-steps-mobile-max-width)-1px)',
|
|
'@media screen and (max-width: 768px)'
|
|
],
|
|
[
|
|
'var(--bulma-$steps-divider-height)',
|
|
'var(--bulma-steps-divider-height)'
|
|
]
|
|
])
|
|
|
|
function getGitCommit() {
|
|
try {
|
|
return execSync('git rev-parse --short HEAD').toString().trim()
|
|
} catch {
|
|
return 'unknown'
|
|
}
|
|
}
|
|
|
|
function patchBuefyCssMediaQuery() {
|
|
return {
|
|
name: 'patch-buefy-css-media-query',
|
|
enforce: 'pre',
|
|
transform(code, id) {
|
|
if (!id.includes('buefy/dist/css/buefy.css')) {
|
|
return null
|
|
}
|
|
|
|
let nextCode = code
|
|
let didPatch = false
|
|
|
|
for (const [invalidSnippet, fixedSnippet] of BUEFY_CSS_REPLACEMENTS.entries()) {
|
|
if (!nextCode.includes(invalidSnippet)) {
|
|
continue
|
|
}
|
|
|
|
nextCode = nextCode.replaceAll(invalidSnippet, fixedSnippet)
|
|
didPatch = true
|
|
}
|
|
|
|
if (!didPatch) {
|
|
return null
|
|
}
|
|
|
|
return nextCode
|
|
}
|
|
}
|
|
}
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
const isProd = mode === 'production'
|
|
const isPlaywrightRuntime = process.env.PLAYWRIGHT === '1'
|
|
const workspaceRoot = process.cwd()
|
|
|
|
// Set COMMIT_HASH env var for use in the app
|
|
const version = process.env.npm_package_version || '0.0.0'
|
|
const commit = getGitCommit()
|
|
console.log(`Building version ${version} (commit: ${commit})`)
|
|
process.env.COMMIT_HASH = commit
|
|
process.env.APP_VERSION = version
|
|
process.env.IS_DEV = !isProd ? 'true' : 'false'
|
|
// If the build is a production build, update the server with the new version
|
|
if (isProd) {
|
|
console.log('Updating server with new version...')
|
|
const token = process.env.SERVER_UPDATE_TOKEN
|
|
if (!token) {
|
|
console.warn('SERVER_UPDATE_TOKEN is not set. Skipping server update.')
|
|
} else {
|
|
// Run GET request to update version endpoint
|
|
const url = `https://api.truckwash.io/worker/update-version?version=${commit}`
|
|
const bearer = `Bearer ${token}`
|
|
console.log(`Calling URL: ${url}`)
|
|
try {
|
|
execSync(`curl -f -X GET "${url}" -H "Authorization: ${bearer}"`)
|
|
console.log('Successfully updated server with new version.')
|
|
} catch (error) {
|
|
console.error('Failed to update server with new version:', error.message)
|
|
}
|
|
}
|
|
}
|
|
// Keep a relative base for static hosting; align manifest with base
|
|
const base = '/'
|
|
const pwaScope = base === './' ? '.' : base
|
|
|
|
// Enable single-file build only when explicitly requested (disabled by default for PWA)
|
|
const enableSingleFile = process.env.VITE_SINGLE_FILE === 'true' && !isProd
|
|
|
|
return {
|
|
base,
|
|
plugins: [
|
|
patchBuefyCssMediaQuery(),
|
|
vue(),
|
|
VueJsx(),
|
|
!isProd && !isPlaywrightRuntime && vueDevTools(),
|
|
enableSingleFile && viteSingleFile(),
|
|
VitePWA({
|
|
registerType: 'autoUpdate',
|
|
injectRegister: isProd ? 'auto' : false,
|
|
strategies: 'generateSW',
|
|
devOptions: {
|
|
enabled: false // keep for local dev
|
|
},
|
|
workbox: {
|
|
maximumFileSizeToCacheInBytes: 12 * 1024 * 1024,
|
|
cleanupOutdatedCaches: true,
|
|
skipWaiting: true,
|
|
clientsClaim: true,
|
|
globPatterns: ['**/*.{js,css,html,ico,svg,woff2}'],
|
|
globIgnores: ['**/registerSW.js', '**/sw.js'],
|
|
runtimeCaching: [
|
|
{
|
|
// cache API responses
|
|
urlPattern: ({ url }) => url.origin === 'https://api.truckwash.io',
|
|
handler: 'NetworkFirst',
|
|
options: {
|
|
cacheName: 'pleno-api-cache',
|
|
expiration: {
|
|
maxEntries: 100,
|
|
maxAgeSeconds: 5 * 60 // 5 minutes
|
|
},
|
|
networkTimeoutSeconds: 10,
|
|
cacheableResponse: {
|
|
statuses: [200] // avoid opaque caching for API
|
|
}
|
|
}
|
|
},
|
|
{
|
|
// cache same-origin static assets
|
|
urlPattern: ({ sameOrigin, request }) =>
|
|
sameOrigin && ['style', 'script', 'font'].includes(request.destination),
|
|
handler: 'StaleWhileRevalidate',
|
|
options: {
|
|
cacheName: 'pleno-website-cache',
|
|
expiration: {
|
|
maxEntries: 50,
|
|
maxAgeSeconds: 24 * 60 * 60 // 24 hours
|
|
},
|
|
cacheableResponse: {
|
|
statuses: [0, 200]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
// cache images (restrict to same-origin; loosen if you use a trusted CDN)
|
|
urlPattern: ({ sameOrigin, request }) =>
|
|
sameOrigin && request.destination === 'image',
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'pleno-image-cache',
|
|
expiration: {
|
|
maxEntries: 100,
|
|
maxAgeSeconds: 7 * 24 * 60 * 60 // 7 days
|
|
},
|
|
cacheableResponse: {
|
|
statuses: [0, 200]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
manifest: {
|
|
name: 'Truck Wash Kundeportal',
|
|
short_name: 'Truck Wash',
|
|
description: 'Access your Truck Wash accounts and transactions from anywhere.',
|
|
theme_color: '#063651',
|
|
background_color: '#0787bb',
|
|
// Use relative scope when base is relative
|
|
start_url: pwaScope,
|
|
scope: pwaScope,
|
|
display: 'standalone',
|
|
orientation: 'portrait',
|
|
launch_handler: { client_mode: 'navigate-existing' },
|
|
icons: [
|
|
{ src: 'favicons/web-app-manifest-192x192.png', sizes: '192x192', type: 'image/png' },
|
|
{ src: 'favicons/web-app-manifest-512x512.png', sizes: '512x512', type: 'image/png' },
|
|
// Optional: add a maskable icon
|
|
// { src: 'icons/icon-512x512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'any maskable' }
|
|
],
|
|
id: 'pleno-pwa-test'
|
|
},
|
|
useCredentials: true
|
|
})
|
|
].filter(Boolean),
|
|
resolve: {
|
|
alias: {
|
|
// Anchor aliases to the active workspace so hardlinked worktrees do not
|
|
// accidentally resolve into a sibling checkout.
|
|
'@': path.resolve(workspaceRoot, 'src')
|
|
}
|
|
},
|
|
define: {
|
|
'import.meta.env.VITE_BUILD_DATE': JSON.stringify(new Date().toISOString()),
|
|
'import.meta.env.VITE_APP_VERSION': JSON.stringify(process.env.APP_VERSION || '0.0.0'),
|
|
'import.meta.env.VITE_COMMIT_HASH': JSON.stringify(commit),
|
|
// Tip: import.meta.env.DEV/PROD are available at runtime
|
|
'import.meta.env.VITE_IS_DEV': JSON.stringify(!isProd),
|
|
},
|
|
css: {
|
|
preprocessorOptions: {
|
|
scss: {
|
|
api: 'modern-compiler',
|
|
quietDeps: true,
|
|
silenceDeprecations: ['import', 'global-builtin', 'legacy-js-api', 'if-function']
|
|
}
|
|
}
|
|
},
|
|
server: {
|
|
watch: {
|
|
ignored: ['**/output/playwright/**']
|
|
}
|
|
},
|
|
optimizeDeps: isPlaywrightRuntime
|
|
? {
|
|
entries: ['index.html']
|
|
}
|
|
: undefined
|
|
|
|
}
|
|
})
|