Files
xevion.dev/web/vite-plugin-json-logger.ts
Xevion b3dd1954d3 refactor: formatting and accessibility improvements across admin components
- Enforce consistent code formatting via ESLint rules
- Add accessibility enhancements (input IDs, labels, ARIA attributes)
- Replace Svelte store initialization patterns with reactive $effect
- Use $derived for reactive computed values
- Update error-codes and og-types indentation to spaces
- Add TypeScript definitions for html-minifier-terser
- Use resolve() for internal links in error and login pages
2026-01-06 14:39:21 -06:00

158 lines
3.6 KiB
TypeScript

import type { Plugin, ViteDevServer } from "vite";
import { configure, getLogger, type LogRecord } from "@logtape/logtape";
interface RailwayLogEntry {
timestamp: string;
level: string;
message: string;
target: string;
[key: string]: unknown;
}
function railwayFormatter(record: LogRecord): string {
const entry: RailwayLogEntry = {
timestamp: new Date().toISOString(),
level: record.level.toLowerCase(),
message: record.message.join(" "),
target: "vite",
};
if (record.properties && Object.keys(record.properties).length > 0) {
Object.assign(entry, record.properties);
}
return JSON.stringify(entry) + "\n";
}
function stripAnsi(str: string): string {
// eslint-disable-next-line no-control-regex
return str.replace(/\u001b\[[0-9;]*m/g, "").trim();
}
export function jsonLogger(): Plugin {
const useJsonLogs =
process.env.LOG_JSON === "true" || process.env.LOG_JSON === "1";
if (!useJsonLogs) {
return {
name: "vite-plugin-json-logger",
};
}
let loggerConfigured = false;
const configureLogger = async () => {
if (loggerConfigured) return;
await configure({
sinks: {
json: (record: LogRecord) => {
process.stdout.write(railwayFormatter(record));
},
},
filters: {},
loggers: [
{
category: ["logtape", "meta"],
lowestLevel: "warning",
sinks: ["json"],
},
{
category: [],
lowestLevel: "debug",
sinks: ["json"],
},
],
});
loggerConfigured = true;
};
let server: ViteDevServer;
const ignoredMessages = new Set(["press h + enter to show help", "ready in"]);
return {
name: "vite-plugin-json-logger",
async config() {
await configureLogger();
const logger = getLogger(["vite"]);
return {
customLogger: {
info(msg: string) {
const cleaned = stripAnsi(msg);
if (
!cleaned ||
ignoredMessages.has(cleaned) ||
cleaned.includes("VITE v")
) {
return;
}
logger.info(cleaned);
},
warn(msg: string) {
const cleaned = stripAnsi(msg);
if (cleaned) {
logger.warn(cleaned);
}
},
error(msg: string) {
const cleaned = stripAnsi(msg);
if (cleaned) {
logger.error(cleaned);
}
},
clearScreen() {},
hasErrorLogged() {
return false;
},
hasWarned: false,
warnOnce(msg: string) {
this.warn(msg);
},
},
};
},
configureServer(s) {
server = s;
const logger = getLogger(["vite"]);
server.printUrls = () => {
const urls = server.resolvedUrls;
if (urls) {
logger.info("dev server running", {
local: urls.local,
network: urls.network,
});
}
};
server.httpServer?.once("listening", () => {
logger.info("server listening");
});
server.ws.on("connection", () => {
logger.info("client connected");
});
},
handleHotUpdate({ file, modules }) {
const logger = getLogger(["vite"]);
logger.info("hmr update", {
file: file.replace(process.cwd(), ""),
modules: modules.length,
});
return modules;
},
buildStart() {
const logger = getLogger(["vite"]);
logger.info("build started");
},
buildEnd() {
const logger = getLogger(["vite"]);
logger.info("build ended");
},
};
}