Files
cinny/vite.config.js
T
jared d7d7b59866
CI / Build & Quality Checks (push) Successful in 10m33s
Trigger Desktop Build / trigger (push) Successful in 5s
fix: add public/fonts to viteStaticCopy targets
public/fonts/ (custom-fonts.css + woff2 files) was never listed in the
static copy plugin config, so it was absent from every dist build output.
The site served HTML instead of CSS for /fonts/custom-fonts.css, causing
JetBrains Mono and Fira Code to 404. Adding the target copies the directory
to dist/fonts/ where nginx serves it correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 14:17:36 -04:00

191 lines
5.7 KiB
JavaScript

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { sentryVitePlugin } from '@sentry/vite-plugin';
import { wasm } from '@rollup/plugin-wasm';
import inject from '@rollup/plugin-inject';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { VitePWA } from 'vite-plugin-pwa';
import fs from 'fs';
import path from 'path';
import buildConfig from './build.config';
const copyFiles = {
targets: [
{
// Element Call's dist must land flat in public/element-call/ so the call
// widget URL (/public/element-call/index.html) resolves. v4.x of
// vite-plugin-static-copy preserves the full source path under dest, so
// we strip the 4 leading segments of the source base
// (node_modules/@element-hq/element-call-embedded/dist) — mirroring the
// stripBase pattern used by the android/locales targets below. The old
// `rename: 'element-call'` form silently produced
// public/node_modules/.../dist/ under v4.x, 404ing the widget (calls
// broke on cinny-desktop; web only worked because its deployed copy was
// a stale artifact from before the vite-plugin-static-copy v4 bump).
src: 'node_modules/@element-hq/element-call-embedded/dist',
dest: 'public/element-call',
rename: { stripBase: 4 },
},
{
src: 'config.json',
dest: '',
},
{
src: 'public/manifest.json',
dest: '',
rename: { stripBase: true },
},
{
src: 'public/res/android',
dest: 'public/',
rename: { stripBase: 2 },
},
{
src: 'public/locales',
dest: 'public/',
rename: { stripBase: 1 },
},
{
src: 'public/fonts',
dest: '',
rename: { stripBase: 1 },
},
],
};
function copyPdfWorker() {
return {
name: 'copy-pdf-worker',
closeBundle() {
const src = path.resolve('node_modules/pdfjs-dist/build/pdf.worker.min.mjs');
const dest = path.resolve('dist/pdf.worker.min.js');
if (fs.existsSync(src)) fs.copyFileSync(src, dest);
},
};
}
function serverMatrixSdkCryptoWasm(wasmFilePath) {
return {
name: 'vite-plugin-serve-matrix-sdk-crypto-wasm',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url === wasmFilePath) {
const resolvedPath = path.join(
path.resolve(),
'/node_modules/@matrix-org/matrix-sdk-crypto-wasm/pkg/matrix_sdk_crypto_wasm_bg.wasm',
);
if (fs.existsSync(resolvedPath)) {
res.setHeader('Content-Type', 'application/wasm');
res.setHeader('Cache-Control', 'no-cache');
const fileStream = fs.createReadStream(resolvedPath);
fileStream.pipe(res);
} else {
res.writeHead(404);
res.end('File not found');
}
} else {
next();
}
});
},
};
}
const vendorChunks = (id) => {
if (id.includes('node_modules/matrix-js-sdk')) return 'matrix-sdk';
if (id.includes('node_modules/react-dom')) return 'react-dom';
if (
id.includes('node_modules/react-router-dom') ||
id.includes('node_modules/@remix-run') ||
id.includes('node_modules/react-router/')
)
return 'router';
if (id.includes('node_modules/@tanstack')) return 'react-query';
if (id.includes('node_modules/linkify')) return 'linkify';
if (id.includes('node_modules/dompurify')) return 'dompurify';
if (id.includes('node_modules/@sentry')) return 'sentry';
if (id.includes('node_modules/i18next') || id.includes('node_modules/react-i18next'))
return 'i18n';
if (id.includes('node_modules/jotai')) return 'jotai';
if (id.includes('node_modules/immer')) return 'immer';
if (id.includes('node_modules/folds')) return 'folds';
if (id.includes('node_modules/emojibase')) return 'emojibase';
};
export default defineConfig({
appType: 'spa',
publicDir: false,
base: buildConfig.base,
server: {
port: 8080,
host: true,
fs: {
allow: ['..'],
},
},
plugins: [
serverMatrixSdkCryptoWasm('/node_modules/.vite/deps/pkg/matrix_sdk_crypto_wasm_bg.wasm'),
viteStaticCopy(copyFiles),
vanillaExtractPlugin(),
wasm(),
react(),
copyPdfWorker(),
...(process.env.SENTRY_AUTH_TOKEN
? [
sentryVitePlugin({
org: 'lotus-guild',
project: 'javascript-react',
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: {
filesToDeleteAfterUpload: ['./dist/**/*.map'],
},
release: { name: process.env.VITE_APP_VERSION ?? 'lotus' },
telemetry: false,
}),
]
: []),
VitePWA({
srcDir: 'src',
filename: 'sw.ts',
strategies: 'injectManifest',
injectRegister: false,
manifest: false,
injectManifest: {
injectionPoint: undefined,
// codeSplitting: false is not yet supported by vite-plugin-pwa 1.3.0;
// the inlineDynamicImports deprecation warning from Vite is from pwa internal build
},
devOptions: {
enabled: true,
type: 'module',
},
}),
],
optimizeDeps: {
rolldownOptions: {
define: {
global: 'globalThis',
},
},
},
build: {
target: 'esnext',
outDir: 'dist',
sourcemap: process.env.SENTRY_AUTH_TOKEN ? 'hidden' : false,
copyPublicDir: false,
// manualChunks must be in rolldownOptions (not rollupOptions) for Vite 8 / Rolldown
rolldownOptions: {
checks: { preferBuiltinFeature: false },
output: {
manualChunks: vendorChunks,
},
},
rollupOptions: {
plugins: [inject({ Buffer: ['buffer', 'Buffer'] })],
},
},
});