Move brand images to proprietary/ and support multi-dir asset pipeline (#3662)
## Description: * Move proprietary brand images (logos, favicon) from resources/images/ to proprietary/images/ to separate open-source assets from proprietary ones * Extend the asset pipeline (PublicAssetManifest, vite.config.ts) to support multiple source directories (resources/ + proprietary/), so buildAssetUrl resolves assets from either location transparently * In dev, serve proprietary/ as a fallback middleware (registered after Vite's publicDir handler) so resources/ takes precedence when files exist in both. The idea is we could have placeholder assets placeholders that can be used by forks, and only the production build uses proprietary assets. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
@@ -1,7 +1,5 @@
|
|||||||
import { Howl } from "howler";
|
import { Howl } from "howler";
|
||||||
import of4 from "../../../proprietary/sounds/music/of4.mp3";
|
import { assetUrl } from "../../core/AssetUrls";
|
||||||
import openfront from "../../../proprietary/sounds/music/openfront.mp3";
|
|
||||||
import war from "../../../proprietary/sounds/music/war.mp3";
|
|
||||||
import { EventBus } from "../../core/EventBus";
|
import { EventBus } from "../../core/EventBus";
|
||||||
import { UserSettings } from "../../core/game/UserSettings";
|
import { UserSettings } from "../../core/game/UserSettings";
|
||||||
import {
|
import {
|
||||||
@@ -33,19 +31,19 @@ export class SoundManager {
|
|||||||
this.safely("initialize background music", () => {
|
this.safely("initialize background music", () => {
|
||||||
this.backgroundMusic = [
|
this.backgroundMusic = [
|
||||||
new Howl({
|
new Howl({
|
||||||
src: [of4],
|
src: [assetUrl("sounds/music/of4.mp3")],
|
||||||
loop: false,
|
loop: false,
|
||||||
onend: this.playNext.bind(this),
|
onend: this.playNext.bind(this),
|
||||||
volume: 0,
|
volume: 0,
|
||||||
}),
|
}),
|
||||||
new Howl({
|
new Howl({
|
||||||
src: [openfront],
|
src: [assetUrl("sounds/music/openfront.mp3")],
|
||||||
loop: false,
|
loop: false,
|
||||||
onend: this.playNext.bind(this),
|
onend: this.playNext.bind(this),
|
||||||
volume: 0,
|
volume: 0,
|
||||||
}),
|
}),
|
||||||
new Howl({
|
new Howl({
|
||||||
src: [war],
|
src: [assetUrl("sounds/music/war.mp3")],
|
||||||
loop: false,
|
loop: false,
|
||||||
onend: this.playNext.bind(this),
|
onend: this.playNext.bind(this),
|
||||||
volume: 0,
|
volume: 0,
|
||||||
|
|||||||
@@ -233,20 +233,44 @@ export function getResourcesDir(rootDir: string = process.cwd()): string {
|
|||||||
return path.join(rootDir, "resources");
|
return path.join(rootDir, "resources");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProprietaryDir(rootDir: string = process.cwd()): string {
|
||||||
|
return path.join(rootDir, "proprietary");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scans directories with synchronous fs.existsSync — assumes a small number of sourceDirs.
|
||||||
|
function resolveSourceDir(relativePath: string, sourceDirs: string[]): string {
|
||||||
|
for (const dir of sourceDirs) {
|
||||||
|
const candidate = path.join(dir, relativePath);
|
||||||
|
if (fs.existsSync(candidate)) {
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Asset ${relativePath} not found in any source directory: ${sourceDirs.join(", ")}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSourceFile(relativePath: string, sourceDirs: string[]): string {
|
||||||
|
return path.join(resolveSourceDir(relativePath, sourceDirs), relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
export function shouldKeepRootPublicFile(relativePath: string): boolean {
|
export function shouldKeepRootPublicFile(relativePath: string): boolean {
|
||||||
return ROOT_PUBLIC_FILES.has(normalizeAssetPath(relativePath));
|
return ROOT_PUBLIC_FILES.has(normalizeAssetPath(relativePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listHashedPublicAssetPaths(resourcesDir: string): string[] {
|
export function listHashedPublicAssetPaths(sourceDirs: string[]): string[] {
|
||||||
const files = new Set<string>();
|
const files = new Set<string>();
|
||||||
for (const pattern of HASHED_PUBLIC_ASSET_GLOBS) {
|
for (const dir of sourceDirs) {
|
||||||
for (const file of globSync(pattern, {
|
if (!fs.existsSync(dir)) continue;
|
||||||
cwd: resourcesDir,
|
for (const pattern of HASHED_PUBLIC_ASSET_GLOBS) {
|
||||||
nodir: true,
|
for (const file of globSync(pattern, {
|
||||||
dot: false,
|
cwd: dir,
|
||||||
posix: true,
|
nodir: true,
|
||||||
})) {
|
dot: false,
|
||||||
files.add(normalizeAssetPath(file));
|
posix: true,
|
||||||
|
})) {
|
||||||
|
files.add(normalizeAssetPath(file));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...files].sort();
|
return [...files].sort();
|
||||||
@@ -264,13 +288,14 @@ export function listRootPublicFiles(resourcesDir: string): string[] {
|
|||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildPublicAssetManifest(resourcesDir: string): AssetManifest {
|
export function buildPublicAssetManifest(sourceDirs: string[]): AssetManifest {
|
||||||
const cached = manifestCache.get(resourcesDir);
|
const cacheKey = sourceDirs.join("\0");
|
||||||
|
const cached = manifestCache.get(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedPublicAssetPaths = listHashedPublicAssetPaths(resourcesDir);
|
const hashedPublicAssetPaths = listHashedPublicAssetPaths(sourceDirs);
|
||||||
const rawAssetPaths = hashedPublicAssetPaths.filter(
|
const rawAssetPaths = hashedPublicAssetPaths.filter(
|
||||||
(relativePath) => !isDerivedPublicAsset(relativePath),
|
(relativePath) => !isDerivedPublicAsset(relativePath),
|
||||||
);
|
);
|
||||||
@@ -280,14 +305,14 @@ export function buildPublicAssetManifest(resourcesDir: string): AssetManifest {
|
|||||||
|
|
||||||
const manifest: AssetManifest = {};
|
const manifest: AssetManifest = {};
|
||||||
for (const relativePath of rawAssetPaths) {
|
for (const relativePath of rawAssetPaths) {
|
||||||
const absolutePath = path.join(resourcesDir, relativePath);
|
const absolutePath = resolveSourceFile(relativePath, sourceDirs);
|
||||||
const hash = createContentHash(absolutePath);
|
const hash = createContentHash(absolutePath);
|
||||||
manifest[relativePath] = createHashedAssetUrl(relativePath, hash);
|
manifest[relativePath] = createHashedAssetUrl(relativePath, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const relativePath of derivedAssetPaths) {
|
for (const relativePath of derivedAssetPaths) {
|
||||||
const renderedAsset = renderDerivedPublicAsset(
|
const renderedAsset = renderDerivedPublicAsset(
|
||||||
resourcesDir,
|
resolveSourceDir(relativePath, sourceDirs),
|
||||||
relativePath,
|
relativePath,
|
||||||
manifest,
|
manifest,
|
||||||
);
|
);
|
||||||
@@ -301,7 +326,7 @@ export function buildPublicAssetManifest(resourcesDir: string): AssetManifest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestCache.set(resourcesDir, manifest);
|
manifestCache.set(cacheKey, manifest);
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,17 +335,18 @@ export function clearPublicAssetManifestCache(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createHashedPublicAssetFiles(
|
export function createHashedPublicAssetFiles(
|
||||||
resourcesDir: string,
|
sourceDirs: string[],
|
||||||
outDir: string,
|
outDir: string,
|
||||||
assetManifest: AssetManifest,
|
assetManifest: AssetManifest,
|
||||||
): void {
|
): void {
|
||||||
for (const [relativePath, hashedUrl] of Object.entries(assetManifest)) {
|
for (const [relativePath, hashedUrl] of Object.entries(assetManifest)) {
|
||||||
const sourcePath = path.join(resourcesDir, relativePath);
|
const sourceDir = resolveSourceDir(relativePath, sourceDirs);
|
||||||
|
const sourcePath = path.join(sourceDir, relativePath);
|
||||||
const outputPath = path.join(outDir, normalizeAssetPath(hashedUrl));
|
const outputPath = path.join(outDir, normalizeAssetPath(hashedUrl));
|
||||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||||
|
|
||||||
const renderedAsset = renderDerivedPublicAsset(
|
const renderedAsset = renderDerivedPublicAsset(
|
||||||
resourcesDir,
|
sourceDir,
|
||||||
relativePath,
|
relativePath,
|
||||||
assetManifest,
|
assetManifest,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,17 +39,6 @@ vi.mock("howler", () => {
|
|||||||
return { Howl: MockHowl };
|
return { Howl: MockHowl };
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mock music imports
|
|
||||||
vi.mock("../../../../proprietary/sounds/music/of4.mp3", () => ({
|
|
||||||
default: "of4.mp3",
|
|
||||||
}));
|
|
||||||
vi.mock("../../../../proprietary/sounds/music/openfront.mp3", () => ({
|
|
||||||
default: "openfront.mp3",
|
|
||||||
}));
|
|
||||||
vi.mock("../../../../proprietary/sounds/music/war.mp3", () => ({
|
|
||||||
default: "war.mp3",
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock the Sounds module so tests don't depend on actual asset paths
|
// Mock the Sounds module so tests don't depend on actual asset paths
|
||||||
vi.mock("../../../src/client/sound/Sounds", async (importOriginal) => {
|
vi.mock("../../../src/client/sound/Sounds", async (importOriginal) => {
|
||||||
const actual =
|
const actual =
|
||||||
|
|||||||
@@ -107,11 +107,11 @@ describe("PublicAssetManifest", () => {
|
|||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
|
|
||||||
const firstManifest = buildPublicAssetManifest(resourcesDir);
|
const firstManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
const firstManifestHref = firstManifest["manifest.json"];
|
const firstManifestHref = firstManifest["manifest.json"];
|
||||||
const firstIconHref = firstManifest["icons/app-icon.png"];
|
const firstIconHref = firstManifest["icons/app-icon.png"];
|
||||||
|
|
||||||
createHashedPublicAssetFiles(resourcesDir, outDir, firstManifest);
|
createHashedPublicAssetFiles([resourcesDir], outDir, firstManifest);
|
||||||
const firstOutput = await fs.readFile(
|
const firstOutput = await fs.readFile(
|
||||||
path.join(outDir, firstManifestHref.slice(1)),
|
path.join(outDir, firstManifestHref.slice(1)),
|
||||||
"utf8",
|
"utf8",
|
||||||
@@ -124,7 +124,7 @@ describe("PublicAssetManifest", () => {
|
|||||||
);
|
);
|
||||||
clearPublicAssetManifestCache();
|
clearPublicAssetManifestCache();
|
||||||
|
|
||||||
const secondManifest = buildPublicAssetManifest(resourcesDir);
|
const secondManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
const secondManifestHref = secondManifest["manifest.json"];
|
const secondManifestHref = secondManifest["manifest.json"];
|
||||||
const secondIconHref = secondManifest["icons/app-icon.png"];
|
const secondIconHref = secondManifest["icons/app-icon.png"];
|
||||||
|
|
||||||
@@ -147,8 +147,8 @@ describe("PublicAssetManifest", () => {
|
|||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
|
|
||||||
const assetManifest = buildPublicAssetManifest(resourcesDir);
|
const assetManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
createHashedPublicAssetFiles(resourcesDir, outDir, assetManifest);
|
createHashedPublicAssetFiles([resourcesDir], outDir, assetManifest);
|
||||||
|
|
||||||
const emittedManifest = await emitHashedAsset(
|
const emittedManifest = await emitHashedAsset(
|
||||||
outDir,
|
outDir,
|
||||||
@@ -164,7 +164,7 @@ describe("PublicAssetManifest", () => {
|
|||||||
|
|
||||||
await writeWebManifestFixture(resourcesDir, [{ src: "icons/missing.png" }]);
|
await writeWebManifestFixture(resourcesDir, [{ src: "icons/missing.png" }]);
|
||||||
|
|
||||||
expect(() => buildPublicAssetManifest(resourcesDir)).toThrow(
|
expect(() => buildPublicAssetManifest([resourcesDir])).toThrow(
|
||||||
/manifest\.json references icons\/missing\.png/i,
|
/manifest\.json references icons\/missing\.png/i,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -177,8 +177,8 @@ describe("PublicAssetManifest", () => {
|
|||||||
{ src: "data:image/png;base64,AAA" },
|
{ src: "data:image/png;base64,AAA" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const assetManifest = buildPublicAssetManifest(resourcesDir);
|
const assetManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
createHashedPublicAssetFiles(resourcesDir, outDir, assetManifest);
|
createHashedPublicAssetFiles([resourcesDir], outDir, assetManifest);
|
||||||
|
|
||||||
const emittedManifest = await emitHashedAsset(
|
const emittedManifest = await emitHashedAsset(
|
||||||
outDir,
|
outDir,
|
||||||
@@ -198,8 +198,8 @@ describe("PublicAssetManifest", () => {
|
|||||||
"test.png",
|
"test.png",
|
||||||
);
|
);
|
||||||
|
|
||||||
const assetManifest = buildPublicAssetManifest(resourcesDir);
|
const assetManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
createHashedPublicAssetFiles(resourcesDir, outDir, assetManifest);
|
createHashedPublicAssetFiles([resourcesDir], outDir, assetManifest);
|
||||||
|
|
||||||
const xmlHref = assetManifest["fonts/test.xml"];
|
const xmlHref = assetManifest["fonts/test.xml"];
|
||||||
const pngHref = assetManifest["fonts/test.png"];
|
const pngHref = assetManifest["fonts/test.png"];
|
||||||
@@ -220,12 +220,12 @@ describe("PublicAssetManifest", () => {
|
|||||||
"test.png",
|
"test.png",
|
||||||
);
|
);
|
||||||
|
|
||||||
const firstManifest = buildPublicAssetManifest(resourcesDir);
|
const firstManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
|
|
||||||
await fs.writeFile(path.join(resourcesDir, "fonts", "test.png"), "png-v2");
|
await fs.writeFile(path.join(resourcesDir, "fonts", "test.png"), "png-v2");
|
||||||
clearPublicAssetManifestCache();
|
clearPublicAssetManifestCache();
|
||||||
|
|
||||||
const secondManifest = buildPublicAssetManifest(resourcesDir);
|
const secondManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
|
|
||||||
expect(firstManifest["fonts/test.png"]).not.toBe(
|
expect(firstManifest["fonts/test.png"]).not.toBe(
|
||||||
secondManifest["fonts/test.png"],
|
secondManifest["fonts/test.png"],
|
||||||
@@ -250,7 +250,7 @@ describe("PublicAssetManifest", () => {
|
|||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(() => buildPublicAssetManifest(resourcesDir)).toThrow(
|
expect(() => buildPublicAssetManifest([resourcesDir])).toThrow(
|
||||||
/missing from the asset manifest/i,
|
/missing from the asset manifest/i,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -265,8 +265,8 @@ describe("PublicAssetManifest", () => {
|
|||||||
"nested-png",
|
"nested-png",
|
||||||
);
|
);
|
||||||
|
|
||||||
const assetManifest = buildPublicAssetManifest(resourcesDir);
|
const assetManifest = buildPublicAssetManifest([resourcesDir]);
|
||||||
createHashedPublicAssetFiles(resourcesDir, outDir, assetManifest);
|
createHashedPublicAssetFiles([resourcesDir], outDir, assetManifest);
|
||||||
|
|
||||||
const xmlHref = assetManifest["fonts/nested/atlas.xml"];
|
const xmlHref = assetManifest["fonts/nested/atlas.xml"];
|
||||||
const pngHref = assetManifest["fonts/nested/pages/p0.png"];
|
const pngHref = assetManifest["fonts/nested/pages/p0.png"];
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import tailwindcss from "@tailwindcss/vite";
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { defineConfig, loadEnv } from "vite";
|
import { defineConfig, loadEnv, type Plugin } from "vite";
|
||||||
import { createHtmlPlugin } from "vite-plugin-html";
|
import { createHtmlPlugin } from "vite-plugin-html";
|
||||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
|
||||||
import tsconfigPaths from "vite-tsconfig-paths";
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
import { type AssetManifest, buildAssetUrl } from "./src/core/AssetUrls";
|
import { type AssetManifest, buildAssetUrl } from "./src/core/AssetUrls";
|
||||||
import {
|
import {
|
||||||
buildPublicAssetManifest,
|
buildPublicAssetManifest,
|
||||||
copyRootPublicFiles,
|
copyRootPublicFiles,
|
||||||
createHashedPublicAssetFiles,
|
createHashedPublicAssetFiles,
|
||||||
|
getProprietaryDir,
|
||||||
getResourcesDir,
|
getResourcesDir,
|
||||||
writePublicAssetManifestModule,
|
writePublicAssetManifestModule,
|
||||||
} from "./src/server/PublicAssetManifest";
|
} from "./src/server/PublicAssetManifest";
|
||||||
@@ -18,12 +19,43 @@ import {
|
|||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
function serveProprietaryDir(dir: string): Plugin {
|
||||||
|
const resolvedDir = path.resolve(dir) + path.sep;
|
||||||
|
return {
|
||||||
|
name: "serve-proprietary-dir",
|
||||||
|
configureServer(server) {
|
||||||
|
// Return a function so the middleware is registered after Vite's internal
|
||||||
|
// static-file handler (publicDir). This makes proprietary/ a fallback
|
||||||
|
// rather than taking precedence over resources/.
|
||||||
|
return () => {
|
||||||
|
server.middlewares.use((req, res, next) => {
|
||||||
|
if (!req.url) return next();
|
||||||
|
const urlPath = new URL(req.url, "http://localhost").pathname;
|
||||||
|
const filePath = path.resolve(
|
||||||
|
dir,
|
||||||
|
decodeURIComponent(urlPath).replace(/^\//, ""),
|
||||||
|
);
|
||||||
|
if (!filePath.startsWith(resolvedDir)) return next();
|
||||||
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
||||||
|
res.setHeader("Cache-Control", "no-cache");
|
||||||
|
fs.createReadStream(filePath).pipe(res);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const env = loadEnv(mode, process.cwd(), "");
|
const env = loadEnv(mode, process.cwd(), "");
|
||||||
const isProduction = mode === "production";
|
const isProduction = mode === "production";
|
||||||
const resourcesDir = getResourcesDir(__dirname);
|
const resourcesDir = getResourcesDir(__dirname);
|
||||||
|
const proprietaryDir = getProprietaryDir(__dirname);
|
||||||
|
const sourceDirs = [resourcesDir, proprietaryDir];
|
||||||
const assetManifest: AssetManifest = isProduction
|
const assetManifest: AssetManifest = isProduction
|
||||||
? buildPublicAssetManifest(resourcesDir)
|
? buildPublicAssetManifest(sourceDirs)
|
||||||
: {};
|
: {};
|
||||||
const htmlAssetData = {
|
const htmlAssetData = {
|
||||||
assetManifest: JSON.stringify(assetManifest),
|
assetManifest: JSON.stringify(assetManifest),
|
||||||
@@ -45,7 +77,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
closeBundle() {
|
closeBundle() {
|
||||||
const outDir = path.join(__dirname, "static");
|
const outDir = path.join(__dirname, "static");
|
||||||
copyRootPublicFiles(resourcesDir, outDir);
|
copyRootPublicFiles(resourcesDir, outDir);
|
||||||
createHashedPublicAssetFiles(resourcesDir, outDir, assetManifest);
|
createHashedPublicAssetFiles(sourceDirs, outDir, assetManifest);
|
||||||
writePublicAssetManifestModule(outDir, assetManifest);
|
writePublicAssetManifestModule(outDir, assetManifest);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -91,6 +123,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
|
...(!isProduction ? [serveProprietaryDir(proprietaryDir)] : []),
|
||||||
...(isProduction
|
...(isProduction
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
@@ -106,14 +139,6 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
viteStaticCopy({
|
|
||||||
targets: [
|
|
||||||
{
|
|
||||||
src: "proprietary/*",
|
|
||||||
dest: ".",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
...(isProduction ? [syncHashedPublicAssets()] : []),
|
...(isProduction ? [syncHashedPublicAssets()] : []),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
],
|
],
|
||||||
|
|||||||