diff --git a/.env.local.example b/.env.local.example index 1abd202a5e..9c7389158f 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,8 +1,13 @@ -# Duplicate this file and remove the '.example' suffix. -# Adjust the values as needed. +# (1) Duplicate this file and remove the '.example' suffix. +# Naming this file '.env.local' is a Vite convention to prevent accidentally +# submitting to git. +# For more info see: https://vitejs.dev/guide/env-and-mode.html#env-files -VITE_IS_ONLINE=true -VITE_WORKBOX_DEBUG=false -SENTRY_AUTH_TOKEN=YOUR_TOKEN -SENTRY_ORG=vikunja -SENTRY_PROJECT=frontend-oss \ No newline at end of file +# (2) Comment in and adjust the values as needed. + +# VITE_IS_ONLINE=true +# VITE_WORKBOX_DEBUG=false +# SENTRY_AUTH_TOKEN=YOUR_TOKEN +# SENTRY_ORG=vikunja +# SENTRY_PROJECT=frontend-oss +# VIKUNJA_FRONTEND_BASE=/custom-subpath \ No newline at end of file diff --git a/src/helpers/getFullBaseUrl.ts b/src/helpers/getFullBaseUrl.ts new file mode 100644 index 0000000000..b0d9ef595f --- /dev/null +++ b/src/helpers/getFullBaseUrl.ts @@ -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 + '/' +} \ No newline at end of file diff --git a/src/registerServiceWorker.ts b/src/registerServiceWorker.ts index a497d63ad2..8e0a30d2f6 100644 --- a/src/registerServiceWorker.ts +++ b/src/registerServiceWorker.ts @@ -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.') }, diff --git a/src/router/index.ts b/src/router/index.ts index 6670e854d4..d031f34b57 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -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) { diff --git a/src/sw.ts b/src/sw.ts index b59a36ef0d..c687a1938a 100644 --- a/src/sw.ts +++ b/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 } }) diff --git a/tsconfig.app.json b/tsconfig.app.json index e059350b1e..b095907f04 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -5,7 +5,7 @@ "compilerOptions": { "composite": true, "baseUrl": ".", - "lib": ["ESNext"], + "lib": ["ESNext", "DOM", "WebWorker"], "importHelpers": true, "sourceMap": true, diff --git a/vite.config.ts b/vite.config.ts index 61b16162b7..5308ee387a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,14 +1,14 @@ /// -import {defineConfig, type PluginOption} from 'vite' +import {defineConfig, type PluginOption, loadEnv} 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' @@ -49,7 +49,14 @@ function createFontMatcher(fontNames: string[]) { } // https://vitejs.dev/config/ -export default defineConfig({ +export default defineConfig(({mode}) => { + // Load env file based on `mode` in the current working directory. + // Set the third parameter to '' to load all env regardless of the `VITE_` prefix. + // https://vitejs.dev/config/#environment-variables + const env = loadEnv(mode, process.cwd(), '') + + return { + base: env.VIKUNJA_FRONTEND_BASE, // https://vitest.dev/config/ test: { environment: 'happy-dom', @@ -65,7 +72,7 @@ export default defineConfig({ plugins: [ postcssEasings(), postcssEasingGradients(), - postcssPresetEnv(), + postcssPresetEnv(), ], }, }, @@ -97,7 +104,6 @@ export default defineConfig({ VitePWA({ srcDir: 'src', filename: 'sw.ts', - base: '/', strategies: 'injectManifest', injectRegister: false, manifest: { @@ -181,4 +187,5 @@ export default defineConfig({ ], }, }, + } })