Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac731ae19 | ||
|
|
2f2beb774a | ||
|
|
a98dc061da |
@@ -1,6 +1,16 @@
|
|||||||
FROM node:24-alpine AS build
|
FROM node:24-alpine AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
ARG RELEASE_COMMIT_SHA=""
|
||||||
|
ARG COMMIT_SHA=""
|
||||||
|
ARG GITHUB_SHA=""
|
||||||
|
ARG SOURCE_COMMIT=""
|
||||||
|
ARG VITE_BASE_PATH=""
|
||||||
|
ENV RELEASE_COMMIT_SHA="${RELEASE_COMMIT_SHA}"
|
||||||
|
ENV COMMIT_SHA="${COMMIT_SHA}"
|
||||||
|
ENV GITHUB_SHA="${GITHUB_SHA}"
|
||||||
|
ENV SOURCE_COMMIT="${SOURCE_COMMIT}"
|
||||||
|
ENV VITE_BASE_PATH="${VITE_BASE_PATH}"
|
||||||
|
|
||||||
RUN apk add --no-cache git
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default defineConfig({
|
|||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
forbidOnly: isCI,
|
forbidOnly: isCI,
|
||||||
retries: isCI ? 2 : 0,
|
retries: isCI ? 2 : 0,
|
||||||
workers: isCI ? 2 : 3,
|
workers: isCI ? 2 : 1,
|
||||||
reporter: [["list"], ["html", { open: "never", outputFolder: "output/playwright/prod/report" }]],
|
reporter: [["list"], ["html", { open: "never", outputFolder: "output/playwright/prod/report" }]],
|
||||||
outputDir: "output/playwright/prod/test-results",
|
outputDir: "output/playwright/prod/test-results",
|
||||||
use: {
|
use: {
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ const getProductOptionsLabel = (vehicle) => {
|
|||||||
v-if="!props.compact"
|
v-if="!props.compact"
|
||||||
:user_id="object.user_id"
|
:user_id="object.user_id"
|
||||||
:reg_1="object.reg"
|
:reg_1="object.reg"
|
||||||
:displayActionsDirectly="false"
|
:displayActionsDirectly="true"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<!-- View (Redirect to the vehicle page) -->
|
<!-- View (Redirect to the vehicle page) -->
|
||||||
|
|||||||
+2
-2
@@ -197,7 +197,7 @@ onMounted(() => {
|
|||||||
<div class="error-state__content">
|
<div class="error-state__content">
|
||||||
<p>{{ t('superuser_invoice_distribution.errors.overview_load_failed') }}: {{ errorMessage }}</p>
|
<p>{{ t('superuser_invoice_distribution.errors.overview_load_failed') }}: {{ errorMessage }}</p>
|
||||||
<b-button type="is-danger" class="control-button is-light" @click="loadOverview">
|
<b-button type="is-danger" class="control-button is-light" @click="loadOverview">
|
||||||
{{ t('common.retry') }}
|
{{ t('superuser_invoice_distribution.actions.try_again') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
</b-message>
|
</b-message>
|
||||||
@@ -281,7 +281,7 @@ onMounted(() => {
|
|||||||
<div v-if="!sortedMonthSummaries.length" class="empty-state has-text-centered">
|
<div v-if="!sortedMonthSummaries.length" class="empty-state has-text-centered">
|
||||||
<p class="has-text-grey mb-3">{{ t('superuser_invoice_distribution.empty.no_months') }}</p>
|
<p class="has-text-grey mb-3">{{ t('superuser_invoice_distribution.empty.no_months') }}</p>
|
||||||
<b-button type="is-link" class="control-button is-light" @click="loadOverview">
|
<b-button type="is-link" class="control-button is-light" @click="loadOverview">
|
||||||
{{ t('common.retry') }}
|
{{ t('superuser_invoice_distribution.actions.try_again') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export function attachPageHealthGuards(page: Page, baseURL: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function expectBodyHasContent(page: Page, minimumLength = 20) {
|
export async function expectBodyHasContent(page: Page, minimumLength = 20, timeout = 45_000) {
|
||||||
await expect
|
await expect
|
||||||
.poll(
|
.poll(
|
||||||
async () => {
|
async () => {
|
||||||
@@ -101,7 +101,7 @@ export async function expectBodyHasContent(page: Page, minimumLength = 20) {
|
|||||||
.catch(() => "");
|
.catch(() => "");
|
||||||
return text.replace(/\s+/g, " ").trim().length;
|
return text.replace(/\s+/g, " ").trim().length;
|
||||||
},
|
},
|
||||||
{ timeout: 15_000 }
|
{ timeout }
|
||||||
)
|
)
|
||||||
.toBeGreaterThan(minimumLength);
|
.toBeGreaterThan(minimumLength);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ test("@public-live api-v2 gateway and channel API prefixes serve JSON ping respo
|
|||||||
test("@public-live guest flow renders on the deployed environment", async ({ page }) => {
|
test("@public-live guest flow renders on the deployed environment", async ({ page }) => {
|
||||||
const guards = attachPageHealthGuards(page, baseURL);
|
const guards = attachPageHealthGuards(page, baseURL);
|
||||||
|
|
||||||
await page.goto("/guest/book/wash");
|
await page.goto(liveUrl("guest/book/wash"));
|
||||||
await settlePage(page);
|
await settlePage(page);
|
||||||
await expect(page).toHaveURL(/\/guest\/book\/wash(?:\?.*)?$/);
|
await expect(page).toHaveURL(/\/guest\/book\/wash(?:\?.*)?$/);
|
||||||
await expectBodyHasContent(page);
|
await expectBodyHasContent(page);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function toLoopbackRequestUrl(url: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function gotoHealthyRoute(page, path: string) {
|
async function gotoHealthyRoute(page, path: string) {
|
||||||
await page.goto(path);
|
await page.goto(path, { waitUntil: "domcontentloaded" });
|
||||||
await settlePage(page);
|
await settlePage(page);
|
||||||
await expectBodyHasContent(page);
|
await expectBodyHasContent(page);
|
||||||
await expect(page.locator("body")).not.toContainText(/404/i);
|
await expect(page.locator("body")).not.toContainText(/404/i);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const API_HOST =
|
const API_HOST =
|
||||||
/https?:\/\/(?:api\.truckwash\.io(?::\d+)?\/.*|localhost(?::\d+)?\/api\/.*|127\.0\.0\.1(?::\d+)?\/api\/.*)/i;
|
/https?:\/\/(?:api(?:-v2)?\.truckwash\.io(?::\d+)?\/.*|localhost(?::\d+)?\/api\/.*|127\.0\.0\.1(?::\d+)?\/api\/.*)/i;
|
||||||
const TINY_PNG = Buffer.from(
|
const TINY_PNG = Buffer.from(
|
||||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAukB9pY9ZxQAAAAASUVORK5CYII=",
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAukB9pY9ZxQAAAAASUVORK5CYII=",
|
||||||
"base64"
|
"base64"
|
||||||
@@ -6042,6 +6042,46 @@ export async function mockApi(page, options = {}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pathname.endsWith("/release/runtime") && method === "GET") {
|
||||||
|
await route.fulfill(
|
||||||
|
json({
|
||||||
|
data: {
|
||||||
|
source: "local",
|
||||||
|
requested_source: "deployment",
|
||||||
|
generated_at: new Date().toISOString(),
|
||||||
|
trace_id: "playwright-local-prod-runtime",
|
||||||
|
channel: {
|
||||||
|
slug: "stable",
|
||||||
|
name: "Stable",
|
||||||
|
default_channel: true,
|
||||||
|
route_slug: "master",
|
||||||
|
},
|
||||||
|
available_channels: [],
|
||||||
|
versions: {
|
||||||
|
frontend: null,
|
||||||
|
api: null,
|
||||||
|
service_set: null,
|
||||||
|
bundle_id: null,
|
||||||
|
bundle: null,
|
||||||
|
},
|
||||||
|
frontend_base_url: "http://127.0.0.1:4173",
|
||||||
|
api_base_url: "https://api-v2.truckwash.io/master/api",
|
||||||
|
urls: {
|
||||||
|
frontend_base_url: "http://127.0.0.1:4173",
|
||||||
|
api_base_url: "https://api-v2.truckwash.io/master/api",
|
||||||
|
},
|
||||||
|
availability: {
|
||||||
|
configured: true,
|
||||||
|
missing: [],
|
||||||
|
status: "ready",
|
||||||
|
explicit: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pathname.endsWith("/worker/version") && method === "GET") {
|
if (pathname.endsWith("/worker/version") && method === "GET") {
|
||||||
await route.fulfill(
|
await route.fulfill(
|
||||||
json({
|
json({
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ vi.mock("@/components/session/token/SessionUser.vue", () => {
|
|||||||
return {
|
return {
|
||||||
SessionUser: {
|
SessionUser: {
|
||||||
canAccessDepartment: (...args) => canAccessDepartment(...args),
|
canAccessDepartment: (...args) => canAccessDepartment(...args),
|
||||||
|
canAccessAssignedDepartment: (...args) => canAccessDepartment(...args),
|
||||||
functions: {
|
functions: {
|
||||||
selectDepartment: (...args) => selectDepartment(...args),
|
selectDepartment: (...args) => selectDepartment(...args),
|
||||||
getDepartmentIdFromUrl: (...args) => getDepartmentIdFromUrl(...args),
|
getDepartmentIdFromUrl: (...args) => getDepartmentIdFromUrl(...args),
|
||||||
@@ -149,12 +150,12 @@ describe("NavigationMenuDepartment", () => {
|
|||||||
it("updates the selected department label when route department changes", async () => {
|
it("updates the selected department label when route department changes", async () => {
|
||||||
const wrapper = mount(NavigationMenuDepartment);
|
const wrapper = mount(NavigationMenuDepartment);
|
||||||
|
|
||||||
expect(wrapper.get("[data-testid='mock-button-label']").text()).toBe("Select department");
|
expect(wrapper.get("[data-testid='mobile-header-department-toggle']").text()).toContain("Select department");
|
||||||
expect(__getDepartmentsMock).toHaveBeenCalledTimes(1);
|
expect(__getDepartmentsMock).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
__setDepartmentId("12");
|
__setDepartmentId("12");
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(wrapper.get("[data-testid='mock-button-label']").text()).toBe("Taastrup");
|
expect(wrapper.get("[data-testid='mobile-header-department-toggle']").text()).toContain("Taastrup");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -205,6 +205,9 @@ function mountStepOne() {
|
|||||||
return mountWithApp(PosDepartmentStep1, {
|
return mountWithApp(PosDepartmentStep1, {
|
||||||
messages: {
|
messages: {
|
||||||
en: {
|
en: {
|
||||||
|
common: {
|
||||||
|
order: "Order",
|
||||||
|
},
|
||||||
admin: {
|
admin: {
|
||||||
pos: {
|
pos: {
|
||||||
license_plates: "License plates",
|
license_plates: "License plates",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @vitest-environment jsdom
|
// @vitest-environment jsdom
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { mountWithApp } from "./helpers/mountWithApp.js";
|
import { mountWithApp } from "./helpers/mountWithApp.js";
|
||||||
import ReferenceAutocompletePOS from "@/components/forms/department/pos/input/ReferenceAutocompletePOS.vue";
|
import ReferenceAutocompletePOS from "@/components/forms/department/pos/input/ReferenceAutocompletePOS.vue";
|
||||||
|
|
||||||
@@ -16,8 +16,10 @@ const flushPromises = async () => {
|
|||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let wrapper = null;
|
||||||
|
|
||||||
function mountAutocomplete(props = {}) {
|
function mountAutocomplete(props = {}) {
|
||||||
return mountWithApp(ReferenceAutocompletePOS, {
|
wrapper = mountWithApp(ReferenceAutocompletePOS, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: "",
|
modelValue: "",
|
||||||
departmentId: 12,
|
departmentId: 12,
|
||||||
@@ -27,6 +29,7 @@ function mountAutocomplete(props = {}) {
|
|||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("ReferenceAutocompletePOS", () => {
|
describe("ReferenceAutocompletePOS", () => {
|
||||||
@@ -35,6 +38,12 @@ describe("ReferenceAutocompletePOS", () => {
|
|||||||
requestState.authenticatedRequest.mockResolvedValue({ data: { data: [] } });
|
requestState.authenticatedRequest.mockResolvedValue({ data: { data: [] } });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
wrapper?.unmount();
|
||||||
|
wrapper = null;
|
||||||
|
await flushPromises();
|
||||||
|
});
|
||||||
|
|
||||||
it("fetches async suggestions with the current POS context", async () => {
|
it("fetches async suggestions with the current POS context", async () => {
|
||||||
requestState.authenticatedRequest.mockResolvedValue({
|
requestState.authenticatedRequest.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe("weatherapi superuser wiring", () => {
|
|||||||
|
|
||||||
it("appears in superuser configuration navigation menus", () => {
|
it("appears in superuser configuration navigation menus", () => {
|
||||||
expect(navItemsSource).toContain("SessionUser.superUser.modules.weatherapi.meta.labels.multiple");
|
expect(navItemsSource).toContain("SessionUser.superUser.modules.weatherapi.meta.labels.multiple");
|
||||||
expect(navItemsSource).toContain("to: '/superuser/configuration/weatherapi'");
|
expect(navItemsSource).toMatch(/to:\s*["']\/superuser\/configuration\/weatherapi["']/);
|
||||||
|
|
||||||
expect(leftMenuSource).toContain("SessionUser.superUser.modules.weatherapi.meta.title");
|
expect(leftMenuSource).toContain("SessionUser.superUser.modules.weatherapi.meta.title");
|
||||||
expect(leftMenuSource).toContain("SessionUser.superUser.modules.weatherapi.meta.config_endpoint");
|
expect(leftMenuSource).toContain("SessionUser.superUser.modules.weatherapi.meta.config_endpoint");
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe("workfeed superuser wiring", () => {
|
|||||||
|
|
||||||
it("appears in superuser configuration navigation menus", () => {
|
it("appears in superuser configuration navigation menus", () => {
|
||||||
expect(navItemsSource).toContain("SessionUser.superUser.modules.workfeed.meta.labels.multiple");
|
expect(navItemsSource).toContain("SessionUser.superUser.modules.workfeed.meta.labels.multiple");
|
||||||
expect(navItemsSource).toContain("to: '/superuser/configuration/workfeed'");
|
expect(navItemsSource).toMatch(/to:\s*["']\/superuser\/configuration\/workfeed["']/);
|
||||||
|
|
||||||
expect(leftMenuSource).toContain("SessionUser.superUser.modules.workfeed.meta.title");
|
expect(leftMenuSource).toContain("SessionUser.superUser.modules.workfeed.meta.title");
|
||||||
expect(leftMenuSource).toContain("SessionUser.superUser.modules.workfeed.meta.config_endpoint");
|
expect(leftMenuSource).toContain("SessionUser.superUser.modules.workfeed.meta.config_endpoint");
|
||||||
|
|||||||
+55
-3
@@ -37,6 +37,11 @@ const PUBLIC_ASSET_ALIASES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
function getGitCommit() {
|
function getGitCommit() {
|
||||||
|
const explicitCommit = getExplicitCommitSha()
|
||||||
|
if (explicitCommit !== 'unknown') {
|
||||||
|
return explicitCommit.slice(0, 12)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return execSync('git rev-parse --short HEAD').toString().trim()
|
return execSync('git rev-parse --short HEAD').toString().trim()
|
||||||
} catch {
|
} catch {
|
||||||
@@ -45,6 +50,11 @@ function getGitCommit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getGitCommitSha() {
|
function getGitCommitSha() {
|
||||||
|
const explicitCommit = getExplicitCommitSha()
|
||||||
|
if (explicitCommit !== 'unknown') {
|
||||||
|
return explicitCommit
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return execSync('git rev-parse HEAD').toString().trim()
|
return execSync('git rev-parse HEAD').toString().trim()
|
||||||
} catch {
|
} catch {
|
||||||
@@ -52,6 +62,17 @@ function getGitCommitSha() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExplicitCommitSha() {
|
||||||
|
for (const key of ['RELEASE_COMMIT_SHA', 'COMMIT_SHA', 'GITHUB_SHA', 'SOURCE_COMMIT', 'VITE_COMMIT_HASH']) {
|
||||||
|
const value = String(process.env[key] || '').trim()
|
||||||
|
if (/^[a-f0-9]{7,40}$/i.test(value)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
function releaseBuildId(commitSha) {
|
function releaseBuildId(commitSha) {
|
||||||
const explicitBuildId = process.env.RELEASE_BUILD_ID || process.env.BUILD_ID
|
const explicitBuildId = process.env.RELEASE_BUILD_ID || process.env.BUILD_ID
|
||||||
if (explicitBuildId) {
|
if (explicitBuildId) {
|
||||||
@@ -66,6 +87,33 @@ function releaseBuildId(commitSha) {
|
|||||||
return `${prefix}-${new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14)}`
|
return `${prefix}-${new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeViteBase(value) {
|
||||||
|
const base = String(value || '').trim()
|
||||||
|
if (!base || base === '/') {
|
||||||
|
return '/'
|
||||||
|
}
|
||||||
|
if (base === './') {
|
||||||
|
return './'
|
||||||
|
}
|
||||||
|
|
||||||
|
return `/${base.replace(/^\/+|\/+$/g, '')}/`
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripReleaseBasePath(assetPath, base) {
|
||||||
|
if (!assetPath || !assetPath.startsWith('/')) {
|
||||||
|
return assetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedBase = normalizeViteBase(base)
|
||||||
|
if (normalizedBase === '/' || normalizedBase === './') {
|
||||||
|
return assetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return assetPath.startsWith(normalizedBase)
|
||||||
|
? `/${assetPath.slice(normalizedBase.length)}`
|
||||||
|
: assetPath
|
||||||
|
}
|
||||||
|
|
||||||
function releaseUrlPath(value, basePath = '/') {
|
function releaseUrlPath(value, basePath = '/') {
|
||||||
if (!value || value.startsWith('#') || /^(?:data|mailto|tel|javascript):/i.test(value)) {
|
if (!value || value.startsWith('#') || /^(?:data|mailto|tel|javascript):/i.test(value)) {
|
||||||
return ''
|
return ''
|
||||||
@@ -230,6 +278,7 @@ function rootPwaManifestAlias() {
|
|||||||
|
|
||||||
function releaseMetadataManifest() {
|
function releaseMetadataManifest() {
|
||||||
let outputDirectory = fromProjectRoot('dist')
|
let outputDirectory = fromProjectRoot('dist')
|
||||||
|
let releaseBasePath = '/'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'release-metadata-manifest',
|
name: 'release-metadata-manifest',
|
||||||
@@ -238,6 +287,7 @@ function releaseMetadataManifest() {
|
|||||||
outputDirectory = path.isAbsolute(config.build.outDir)
|
outputDirectory = path.isAbsolute(config.build.outDir)
|
||||||
? config.build.outDir
|
? config.build.outDir
|
||||||
: path.resolve(config.root, config.build.outDir)
|
: path.resolve(config.root, config.build.outDir)
|
||||||
|
releaseBasePath = config.base || '/'
|
||||||
},
|
},
|
||||||
closeBundle() {
|
closeBundle() {
|
||||||
const releaseEntryPath = path.join(outputDirectory, 'release-entry.json')
|
const releaseEntryPath = path.join(outputDirectory, 'release-entry.json')
|
||||||
@@ -257,11 +307,12 @@ function releaseMetadataManifest() {
|
|||||||
const rootFiles = fs.readdirSync(outputDirectory)
|
const rootFiles = fs.readdirSync(outputDirectory)
|
||||||
const indexAssetUrls = Array.from(indexHtml.matchAll(/\b(?:href|src)=["']([^"']+)["']/g))
|
const indexAssetUrls = Array.from(indexHtml.matchAll(/\b(?:href|src)=["']([^"']+)["']/g))
|
||||||
.map((match) => releaseUrlPath(match[1], '/index.html'))
|
.map((match) => releaseUrlPath(match[1], '/index.html'))
|
||||||
|
.map((assetPath) => stripReleaseBasePath(assetPath, releaseBasePath))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
const releaseEntryAssetUrls = [
|
const releaseEntryAssetUrls = [
|
||||||
releaseEntry.entry ? `/${releaseEntry.entry}` : '',
|
releaseEntry.entry ? `/${releaseEntry.entry}` : '',
|
||||||
...(Array.isArray(releaseEntry.css) ? releaseEntry.css.map((fileName) => `/${fileName}`) : [])
|
...(Array.isArray(releaseEntry.css) ? releaseEntry.css.map((fileName) => `/${fileName}`) : [])
|
||||||
].filter(Boolean)
|
].map((assetPath) => stripReleaseBasePath(assetPath, releaseBasePath)).filter(Boolean)
|
||||||
const pwaAssetUrls = [
|
const pwaAssetUrls = [
|
||||||
'manifest.json',
|
'manifest.json',
|
||||||
'manifest.webmanifest',
|
'manifest.webmanifest',
|
||||||
@@ -275,6 +326,7 @@ function releaseMetadataManifest() {
|
|||||||
]
|
]
|
||||||
.filter((fileName) => fs.existsSync(path.join(outputDirectory, fileName)))
|
.filter((fileName) => fs.existsSync(path.join(outputDirectory, fileName)))
|
||||||
.map((fileName) => `/${fileName.replace(/\\/g, '/')}`)
|
.map((fileName) => `/${fileName.replace(/\\/g, '/')}`)
|
||||||
|
.map((assetPath) => stripReleaseBasePath(assetPath, releaseBasePath))
|
||||||
|
|
||||||
for (const manifestPath of ['manifest.webmanifest', 'assets/manifest.webmanifest']) {
|
for (const manifestPath of ['manifest.webmanifest', 'assets/manifest.webmanifest']) {
|
||||||
const absoluteManifestPath = path.join(outputDirectory, manifestPath)
|
const absoluteManifestPath = path.join(outputDirectory, manifestPath)
|
||||||
@@ -287,7 +339,7 @@ function releaseMetadataManifest() {
|
|||||||
for (const icon of pwaManifest.icons || []) {
|
for (const icon of pwaManifest.icons || []) {
|
||||||
const iconPath = releaseUrlPath(icon.src || '', `/${manifestPath}`)
|
const iconPath = releaseUrlPath(icon.src || '', `/${manifestPath}`)
|
||||||
if (iconPath) {
|
if (iconPath) {
|
||||||
pwaAssetUrls.push(iconPath)
|
pwaAssetUrls.push(stripReleaseBasePath(iconPath, releaseBasePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -372,7 +424,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
process.env.IS_DEV = !isProd ? 'true' : 'false'
|
process.env.IS_DEV = !isProd ? 'true' : 'false'
|
||||||
// Server version updates happen after deploy verification, not during asset builds.
|
// Server version updates happen after deploy verification, not during asset builds.
|
||||||
// Keep a relative base for static hosting; align manifest with base
|
// Keep a relative base for static hosting; align manifest with base
|
||||||
const base = '/'
|
const base = normalizeViteBase(process.env.VITE_BASE_PATH || process.env.BASE_PATH || '/')
|
||||||
const pwaScope = base === './' ? '.' : base
|
const pwaScope = base === './' ? '.' : base
|
||||||
|
|
||||||
// Enable single-file build only when explicitly requested (disabled by default for PWA)
|
// Enable single-file build only when explicitly requested (disabled by default for PWA)
|
||||||
|
|||||||
Reference in New Issue
Block a user