mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
1 Commits
release
...
vite-build
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64d4c0c6be |
@@ -1,12 +0,0 @@
|
||||
const { createConfig, build, watch } = require('Common/UI/esbuild-config');
|
||||
|
||||
const config = createConfig({
|
||||
serviceName: 'Accounts',
|
||||
publicPath: '/accounts/dist/',
|
||||
});
|
||||
|
||||
if (process.argv.includes('--watch')) {
|
||||
watch(config, 'Accounts');
|
||||
} else {
|
||||
build(config, 'Accounts');
|
||||
}
|
||||
@@ -7,10 +7,10 @@
|
||||
"url": "https://github.com/OneUptime/oneuptime"
|
||||
},
|
||||
"scripts": {
|
||||
"dev-build": "NODE_ENV=development node esbuild.config.js",
|
||||
"dev-build": "NODE_ENV=development npx vite build",
|
||||
"dev": "npx nodemon",
|
||||
"build": "NODE_ENV=production node esbuild.config.js",
|
||||
"analyze": "analyze=true NODE_ENV=production node esbuild.config.js",
|
||||
"build": "NODE_ENV=production npx vite build",
|
||||
"analyze": "analyze=true NODE_ENV=production npx vite build",
|
||||
"test": "",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
|
||||
6
Accounts/vite.config.ts
Normal file
6
Accounts/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createViteConfig } from 'Common/UI/vite.config';
|
||||
|
||||
export default createViteConfig({
|
||||
serviceName: 'Accounts',
|
||||
publicPath: '/accounts/dist/',
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
const { createConfig, build, watch } = require('Common/UI/esbuild-config');
|
||||
|
||||
const config = createConfig({
|
||||
serviceName: 'AdminDashboard',
|
||||
publicPath: '/admin/dist/',
|
||||
});
|
||||
|
||||
if (process.argv.includes('--watch')) {
|
||||
watch(config, 'AdminDashboard');
|
||||
} else {
|
||||
build(config, 'AdminDashboard');
|
||||
}
|
||||
@@ -15,12 +15,11 @@
|
||||
"react-router-dom": "^6.30.2"
|
||||
},
|
||||
"scripts": {
|
||||
"dev-build": "NODE_ENV=development node esbuild.config.js",
|
||||
"dev-build": "NODE_ENV=development npx vite build",
|
||||
"dev": "npx nodemon",
|
||||
"build": "NODE_ENV=production node esbuild.config.js",
|
||||
"analyze": "analyze=true NODE_ENV=production node esbuild.config.js",
|
||||
"build": "NODE_ENV=production npx vite build",
|
||||
"analyze": "analyze=true NODE_ENV=production npx vite build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "echo 'esbuild does not require eject'",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
"start": "node --require ts-node/register Serve.ts",
|
||||
|
||||
6
AdminDashboard/vite.config.ts
Normal file
6
AdminDashboard/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createViteConfig } from 'Common/UI/vite.config';
|
||||
|
||||
export default createViteConfig({
|
||||
serviceName: 'AdminDashboard',
|
||||
publicPath: '/admin/dist/',
|
||||
});
|
||||
@@ -1,245 +0,0 @@
|
||||
/**
|
||||
* Shared esbuild configuration factory for OneUptime frontend services
|
||||
* This creates consistent build configurations across all services
|
||||
*/
|
||||
|
||||
const esbuild = require('esbuild');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
function createRefractorCompatibilityPlugin() {
|
||||
const candidateRoots = [
|
||||
path.resolve(__dirname, '../node_modules/refractor'),
|
||||
path.resolve(__dirname, '../../node_modules/refractor'),
|
||||
];
|
||||
|
||||
const refractorRoot = candidateRoots.find((packagePath) => fs.existsSync(packagePath));
|
||||
|
||||
if (!refractorRoot) {
|
||||
throw new Error('Unable to locate refractor package for esbuild compatibility plugin.');
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'refractor-compatibility',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^refractor\/lib\// }, (args) => {
|
||||
const relativePath = args.path.replace(/^refractor\/lib\//, '');
|
||||
const candidatePath = path.join(refractorRoot, 'lib', `${relativePath}.js`);
|
||||
return { path: candidatePath };
|
||||
});
|
||||
|
||||
build.onResolve({ filter: /^refractor\/lang\// }, (args) => {
|
||||
const relativePath = args.path.replace(/^refractor\/lang\//, '');
|
||||
const filename = relativePath.endsWith('.js') ? relativePath : `${relativePath}.js`;
|
||||
const candidatePath = path.join(refractorRoot, 'lang', filename);
|
||||
return { path: candidatePath };
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// CSS Plugin to handle CSS/SCSS files
|
||||
function createCSSPlugin() {
|
||||
return {
|
||||
name: 'css',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.s?css$/ }, async (args) => {
|
||||
const sass = require('sass');
|
||||
const fs = require('fs');
|
||||
|
||||
let contents = fs.readFileSync(args.path, 'utf8');
|
||||
|
||||
// Compile SCSS to CSS if it's a SCSS file
|
||||
if (args.path.endsWith('.scss') || args.path.endsWith('.sass')) {
|
||||
try {
|
||||
const result = sass.compile(args.path);
|
||||
contents = result.css;
|
||||
} catch (error) {
|
||||
console.error(`SCSS compilation error in ${args.path}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Return CSS as a string that will be injected into the page
|
||||
return {
|
||||
contents: `
|
||||
const style = document.createElement('style');
|
||||
style.textContent = ${JSON.stringify(contents)};
|
||||
document.head.appendChild(style);
|
||||
`,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// File loader plugin for assets
|
||||
function createFileLoaderPlugin() {
|
||||
return {
|
||||
name: 'file-loader',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.(png|jpe?g|gif|svg|woff|woff2|eot|ttf|otf)$/ }, async (args) => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const contents = fs.readFileSync(args.path);
|
||||
const filename = path.basename(args.path);
|
||||
const ext = path.extname(filename);
|
||||
|
||||
// For development, we'll use data URLs for simplicity
|
||||
// In production, you might want to copy files to the output directory
|
||||
const mimeTypes = {
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.woff': 'font/woff',
|
||||
'.woff2': 'font/woff2',
|
||||
'.eot': 'application/vnd.ms-fontobject',
|
||||
'.ttf': 'font/ttf',
|
||||
'.otf': 'font/otf',
|
||||
};
|
||||
|
||||
const mimeType = mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
|
||||
const dataUrl = `data:${mimeType};base64,${contents.toString('base64')}`;
|
||||
|
||||
return {
|
||||
contents: `export default ${JSON.stringify(dataUrl)};`,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Read environment variables from .env file
|
||||
function readEnvFile(pathToFile) {
|
||||
if (!fs.existsSync(pathToFile)) {
|
||||
console.warn(`Environment file not found: ${pathToFile}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed || {};
|
||||
const env = {};
|
||||
|
||||
for (const key in parsed) {
|
||||
env[`process.env.${key}`] = JSON.stringify(parsed[key]);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create esbuild configuration for a service
|
||||
* @param {Object} options - Configuration options
|
||||
* @param {string} options.serviceName - Name of the service (dashboard, accounts, admin, status-page)
|
||||
* @param {string} options.publicPath - Public path for assets
|
||||
* @param {string} [options.entryPoint] - Entry point file (defaults to './src/Index.tsx')
|
||||
* @param {string} [options.outdir] - Output directory (defaults to './public/dist')
|
||||
* @param {Object} [options.additionalDefines] - Additional define variables
|
||||
* @param {Array} [options.additionalExternal] - Additional external modules
|
||||
* @param {Object} [options.additionalAlias] - Additional aliases
|
||||
*/
|
||||
function createConfig(options) {
|
||||
const {
|
||||
serviceName,
|
||||
publicPath,
|
||||
entryPoint = './src/Index.tsx',
|
||||
outdir = './public/dist',
|
||||
additionalDefines = {},
|
||||
additionalExternal = [],
|
||||
additionalAlias = {}
|
||||
} = options;
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production';
|
||||
const isAnalyze = process.env.analyze === 'true';
|
||||
|
||||
return {
|
||||
entryPoints: [entryPoint],
|
||||
bundle: true,
|
||||
outdir,
|
||||
format: 'esm', // Changed from 'iife' to 'esm' to support splitting
|
||||
platform: 'browser',
|
||||
target: 'es2017',
|
||||
sourcemap: isDev ? 'inline' : false,
|
||||
minify: false,
|
||||
splitting: true, // Now supported with ESM format
|
||||
publicPath,
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
||||
...additionalDefines,
|
||||
},
|
||||
external: ['react-native-sqlite-storage', ...additionalExternal],
|
||||
alias: {
|
||||
'react': path.resolve('./node_modules/react'),
|
||||
...additionalAlias,
|
||||
},
|
||||
plugins: [createRefractorCompatibilityPlugin(), createCSSPlugin(), createFileLoaderPlugin()],
|
||||
loader: {
|
||||
'.tsx': 'tsx',
|
||||
'.ts': 'ts',
|
||||
'.jsx': 'jsx',
|
||||
'.js': 'js',
|
||||
'.json': 'json',
|
||||
},
|
||||
resolveExtensions: ['.tsx', '.ts', '.jsx', '.js', '.json', '.css', '.scss'],
|
||||
metafile: isAnalyze,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build function that handles the build process
|
||||
* @param {Object} config - esbuild configuration
|
||||
* @param {string} serviceName - Name of the service for logging
|
||||
*/
|
||||
async function build(config, serviceName) {
|
||||
const isAnalyze = process.env.analyze === 'true';
|
||||
|
||||
try {
|
||||
const result = await esbuild.build(config);
|
||||
|
||||
if (isAnalyze && result.metafile) {
|
||||
const analyzeText = await esbuild.analyzeMetafile(result.metafile);
|
||||
console.log(`\n📊 Bundle analysis for ${serviceName}:`);
|
||||
console.log(analyzeText);
|
||||
|
||||
// Write metafile for external analysis tools
|
||||
const metafilePath = path.join(config.outdir, 'metafile.json');
|
||||
fs.writeFileSync(metafilePath, JSON.stringify(result.metafile, null, 2));
|
||||
console.log(`📝 Metafile written to: ${metafilePath}`);
|
||||
}
|
||||
|
||||
console.log(`✅ ${serviceName} build completed successfully`);
|
||||
} catch (error) {
|
||||
console.error(`❌ ${serviceName} build failed:`, error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch function that handles the watch process
|
||||
* @param {Object} config - esbuild configuration
|
||||
* @param {string} serviceName - Name of the service for logging
|
||||
*/
|
||||
async function watch(config, serviceName) {
|
||||
try {
|
||||
const context = await esbuild.context(config);
|
||||
await context.watch();
|
||||
console.log(`👀 Watching ${serviceName} for changes...`);
|
||||
} catch (error) {
|
||||
console.error(`❌ ${serviceName} watch failed:`, error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createConfig,
|
||||
build,
|
||||
watch,
|
||||
createCSSPlugin,
|
||||
createFileLoaderPlugin,
|
||||
readEnvFile,
|
||||
};
|
||||
184
Common/UI/vite.config.ts
Normal file
184
Common/UI/vite.config.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Shared Vite configuration factory for OneUptime frontend services
|
||||
* This creates consistent build configurations across all services with controlled code splitting
|
||||
*/
|
||||
|
||||
import { defineConfig, UserConfig, Plugin } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
interface ServiceConfig {
|
||||
serviceName: string;
|
||||
publicPath: string;
|
||||
outDir?: string;
|
||||
entryPoint?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Vite plugin for refractor compatibility
|
||||
* Handles resolving refractor/lib/* and refractor/lang/* imports
|
||||
*/
|
||||
function createRefractorCompatibilityPlugin(): Plugin {
|
||||
const candidateRoots = [
|
||||
path.resolve(__dirname, '../node_modules/refractor'),
|
||||
path.resolve(__dirname, '../../node_modules/refractor'),
|
||||
];
|
||||
|
||||
const refractorRoot = candidateRoots.find((packagePath) => fs.existsSync(packagePath));
|
||||
|
||||
return {
|
||||
name: 'refractor-compatibility',
|
||||
resolveId(source: string) {
|
||||
if (!refractorRoot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (source.startsWith('refractor/lib/')) {
|
||||
const relativePath = source.replace(/^refractor\/lib\//, '');
|
||||
const candidatePath = path.join(refractorRoot, 'lib', `${relativePath}.js`);
|
||||
if (fs.existsSync(candidatePath)) {
|
||||
return candidatePath;
|
||||
}
|
||||
}
|
||||
|
||||
if (source.startsWith('refractor/lang/')) {
|
||||
const relativePath = source.replace(/^refractor\/lang\//, '');
|
||||
const filename = relativePath.endsWith('.js') ? relativePath : `${relativePath}.js`;
|
||||
const candidatePath = path.join(refractorRoot, 'lang', filename);
|
||||
if (fs.existsSync(candidatePath)) {
|
||||
return candidatePath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Vite configuration for a service
|
||||
*/
|
||||
export function createViteConfig(config: ServiceConfig): UserConfig {
|
||||
const {
|
||||
serviceName,
|
||||
publicPath,
|
||||
outDir = './public/dist',
|
||||
entryPoint = './src/Index.tsx'
|
||||
} = config;
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production';
|
||||
const isAnalyze = process.env.analyze === 'true';
|
||||
|
||||
return defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
createRefractorCompatibilityPlugin(),
|
||||
],
|
||||
base: publicPath,
|
||||
build: {
|
||||
target: 'es2017',
|
||||
outDir,
|
||||
emptyOutDir: true,
|
||||
sourcemap: isDev ? 'inline' : false,
|
||||
minify: !isDev,
|
||||
rollupOptions: {
|
||||
input: entryPoint,
|
||||
output: {
|
||||
entryFileNames: 'Index.js',
|
||||
chunkFileNames: '[name].js',
|
||||
assetFileNames: '[name][extname]',
|
||||
manualChunks: (id: string) => {
|
||||
// Vendor chunk: React ecosystem
|
||||
if (id.includes('node_modules/react/') ||
|
||||
id.includes('node_modules/react-dom/') ||
|
||||
id.includes('node_modules/react-router-dom/') ||
|
||||
id.includes('node_modules/react-router/') ||
|
||||
id.includes('node_modules/@remix-run/router/')) {
|
||||
return 'vendor';
|
||||
}
|
||||
|
||||
// UI Components chunk: Common UI library
|
||||
if (id.includes('Common/UI/Components/') ||
|
||||
id.includes('Common/UI/Utils/')) {
|
||||
return 'ui';
|
||||
}
|
||||
|
||||
// Charting libraries chunk
|
||||
if (id.includes('node_modules/recharts/') ||
|
||||
id.includes('node_modules/d3-') ||
|
||||
id.includes('node_modules/victory-')) {
|
||||
return 'charts';
|
||||
}
|
||||
|
||||
// Monaco editor chunk (large)
|
||||
if (id.includes('node_modules/@monaco-editor/') ||
|
||||
id.includes('node_modules/monaco-editor/')) {
|
||||
return 'monaco';
|
||||
}
|
||||
|
||||
// Flow/diagram libraries
|
||||
if (id.includes('node_modules/reactflow/') ||
|
||||
id.includes('node_modules/@reactflow/') ||
|
||||
id.includes('node_modules/elkjs/')) {
|
||||
return 'flow';
|
||||
}
|
||||
|
||||
// Syntax highlighting chunk
|
||||
if (id.includes('node_modules/react-syntax-highlighter/') ||
|
||||
id.includes('node_modules/refractor/') ||
|
||||
id.includes('node_modules/prismjs/') ||
|
||||
id.includes('node_modules/highlight.js/')) {
|
||||
return 'syntax';
|
||||
}
|
||||
|
||||
// Date/time utilities
|
||||
if (id.includes('node_modules/moment/') ||
|
||||
id.includes('node_modules/moment-timezone/')) {
|
||||
return 'datetime';
|
||||
}
|
||||
|
||||
// Markdown processing
|
||||
if (id.includes('node_modules/react-markdown/') ||
|
||||
id.includes('node_modules/remark-') ||
|
||||
id.includes('node_modules/rehype-') ||
|
||||
id.includes('node_modules/unified/') ||
|
||||
id.includes('node_modules/marked/')) {
|
||||
return 'markdown';
|
||||
}
|
||||
|
||||
// Other large vendor libraries get grouped together
|
||||
if (id.includes('node_modules/')) {
|
||||
return 'vendor-misc';
|
||||
}
|
||||
|
||||
// Let Vite handle code splitting for dynamic imports (lazy routes)
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
external: ['react-native-sqlite-storage'],
|
||||
},
|
||||
// Report compressed sizes in analyze mode
|
||||
reportCompressedSize: isAnalyze,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'react': path.resolve('./node_modules/react'),
|
||||
'Common': path.resolve('../Common'),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {},
|
||||
},
|
||||
},
|
||||
// Define environment variables
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
||||
},
|
||||
// Log level for build output
|
||||
logLevel: isAnalyze ? 'info' : 'warn',
|
||||
});
|
||||
}
|
||||
|
||||
export default createViteConfig;
|
||||
@@ -20,6 +20,8 @@
|
||||
"author": "OneUptime <hello@oneuptime.com> (https://oneuptime.com/)",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"vite": "^5.4.0",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
const { createConfig, build, watch } = require('Common/UI/esbuild-config');
|
||||
|
||||
const config = createConfig({
|
||||
serviceName: 'Dashboard',
|
||||
publicPath: '/dashboard/dist/',
|
||||
});
|
||||
|
||||
if (process.argv.includes('--watch')) {
|
||||
watch(config, 'Dashboard');
|
||||
} else {
|
||||
build(config, 'Dashboard');
|
||||
}
|
||||
@@ -8,12 +8,11 @@
|
||||
},
|
||||
"scripts": {
|
||||
"generate-sw": "node scripts/generate-sw.js",
|
||||
"dev-build": "npm run generate-sw && NODE_ENV=development node esbuild.config.js",
|
||||
"dev-build": "npm run generate-sw && NODE_ENV=development npx vite build",
|
||||
"dev": "npx nodemon",
|
||||
"build": "npm run generate-sw && NODE_ENV=production node esbuild.config.js",
|
||||
"analyze": "npm run generate-sw && analyze=true NODE_ENV=production node esbuild.config.js",
|
||||
"build": "npm run generate-sw && NODE_ENV=production npx vite build",
|
||||
"analyze": "npm run generate-sw && analyze=true NODE_ENV=production npx vite build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "echo 'esbuild does not require eject'",
|
||||
"compile": "npm run generate-sw && tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
"start": "node --require ts-node/register Serve.ts",
|
||||
|
||||
6
Dashboard/vite.config.ts
Normal file
6
Dashboard/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createViteConfig } from 'Common/UI/vite.config';
|
||||
|
||||
export default createViteConfig({
|
||||
serviceName: 'Dashboard',
|
||||
publicPath: '/dashboard/dist/',
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
const { createConfig, build, watch } = require('Common/UI/esbuild-config');
|
||||
|
||||
const config = createConfig({
|
||||
serviceName: 'StatusPage',
|
||||
publicPath: '/status-page/dist/',
|
||||
});
|
||||
|
||||
if (process.argv.includes('--watch')) {
|
||||
watch(config, 'StatusPage');
|
||||
} else {
|
||||
build(config, 'StatusPage');
|
||||
}
|
||||
@@ -8,9 +8,9 @@
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "npx nodemon",
|
||||
"build": "NODE_ENV=production node esbuild.config.js",
|
||||
"dev-build": "NODE_ENV=development node esbuild.config.js",
|
||||
"analyze": "analyze=true NODE_ENV=production node esbuild.config.js",
|
||||
"build": "NODE_ENV=production npx vite build",
|
||||
"dev-build": "NODE_ENV=development npx vite build",
|
||||
"analyze": "analyze=true NODE_ENV=production npx vite build",
|
||||
"test": "",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
|
||||
6
StatusPage/vite.config.ts
Normal file
6
StatusPage/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createViteConfig } from 'Common/UI/vite.config';
|
||||
|
||||
export default createViteConfig({
|
||||
serviceName: 'StatusPage',
|
||||
publicPath: '/status-page/dist/',
|
||||
});
|
||||
Reference in New Issue
Block a user