🔧 refactor: Build Process and Static Asset Handling (#7605)

* 🔧 chore: Update build script to include post-build image removal

* refactor: staticCache middleware with options and special handling for manifest/sw/index files

* refactor(pwa): optimize service worker caching strategy

* refactor: streamline post-build process and update public directory handling

* chore: remove external images from rollupOptions in Vite config

* chore: enhance logging message in post-build script for clarity
This commit is contained in:
Danny Avila 2025-05-28 09:27:12 -04:00
parent 2f462c9b3c
commit f556aaeaea
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
4 changed files with 64 additions and 13 deletions

View file

@ -1,3 +1,4 @@
const path = require('path');
const expressStaticGzip = require('express-static-gzip'); const expressStaticGzip = require('express-static-gzip');
const oneDayInSeconds = 24 * 60 * 60; const oneDayInSeconds = 24 * 60 * 60;
@ -5,16 +6,45 @@ const oneDayInSeconds = 24 * 60 * 60;
const sMaxAge = process.env.STATIC_CACHE_S_MAX_AGE || oneDayInSeconds; const sMaxAge = process.env.STATIC_CACHE_S_MAX_AGE || oneDayInSeconds;
const maxAge = process.env.STATIC_CACHE_MAX_AGE || oneDayInSeconds * 2; const maxAge = process.env.STATIC_CACHE_MAX_AGE || oneDayInSeconds * 2;
const staticCache = (staticPath) => /**
expressStaticGzip(staticPath, { * Creates an Express static middleware with gzip compression and configurable caching
enableBrotli: false, // disable Brotli, only using gzip *
* @param {string} staticPath - The file system path to serve static files from
* @param {Object} [options={}] - Configuration options
* @param {boolean} [options.noCache=false] - If true, disables caching entirely for all files
* @returns {ReturnType<expressStaticGzip>} Express middleware function for serving static files
*/
function staticCache(staticPath, options = {}) {
const { noCache = false } = options;
return expressStaticGzip(staticPath, {
enableBrotli: false,
orderPreference: ['gz'], orderPreference: ['gz'],
setHeaders: (res, _path) => { setHeaders: (res, filePath) => {
if (process.env.NODE_ENV?.toLowerCase() === 'production') { if (process.env.NODE_ENV?.toLowerCase() !== 'production') {
return;
}
if (noCache) {
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
return;
}
if (filePath.includes('/dist/images/')) {
return;
}
const fileName = path.basename(filePath);
if (
fileName === 'index.html' ||
fileName.endsWith('.webmanifest') ||
fileName === 'manifest.json' ||
fileName === 'sw.js'
) {
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
} else {
res.setHeader('Cache-Control', `public, max-age=${maxAge}, s-maxage=${sMaxAge}`); res.setHeader('Cache-Control', `public, max-age=${maxAge}, s-maxage=${sMaxAge}`);
} }
}, },
index: false, index: false,
}); });
}
module.exports = staticCache; module.exports = staticCache;

View file

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"data-provider": "cd .. && npm run build:data-provider", "data-provider": "cd .. && npm run build:data-provider",
"build:file": "cross-env NODE_ENV=production vite build --debug > vite-output.log 2>&1", "build:file": "cross-env NODE_ENV=production vite build --debug > vite-output.log 2>&1",
"build": "cross-env NODE_ENV=production vite build", "build": "cross-env NODE_ENV=production vite build && node ./scripts/post-build.cjs",
"build:ci": "cross-env NODE_ENV=development vite build --mode ci", "build:ci": "cross-env NODE_ENV=development vite build --mode ci",
"dev": "cross-env NODE_ENV=development vite", "dev": "cross-env NODE_ENV=development vite",
"preview-prod": "cross-env NODE_ENV=development vite preview", "preview-prod": "cross-env NODE_ENV=development vite preview",

View file

@ -0,0 +1,14 @@
const fs = require('fs-extra');
async function postBuild() {
try {
await fs.copy('public/assets', 'dist/assets');
await fs.copy('public/robots.txt', 'dist/robots.txt');
console.log('✅ PWA icons and robots.txt copied successfully. Glob pattern warnings resolved.');
} catch (err) {
console.error('❌ Error copying files:', err);
process.exit(1);
}
}
postBuild();

View file

@ -8,7 +8,7 @@ import { nodePolyfills } from 'vite-plugin-node-polyfills';
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig(({ command }) => ({
server: { server: {
host: 'localhost', host: 'localhost',
port: 3090, port: 3090,
@ -37,13 +37,21 @@ export default defineConfig({
enabled: false, // disable service worker registration in development mode enabled: false, // disable service worker registration in development mode
}, },
useCredentials: true, useCredentials: true,
includeManifestIcons: false,
workbox: { workbox: {
globPatterns: ['**/*'], globPatterns: [
'**/*.{js,css,html}',
'assets/favicon*.png',
'assets/icon-*.png',
'assets/apple-touch-icon*.png',
'assets/maskable-icon.png',
'manifest.webmanifest',
],
globIgnores: ['images/**/*', '**/*.map'], globIgnores: ['images/**/*', '**/*.map'],
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, maximumFileSizeToCacheInBytes: 4 * 1024 * 1024,
navigateFallbackDenylist: [/^\/oauth/], navigateFallbackDenylist: [/^\/oauth/],
}, },
includeAssets: ['**/*'], includeAssets: [],
manifest: { manifest: {
name: 'LibreChat', name: 'LibreChat',
short_name: 'LibreChat', short_name: 'LibreChat',
@ -94,14 +102,13 @@ export default defineConfig({
template: 'treemap', // 'treemap' | 'sunburst' | 'network' template: 'treemap', // 'treemap' | 'sunburst' | 'network'
}), }),
].filter(Boolean), ].filter(Boolean),
publicDir: './public', publicDir: command === 'serve' ? './public' : false,
build: { build: {
sourcemap: process.env.NODE_ENV === 'development', sourcemap: process.env.NODE_ENV === 'development',
outDir: './dist', outDir: './dist',
minify: 'terser', minify: 'terser',
rollupOptions: { rollupOptions: {
preserveEntrySignatures: 'strict', preserveEntrySignatures: 'strict',
// external: ['uuid'],
output: { output: {
manualChunks(id: string) { manualChunks(id: string) {
if (id.includes('node_modules')) { if (id.includes('node_modules')) {
@ -230,10 +237,10 @@ export default defineConfig({
resolve: { resolve: {
alias: { alias: {
'~': path.join(__dirname, 'src/'), '~': path.join(__dirname, 'src/'),
$fonts: '/fonts', $fonts: path.resolve(__dirname, 'public/fonts'),
}, },
}, },
}); }));
interface SourcemapExclude { interface SourcemapExclude {
excludeNodeModules?: boolean; excludeNodeModules?: boolean;