forked from vikunja/frontend
fix(base): Use Build Time Base Path
* If a base path is provided at build time, use it. * Base path can be set with `VIKUNJA_FRONTEND_BASE` at build time * `VIKUNJA_FRONTEND_BASE` sets `import.meta.env.BASE_URL` after Vite resolves it. * Usages of `import.meta.env.BASE_URL` are statically replaced at build time. * If base path is not provided, `import.meta.env.BASE_URL` defaults to '/'. * Documentation: https://vitejs.dev/guide/env-and-mode.html * Fixes: * Manifest not loading because of incorrect path. * Service Worker not loading because path is incorrect in manifest. * Service Worker crashing because import of workbox is from wrong path. * Service Worker not loading a task because path is incorrect in event listener. * Incorrect URLs being set on window because base path is incorrect. * ex: `/login` vs `/base/login` Signed-off-by: Jef Oliver <jef@eljef.me>
This commit is contained in:
parent
81a4f2d977
commit
e92559dc00
14
src/helpers/getFullBaseUrl.ts
Normal file
14
src/helpers/getFullBaseUrl.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Get full BASE_URL
|
||||
* - including path
|
||||
* - will always end with a trailing slash
|
||||
*/
|
||||
export function getFullBaseUrl() {
|
||||
// (1) The injected BASE_URL is declared from the `resolvedBase` that might miss a trailing slash...
|
||||
// see: https://github.com/vitejs/vite/blob/b35fe883fdc699ac1450882562872095abe9959b/packages/vite/src/node/config.ts#LL614C25-L614C25
|
||||
const rawBase = import.meta.env.BASE_URL
|
||||
// (2) so we readd a slash like done here
|
||||
// https://github.com/vitejs/vite/blob/b35fe883fdc699ac1450882562872095abe9959b/packages/vite/src/node/config.ts#L643
|
||||
// See this comment: https://github.com/vitejs/vite/pull/10723#issuecomment-1303627478
|
||||
return rawBase.endsWith('/') ? rawBase : rawBase + '/'
|
||||
}
|
@ -2,8 +2,10 @@
|
||||
|
||||
import {register} from 'register-service-worker'
|
||||
|
||||
import {getFullBaseUrl} from './helpers/getFullBaseUrl'
|
||||
|
||||
if (import.meta.env.PROD) {
|
||||
register('/sw.js', {
|
||||
register(getFullBaseUrl() + 'sw.js', {
|
||||
ready() {
|
||||
console.log('App is being served from cache by a service worker.')
|
||||
},
|
||||
|
@ -81,7 +81,7 @@ const EditTeamComponent = () => import('@/views/teams/EditTeam.vue')
|
||||
const NewTeamComponent = () => import('@/views/teams/NewTeam.vue')
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// If the user is using their forward/backward keys to navigate, we want to restore the scroll view
|
||||
if (savedPosition) {
|
||||
|
12
src/sw.ts
12
src/sw.ts
@ -1,10 +1,16 @@
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import {getFullBaseUrl} from './helpers/getFullBaseUrl'
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope
|
||||
|
||||
const fullBaseUrl = getFullBaseUrl()
|
||||
const workboxVersion = 'v6.5.4'
|
||||
importScripts( `/workbox-${workboxVersion}/workbox-sw.js`)
|
||||
|
||||
importScripts(`${fullBaseUrl}workbox-${workboxVersion}/workbox-sw.js`)
|
||||
workbox.setConfig({
|
||||
modulePathPrefix: `/workbox-${workboxVersion}`,
|
||||
modulePathPrefix: `${fullBaseUrl}workbox-${workboxVersion}`,
|
||||
debug: Boolean(import.meta.env.VITE_WORKBOX_DEBUG),
|
||||
})
|
||||
|
||||
@ -47,7 +53,7 @@ self.addEventListener('notificationclick', function (event) {
|
||||
|
||||
switch (event.action) {
|
||||
case 'show-task':
|
||||
clients.openWindow(`/tasks/${taskId}`)
|
||||
clients.openWindow(`${fullBaseUrl}tasks/${taskId}`)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
@ -5,7 +5,7 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"baseUrl": ".",
|
||||
"lib": ["ESNext"],
|
||||
"lib": ["ESNext", "DOM", "WebWorker"],
|
||||
|
||||
"importHelpers": true,
|
||||
"sourceMap": true,
|
||||
|
256
vite.config.ts
256
vite.config.ts
@ -2,13 +2,13 @@
|
||||
import {defineConfig, type PluginOption} from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import legacyFn from '@vitejs/plugin-legacy'
|
||||
import { URL, fileURLToPath } from 'node:url'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import {URL, fileURLToPath} from 'node:url'
|
||||
import {dirname, resolve} from 'node:path'
|
||||
|
||||
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
|
||||
import {VitePWA} from 'vite-plugin-pwa'
|
||||
import {VitePWA} from 'vite-plugin-pwa'
|
||||
import VitePluginInjectPreload from 'vite-plugin-inject-preload'
|
||||
import {visualizer} from 'rollup-plugin-visualizer'
|
||||
import {visualizer} from 'rollup-plugin-visualizer'
|
||||
import svgLoader from 'vite-svg-loader'
|
||||
import postcssPresetEnv from 'postcss-preset-env'
|
||||
import postcssEasings from 'postcss-easings'
|
||||
@ -50,135 +50,135 @@ function createFontMatcher(fontNames: string[]) {
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
// https://vitest.dev/config/
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: PREFIXED_SCSS_STYLES,
|
||||
charset: false, // fixes "@charset" must be the first rule in the file" warnings
|
||||
},
|
||||
base: process.env.VIKUNJA_FRONTEND_BASE,
|
||||
// https://vitest.dev/config/
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: PREFIXED_SCSS_STYLES,
|
||||
charset: false, // fixes "@charset" must be the first rule in the file" warnings
|
||||
},
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssEasings(),
|
||||
postcssEasingGradients(),
|
||||
},
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssEasings(),
|
||||
postcssEasingGradients(),
|
||||
postcssPresetEnv(),
|
||||
],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
reactivityTransform: true,
|
||||
}),
|
||||
legacy,
|
||||
svgLoader({
|
||||
// Since the svgs are already manually optimized via https://jakearchibald.github.io/svgomg/
|
||||
// we don't need to optimize them again.
|
||||
svgo: false,
|
||||
}),
|
||||
VueI18nPlugin({
|
||||
// TODO: only install needed stuff
|
||||
// Whether to install the full set of APIs, components, etc. provided by Vue I18n.
|
||||
// By default, all of them will be installed.
|
||||
fullInstall: true,
|
||||
include: resolve(dirname(pathSrc), './src/i18n/lang/**'),
|
||||
}),
|
||||
// https://github.com/Applelo/vite-plugin-inject-preload
|
||||
VitePluginInjectPreload({
|
||||
files: [{
|
||||
match: createFontMatcher(['Quicksand', 'OpenSans', 'OpenSans-Italic']),
|
||||
attributes: {crossorigin: 'anonymous'},
|
||||
}],
|
||||
injectTo: 'custom',
|
||||
}),
|
||||
VitePWA({
|
||||
srcDir: 'src',
|
||||
filename: 'sw.ts',
|
||||
base: '/',
|
||||
strategies: 'injectManifest',
|
||||
injectRegister: false,
|
||||
manifest: {
|
||||
name: 'Vikunja',
|
||||
short_name: 'Vikunja',
|
||||
theme_color: '#1973ff',
|
||||
icons: [
|
||||
{
|
||||
src: './images/icons/android-chrome-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: './images/icons/android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: './images/icons/icon-maskable.png',
|
||||
sizes: '1024x1024',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
start_url: '.',
|
||||
display: 'standalone',
|
||||
background_color: '#000000',
|
||||
shortcuts: [
|
||||
{
|
||||
name: 'Overview',
|
||||
url: '/',
|
||||
},
|
||||
{
|
||||
name: 'Namespaces And Lists Overview',
|
||||
short_name: 'Namespaces & Lists',
|
||||
url: '/namespaces',
|
||||
},
|
||||
{
|
||||
name: 'Tasks Next Week',
|
||||
short_name: 'Next Week',
|
||||
url: '/tasks/by/week',
|
||||
},
|
||||
{
|
||||
name: 'Tasks Next Month',
|
||||
short_name: 'Next Month',
|
||||
url: '/tasks/by/month',
|
||||
},
|
||||
{
|
||||
name: 'Teams Overview',
|
||||
short_name: 'Teams',
|
||||
url: '/teams',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: '@',
|
||||
replacement: pathSrc,
|
||||
},
|
||||
],
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
server: {
|
||||
host: '127.0.0.1', // see: https://github.com/vitejs/vite/pull/8543
|
||||
port: 4173,
|
||||
strictPort: true,
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
rollupOptions: {
|
||||
plugins: [
|
||||
visualizer({
|
||||
filename: 'stats.html',
|
||||
gzipSize: true,
|
||||
// template: 'sunburst',
|
||||
// brotliSize: true,
|
||||
}) as PluginOption,
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
reactivityTransform: true,
|
||||
}),
|
||||
legacy,
|
||||
svgLoader({
|
||||
// Since the svgs are already manually optimized via https://jakearchibald.github.io/svgomg/
|
||||
// we don't need to optimize them again.
|
||||
svgo: false,
|
||||
}),
|
||||
VueI18nPlugin({
|
||||
// TODO: only install needed stuff
|
||||
// Whether to install the full set of APIs, components, etc. provided by Vue I18n.
|
||||
// By default, all of them will be installed.
|
||||
fullInstall: true,
|
||||
include: resolve(dirname(pathSrc), './src/i18n/lang/**'),
|
||||
}),
|
||||
// https://github.com/Applelo/vite-plugin-inject-preload
|
||||
VitePluginInjectPreload({
|
||||
files: [{
|
||||
match: createFontMatcher(['Quicksand', 'OpenSans', 'OpenSans-Italic']),
|
||||
attributes: {crossorigin: 'anonymous'},
|
||||
}],
|
||||
injectTo: 'custom',
|
||||
}),
|
||||
VitePWA({
|
||||
srcDir: 'src',
|
||||
filename: 'sw.ts',
|
||||
strategies: 'injectManifest',
|
||||
injectRegister: false,
|
||||
manifest: {
|
||||
name: 'Vikunja',
|
||||
short_name: 'Vikunja',
|
||||
theme_color: '#1973ff',
|
||||
icons: [
|
||||
{
|
||||
src: './images/icons/android-chrome-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: './images/icons/android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: './images/icons/icon-maskable.png',
|
||||
sizes: '1024x1024',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
start_url: '.',
|
||||
display: 'standalone',
|
||||
background_color: '#000000',
|
||||
shortcuts: [
|
||||
{
|
||||
name: 'Overview',
|
||||
url: '/',
|
||||
},
|
||||
{
|
||||
name: 'Namespaces And Lists Overview',
|
||||
short_name: 'Namespaces & Lists',
|
||||
url: '/namespaces',
|
||||
},
|
||||
{
|
||||
name: 'Tasks Next Week',
|
||||
short_name: 'Next Week',
|
||||
url: '/tasks/by/week',
|
||||
},
|
||||
{
|
||||
name: 'Tasks Next Month',
|
||||
short_name: 'Next Month',
|
||||
url: '/tasks/by/month',
|
||||
},
|
||||
{
|
||||
name: 'Teams Overview',
|
||||
short_name: 'Teams',
|
||||
url: '/teams',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: '@',
|
||||
replacement: pathSrc,
|
||||
},
|
||||
],
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
server: {
|
||||
host: '127.0.0.1', // see: https://github.com/vitejs/vite/pull/8543
|
||||
port: 4173,
|
||||
strictPort: true,
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
rollupOptions: {
|
||||
plugins: [
|
||||
visualizer({
|
||||
filename: 'stats.html',
|
||||
gzipSize: true,
|
||||
// template: 'sunburst',
|
||||
// brotliSize: true,
|
||||
}) as PluginOption,
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user