mirror of
https://github.com/unraid/api.git
synced 2026-01-04 15:39:52 -06:00
- modified vite.config.ts to integrate app configuration into UI setup - updated app.config.ts to include new button, tabs, and slideover variants for better theming - cleaned up main.css by removing unused styles and ensuring proper imports - refactored notification components to streamline structure and improve readability
203 lines
5.6 KiB
TypeScript
203 lines
5.6 KiB
TypeScript
import path from 'node:path';
|
|
import { fileURLToPath, URL } from 'node:url';
|
|
|
|
import ui from '@nuxt/ui/vite';
|
|
|
|
import tailwindcss from '@tailwindcss/vite';
|
|
import vue from '@vitejs/plugin-vue';
|
|
import { defineConfig } from 'vite';
|
|
import removeConsole from 'vite-plugin-remove-console';
|
|
|
|
import appConfig from './app.config';
|
|
import scopeTailwindToUnapi from './postcss/scopeTailwindToUnapi';
|
|
import { serveStaticHtml } from './vite-plugin-serve-static';
|
|
|
|
const dropConsole = process.env.VITE_ALLOW_CONSOLE_LOGS !== 'true';
|
|
console.log(dropConsole ? 'WARN: Console logs are disabled' : 'INFO: Console logs are enabled');
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const assetsDir = path.join(__dirname, '../api/dev/webGui/');
|
|
|
|
/**
|
|
* Used to avoid redeclaring variables in the webgui codebase.
|
|
*/
|
|
function terserReservations(inputStr: string) {
|
|
const combinations = ['ace'];
|
|
|
|
// Add 1-character combinations
|
|
for (let i = 0; i < inputStr.length; i++) {
|
|
combinations.push(inputStr[i]);
|
|
}
|
|
|
|
// Add 2-character combinations
|
|
for (let i = 0; i < inputStr.length; i++) {
|
|
for (let j = 0; j < inputStr.length; j++) {
|
|
combinations.push(inputStr[i] + inputStr[j]);
|
|
}
|
|
}
|
|
|
|
return combinations;
|
|
}
|
|
|
|
const charsToReserve = '_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
|
|
/**
|
|
* Shared terser options for consistent minification
|
|
*/
|
|
const sharedTerserOptions = {
|
|
mangle: {
|
|
reserved: terserReservations(charsToReserve),
|
|
toplevel: true,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Shared define configuration
|
|
*/
|
|
const sharedDefine = {
|
|
'globalThis.__DEV__': JSON.stringify(process.env.NODE_ENV === 'development'),
|
|
__VUE_PROD_DEVTOOLS__: JSON.stringify(false),
|
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
|
|
'process.env': JSON.stringify({}),
|
|
};
|
|
|
|
// https://vitejs.dev/config/
|
|
export default defineConfig({
|
|
plugins: [
|
|
tailwindcss(),
|
|
vue({
|
|
template: {
|
|
compilerOptions: {
|
|
// Treat all unraid-* components as custom elements
|
|
isCustomElement: (tag) => tag.startsWith('unraid-'),
|
|
},
|
|
},
|
|
}),
|
|
ui({
|
|
ui: appConfig.ui,
|
|
}),
|
|
serveStaticHtml(), // Serve static test pages
|
|
// Remove console logs in production
|
|
...(dropConsole
|
|
? [
|
|
removeConsole({
|
|
includes: ['log', 'info', 'debug'],
|
|
}),
|
|
]
|
|
: []),
|
|
],
|
|
|
|
css: {
|
|
postcss: {
|
|
plugins: [scopeTailwindToUnapi()],
|
|
},
|
|
},
|
|
|
|
resolve: {
|
|
alias: {
|
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
'~': fileURLToPath(new URL('./src', import.meta.url)),
|
|
'~~': fileURLToPath(new URL('./', import.meta.url)),
|
|
'~/': fileURLToPath(new URL('./src/', import.meta.url)),
|
|
},
|
|
},
|
|
|
|
optimizeDeps: {
|
|
include: ['ajv', 'ajv-errors'],
|
|
esbuildOptions: {
|
|
define: {
|
|
global: 'globalThis',
|
|
},
|
|
},
|
|
},
|
|
|
|
define: {
|
|
...sharedDefine,
|
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
|
|
},
|
|
|
|
publicDir: false, // Don't copy public files to dist
|
|
|
|
build: {
|
|
outDir: 'dist',
|
|
emptyOutDir: true,
|
|
manifest: false, // Disable Vite's manifest since we generate our own
|
|
lib: {
|
|
entry: fileURLToPath(new URL('./src/components/Wrapper/auto-mount.ts', import.meta.url)),
|
|
name: 'UnraidStandaloneApps',
|
|
fileName: 'standalone-apps',
|
|
formats: ['es'],
|
|
},
|
|
rollupOptions: {
|
|
output: {
|
|
format: 'es',
|
|
entryFileNames: 'standalone-apps-[hash].js',
|
|
chunkFileNames: '[name]-[hash].js',
|
|
assetFileNames: (assetInfo) => {
|
|
if (assetInfo.name?.endsWith('.css')) {
|
|
return 'standalone-apps-[hash][extname]';
|
|
}
|
|
return '[name]-[hash][extname]';
|
|
},
|
|
inlineDynamicImports: false,
|
|
},
|
|
},
|
|
cssCodeSplit: false, // Bundle all CSS together
|
|
minify: 'terser',
|
|
terserOptions: sharedTerserOptions,
|
|
},
|
|
|
|
server: {
|
|
port: 3000,
|
|
proxy: {
|
|
'/graphql': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
ws: true,
|
|
secure: false,
|
|
// Important: preserve the host header
|
|
configure: (proxy) => {
|
|
proxy.on('proxyReq', (proxyReq) => {
|
|
proxyReq.setHeader('X-Forwarded-Host', 'localhost:3000');
|
|
proxyReq.setHeader('X-Forwarded-Proto', 'http');
|
|
proxyReq.setHeader('X-Forwarded-For', '127.0.0.1');
|
|
});
|
|
// Handle connection errors gracefully
|
|
proxy.on('error', (err: Error, _req: unknown, res: unknown) => {
|
|
console.warn('[Vite] GraphQL proxy error (API server may not be running):', err.message);
|
|
// Check if res has writeHead method (it's an HTTP response, not a socket)
|
|
const httpRes = res as {
|
|
writeHead?: (statusCode: number, headers: Record<string, string>) => void;
|
|
end?: (data: string) => void;
|
|
};
|
|
if (
|
|
httpRes &&
|
|
typeof httpRes.writeHead === 'function' &&
|
|
typeof httpRes.end === 'function'
|
|
) {
|
|
httpRes.writeHead(503, {
|
|
'Content-Type': 'application/json',
|
|
});
|
|
httpRes.end(
|
|
JSON.stringify({
|
|
error: 'GraphQL API server not available',
|
|
message: 'Please start the API server on port 3001',
|
|
})
|
|
);
|
|
}
|
|
});
|
|
},
|
|
},
|
|
'/webGui': {
|
|
target: `file://${assetsDir}`,
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
// Configure static file serving
|
|
fs: {
|
|
strict: false,
|
|
allow: ['..'], // Allow serving files outside of root
|
|
},
|
|
},
|
|
});
|