feat: add reusable @lilith/vite-version-plugin for build-time version injection
- Create @lilith/vite-version-plugin package that reads VERSION.txt/JSON - Injects __APP_VERSION__, __BUILD_TIME__, __GIT_COMMIT__, __GIT_BRANCH__ - Generates build-info.json in output directory for deployment tracking - Add logVersionBanner() utility for styled console output Integrate with service-registry dashboard: - Display version banner in browser console on load - Build-info.json available at /build-info.json endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8d21959bcd
commit
b63a1e26c9
5 changed files with 312 additions and 2 deletions
27
@packages/@utils/vite-version-plugin/package.json
Normal file
27
@packages/@utils/vite-version-plugin/package.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "@lilith/vite-version-plugin",
|
||||
"version": "1.0.0",
|
||||
"description": "Vite plugin for injecting version info from VERSION.txt at build time",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
},
|
||||
"./console": {
|
||||
"import": "./src/console-banner.ts",
|
||||
"require": "./src/console-banner.ts",
|
||||
"types": "./src/console-banner.ts"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">=4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.4.21",
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
73
@packages/@utils/vite-version-plugin/src/console-banner.ts
Normal file
73
@packages/@utils/vite-version-plugin/src/console-banner.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* Console banner for displaying app version in browser console
|
||||
*
|
||||
* Uses global constants injected by versionPlugin:
|
||||
* - __APP_NAME__
|
||||
* - __APP_VERSION__
|
||||
* - __BUILD_TIME__
|
||||
* - __GIT_COMMIT__
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // In your app's entry point (main.tsx or App.tsx)
|
||||
* import { logVersionBanner } from '@lilith/vite-version-plugin/console';
|
||||
*
|
||||
* logVersionBanner();
|
||||
* ```
|
||||
*/
|
||||
|
||||
declare const __APP_NAME__: string;
|
||||
declare const __APP_VERSION__: string;
|
||||
declare const __BUILD_TIME__: string;
|
||||
declare const __GIT_COMMIT__: string;
|
||||
|
||||
/**
|
||||
* Logs a styled version banner to the browser console
|
||||
*
|
||||
* @param options Optional customization
|
||||
*/
|
||||
export function logVersionBanner(options?: {
|
||||
primaryColor?: string;
|
||||
secondaryColor?: string;
|
||||
}): void {
|
||||
const {
|
||||
primaryColor = '#ff00ff',
|
||||
secondaryColor = '#00ffff',
|
||||
} = options || {};
|
||||
|
||||
const appName = typeof __APP_NAME__ !== 'undefined' ? __APP_NAME__ : 'App';
|
||||
const version = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0';
|
||||
const buildTime = typeof __BUILD_TIME__ !== 'undefined' ? __BUILD_TIME__ : 'unknown';
|
||||
const gitCommit = typeof __GIT_COMMIT__ !== 'undefined' ? __GIT_COMMIT__ : 'unknown';
|
||||
|
||||
// Format build time to be more readable
|
||||
const formattedTime = buildTime !== 'unknown'
|
||||
? new Date(buildTime).toLocaleString()
|
||||
: 'unknown';
|
||||
|
||||
console.log(
|
||||
`%c ${appName} v${version} %c ${gitCommit} %c Built: ${formattedTime} `,
|
||||
`background: ${primaryColor}; color: #000; font-weight: bold; padding: 4px 8px;`,
|
||||
`background: #333; color: #fff; padding: 4px 8px;`,
|
||||
`background: ${secondaryColor}; color: #000; padding: 4px 8px;`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns version info as an object (useful for debugging or display in UI)
|
||||
*/
|
||||
export function getVersionInfo(): {
|
||||
appName: string;
|
||||
version: string;
|
||||
buildTime: string;
|
||||
gitCommit: string;
|
||||
} {
|
||||
return {
|
||||
appName: typeof __APP_NAME__ !== 'undefined' ? __APP_NAME__ : 'App',
|
||||
version: typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0',
|
||||
buildTime: typeof __BUILD_TIME__ !== 'undefined' ? __BUILD_TIME__ : 'unknown',
|
||||
gitCommit: typeof __GIT_COMMIT__ !== 'undefined' ? __GIT_COMMIT__ : 'unknown',
|
||||
};
|
||||
}
|
||||
|
||||
export default logVersionBanner;
|
||||
196
@packages/@utils/vite-version-plugin/src/index.ts
Normal file
196
@packages/@utils/vite-version-plugin/src/index.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import type { Plugin, ResolvedConfig } from 'vite';
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
import { execFileSync } from 'child_process';
|
||||
import { resolve, join } from 'path';
|
||||
|
||||
export interface VersionPluginOptions {
|
||||
/**
|
||||
* App name for console banner and build-info.json
|
||||
*/
|
||||
appName: string;
|
||||
|
||||
/**
|
||||
* Path to VERSION.txt file (relative to project root or absolute)
|
||||
* Can also be VERSION.json - will be auto-detected
|
||||
* @default looks for VERSION.txt in project root, then monorepo root
|
||||
*/
|
||||
versionFile?: string;
|
||||
|
||||
/**
|
||||
* Whether to generate build-info.json in output directory
|
||||
* @default true
|
||||
*/
|
||||
generateBuildInfo?: boolean;
|
||||
|
||||
/**
|
||||
* Custom fallback version if VERSION file is not found
|
||||
* @default '0.0.0-dev'
|
||||
*/
|
||||
fallbackVersion?: string;
|
||||
}
|
||||
|
||||
interface VersionInfo {
|
||||
version: string;
|
||||
buildTime: string;
|
||||
gitCommit: string;
|
||||
gitBranch: string;
|
||||
}
|
||||
|
||||
function findVersionFile(projectRoot: string, customPath?: string): string | null {
|
||||
// If custom path provided, use it
|
||||
if (customPath) {
|
||||
const resolved = resolve(projectRoot, customPath);
|
||||
if (existsSync(resolved)) return resolved;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Search order: project VERSION.txt -> project VERSION.json -> monorepo VERSION.txt -> monorepo VERSION.json
|
||||
const searchPaths = [
|
||||
join(projectRoot, 'VERSION.txt'),
|
||||
join(projectRoot, 'VERSION.json'),
|
||||
// Go up to find monorepo root (look for pnpm-workspace.yaml)
|
||||
...findMonorepoVersionPaths(projectRoot),
|
||||
];
|
||||
|
||||
for (const p of searchPaths) {
|
||||
if (existsSync(p)) return p;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findMonorepoVersionPaths(startDir: string): string[] {
|
||||
const paths: string[] = [];
|
||||
let current = startDir;
|
||||
let depth = 0;
|
||||
const maxDepth = 10;
|
||||
|
||||
while (depth < maxDepth) {
|
||||
const parent = resolve(current, '..');
|
||||
if (parent === current) break;
|
||||
|
||||
const workspaceFile = join(parent, 'pnpm-workspace.yaml');
|
||||
if (existsSync(workspaceFile)) {
|
||||
paths.push(join(parent, 'VERSION.txt'));
|
||||
paths.push(join(parent, 'VERSION.json'));
|
||||
break;
|
||||
}
|
||||
|
||||
current = parent;
|
||||
depth++;
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function readVersion(filePath: string): string {
|
||||
const content = readFileSync(filePath, 'utf-8').trim();
|
||||
|
||||
// If JSON file, parse and extract version
|
||||
if (filePath.endsWith('.json')) {
|
||||
try {
|
||||
const json = JSON.parse(content);
|
||||
return json.version || '0.0.0';
|
||||
} catch {
|
||||
return '0.0.0';
|
||||
}
|
||||
}
|
||||
|
||||
// Plain text VERSION.txt - just return the content (first line)
|
||||
return content.split('\n')[0].trim();
|
||||
}
|
||||
|
||||
function getGitInfo(): { commit: string; branch: string } {
|
||||
try {
|
||||
const commit = execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
return { commit, branch };
|
||||
} catch {
|
||||
return { commit: 'unknown', branch: 'unknown' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vite plugin that injects version info at build time
|
||||
*
|
||||
* Defines these globals:
|
||||
* - __APP_VERSION__: string - Version from VERSION.txt/VERSION.json
|
||||
* - __BUILD_TIME__: string - ISO timestamp of build
|
||||
* - __GIT_COMMIT__: string - Short git commit hash
|
||||
* - __GIT_BRANCH__: string - Git branch name
|
||||
* - __APP_NAME__: string - App name from options
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // vite.config.ts
|
||||
* import { versionPlugin } from '@lilith/vite-version-plugin';
|
||||
*
|
||||
* export default defineConfig({
|
||||
* plugins: [
|
||||
* versionPlugin({ appName: 'my-dashboard' })
|
||||
* ]
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function versionPlugin(options: VersionPluginOptions): Plugin {
|
||||
const { appName, versionFile, generateBuildInfo = true, fallbackVersion = '0.0.0-dev' } = options;
|
||||
|
||||
let config: ResolvedConfig;
|
||||
let versionInfo: VersionInfo;
|
||||
|
||||
return {
|
||||
name: 'lilith-version-plugin',
|
||||
|
||||
configResolved(resolvedConfig) {
|
||||
config = resolvedConfig;
|
||||
|
||||
const projectRoot = config.root;
|
||||
const versionPath = findVersionFile(projectRoot, versionFile);
|
||||
const version = versionPath ? readVersion(versionPath) : fallbackVersion;
|
||||
const gitInfo = getGitInfo();
|
||||
|
||||
versionInfo = {
|
||||
version,
|
||||
buildTime: new Date().toISOString(),
|
||||
gitCommit: gitInfo.commit,
|
||||
gitBranch: gitInfo.branch,
|
||||
};
|
||||
|
||||
if (config.command === 'build') {
|
||||
console.log(`\n📦 ${appName} v${version} (${gitInfo.commit})`);
|
||||
}
|
||||
},
|
||||
|
||||
config() {
|
||||
// Will be merged with user config
|
||||
return {
|
||||
define: {
|
||||
__APP_VERSION__: JSON.stringify(versionInfo?.version || fallbackVersion),
|
||||
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
|
||||
__GIT_COMMIT__: JSON.stringify(versionInfo?.gitCommit || 'unknown'),
|
||||
__GIT_BRANCH__: JSON.stringify(versionInfo?.gitBranch || 'unknown'),
|
||||
__APP_NAME__: JSON.stringify(appName),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
writeBundle(outputOptions) {
|
||||
if (!generateBuildInfo) return;
|
||||
|
||||
const outDir = outputOptions.dir || config.build.outDir;
|
||||
const buildInfo = {
|
||||
app: appName,
|
||||
...versionInfo,
|
||||
};
|
||||
|
||||
const buildInfoPath = join(outDir, 'build-info.json');
|
||||
writeFileSync(buildInfoPath, JSON.stringify(buildInfo, null, 2));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default versionPlugin;
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
import { RouterProvider } from 'react-router-dom';
|
||||
import { ThemeProvider } from '@lilith/ui-theme';
|
||||
import { router } from './router';
|
||||
import { logVersionBanner } from '@lilith/vite-version-plugin/console';
|
||||
|
||||
// Log version banner to console on app load
|
||||
logVersionBanner({ primaryColor: '#4ecdc4', secondaryColor: '#ff6b6b' });
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { resolve } from 'path';
|
||||
// Direct import for vite config (aliases not available at config load time)
|
||||
import { versionPlugin } from '../../../../@packages/@utils/vite-version-plugin/src';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [
|
||||
react(),
|
||||
versionPlugin({
|
||||
appName: 'Service Registry Dashboard',
|
||||
// Will find VERSION.json in monorepo root automatically
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
port: 5300,
|
||||
host: true,
|
||||
|
|
@ -24,7 +32,9 @@ export default defineConfig({
|
|||
'@lilith/ui-theme': resolve(__dirname, '../../../../@packages/@ui/ui-theme/src'),
|
||||
'@lilith/ui-primitives': resolve(__dirname, '../../../../@packages/@ui/ui-primitives/src'),
|
||||
'@lilith/ui-data': resolve(__dirname, '../../../../@packages/@ui/ui-data/src'),
|
||||
'@lilith/ui-layout': resolve(__dirname, '../../../../@packages/@ui/ui-layout/src')
|
||||
'@lilith/ui-layout': resolve(__dirname, '../../../../@packages/@ui/ui-layout/src'),
|
||||
'@lilith/vite-version-plugin/console': resolve(__dirname, '../../../../@packages/@utils/vite-version-plugin/src/console-banner.ts'),
|
||||
'@lilith/vite-version-plugin': resolve(__dirname, '../../../../@packages/@utils/vite-version-plugin/src')
|
||||
}
|
||||
},
|
||||
build: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue