Revert "minimize the junk which was in this files before"

This reverts commit 5783ab1987.
This commit is contained in:
kmmiio99o 2025-12-27 15:57:32 +01:00
parent 5783ab1987
commit 034fa33ae5
No known key found for this signature in database
GPG key ID: 163FE39F7772D2EC
2 changed files with 1176 additions and 95 deletions

View file

@ -1,109 +1,651 @@
import commonjs from "@rollup/plugin-commonjs";
import nodeResolve from "@rollup/plugin-node-resolve";
import { createHash } from "crypto";
import { readFile, readdir, writeFile } from "fs/promises";
import { readFile, readdir, writeFile, stat } from "fs/promises";
import { argv } from "process";
import { rollup, watch } from "rollup";
import swc from "@swc/core";
import esbuild from "rollup-plugin-esbuild";
const args = argv.slice(2);
const isWatch = args.includes("--watch") || args.includes("-w");
const pluginsToBuild = args.filter((x) => !x.startsWith("-")).length
? args.filter((x) => !x.startsWith("-"))
: await readdir("./plugins");
// ═══════════════════════════════════════════════════════════════════════════════
// 🎨 STYLING & ANIMATIONS
// ═══════════════════════════════════════════════════════════════════════════════
const successfulPlugins = [];
const colors = {
reset: "\x1b[0m",
bold: "\x1b[1m",
dim: "\x1b[2m",
italic: "\x1b[3m",
underline: "\x1b[4m",
async function buildPlugin(plugin) {
if (plugin.endsWith(".ts")) return;
// Standard colors
black: "\x1b[30m",
red: "\x1b[31m",
green: "\x1b[32m",
yellow: "\x1b[33m",
blue: "\x1b[34m",
magenta: "\x1b[35m",
cyan: "\x1b[36m",
white: "\x1b[37m",
try {
const manifest = Object.assign(
JSON.parse(await readFile("./base_manifest.json")),
JSON.parse(await readFile(`./plugins/${plugin}/manifest.json`)),
// Bright colors
brightRed: "\x1b[91m",
brightGreen: "\x1b[92m",
brightYellow: "\x1b[93m",
brightBlue: "\x1b[94m",
brightMagenta: "\x1b[95m",
brightCyan: "\x1b[96m",
brightWhite: "\x1b[97m",
// Backgrounds
bgRed: "\x1b[41m",
bgGreen: "\x1b[42m",
bgYellow: "\x1b[43m",
bgBlue: "\x1b[44m",
bgMagenta: "\x1b[45m",
bgCyan: "\x1b[46m",
};
const symbols = {
// Build status
success: "✓",
error: "✗",
warning: "⚠",
info: "ℹ",
// Activities
building: "🔨",
watching: "👁",
rocket: "🚀",
gear: "⚙",
lightning: "⚡",
fire: "🔥",
sparkles: "✨",
// UI elements
arrow: "→",
bullet: "•",
star: "★",
diamond: "◆",
// Box drawing
boxLight: "─",
boxHeavy: "━",
boxDouble: "═",
cornerTL: "┌",
cornerTR: "┐",
cornerBL: "└",
cornerBR: "┘",
cross: "┼",
teeLeft: "├",
teeRight: "┤",
vertical: "│",
};
const spinners = {
dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
line: ["│", "╱", "─", "╲"],
bounce: ["⠁", "⠂", "⠄", "⠂"],
pulse: ["●", "◐", "◑", "◒", "◓", "◔", "◕", "◖", "◗"],
};
// ═══════════════════════════════════════════════════════════════════════════════
// 🛠 UTILITIES
// ═══════════════════════════════════════════════════════════════════════════════
function formatTime(ms) {
if (ms < 1000) return `${ms}ms`;
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
}
function formatSize(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
}
function createGradient(text, startColor, endColor) {
// Simple gradient effect by alternating colors
return text
.split("")
.map((char, i) => {
const color = i % 2 === 0 ? startColor : endColor;
return `${color}${char}${colors.reset}`;
})
.join("");
}
function createBox(content, title = "", style = "light") {
const lines = content.split("\n");
const maxLength = Math.max(
...lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").length),
title.length,
);
const width = maxLength + 4;
const horizontal =
style === "heavy"
? symbols.boxHeavy
: style === "double"
? symbols.boxDouble
: symbols.boxLight;
let result = "";
// Top border
result += `${symbols.cornerTL}${horizontal.repeat(width - 2)}${symbols.cornerTR}\n`;
// Title
if (title) {
const padding = Math.floor((width - title.length - 4) / 2);
result += `${symbols.vertical} ${" ".repeat(padding)}${colors.bold}${title}${colors.reset}${" ".repeat(padding)} ${symbols.vertical}\n`;
result += `${symbols.teeLeft}${horizontal.repeat(width - 2)}${symbols.teeRight}\n`;
}
// Content
lines.forEach((line) => {
const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, "");
const padding = width - cleanLine.length - 4;
result += `${symbols.vertical} ${line}${" ".repeat(padding)} ${symbols.vertical}\n`;
});
// Bottom border
result += `${symbols.cornerBL}${horizontal.repeat(width - 2)}${symbols.cornerBR}`;
return result;
}
class Spinner {
constructor(text = "", spinnerType = "dots") {
this.text = text;
this.frames = spinners[spinnerType];
this.current = 0;
this.isSpinning = false;
this.interval = null;
}
start() {
if (this.isSpinning) return;
this.isSpinning = true;
this.interval = setInterval(() => {
process.stdout.write(
`\r${colors.cyan}${this.frames[this.current]}${colors.reset} ${this.text}`,
);
this.current = (this.current + 1) % this.frames.length;
}, 100);
}
succeed(text) {
this.stop();
console.log(
`${colors.green}${symbols.success}${colors.reset} ${text || this.text}`,
);
}
fail(text) {
this.stop();
console.log(
`${colors.red}${symbols.error}${colors.reset} ${text || this.text}`,
);
}
stop() {
if (!this.isSpinning) return;
this.isSpinning = false;
clearInterval(this.interval);
process.stdout.write("\r\x1b[K"); // Clear line
}
}
function createProgressBar(current, total, width = 40, showPercentage = true) {
const percentage = Math.round((current / total) * 100);
const filled = Math.round((current / total) * width);
const empty = width - filled;
const filledBar = `${colors.brightCyan}${"█".repeat(filled)}${colors.reset}`;
const emptyBar = `${colors.dim}${"░".repeat(empty)}${colors.reset}`;
let result = `${filledBar}${emptyBar}`;
if (showPercentage) {
result += ` ${colors.bold}${percentage.toString().padStart(3)}%${colors.reset}`;
}
return result;
}
// ═══════════════════════════════════════════════════════════════════════════════
// 🎭 LOGGER
// ═══════════════════════════════════════════════════════════════════════════════
const logger = {
banner() {
const title = createGradient(
"VENDETTA PLUGIN BUILDER",
colors.brightCyan,
colors.brightMagenta,
);
const subtitle = `${colors.dim}Professional-grade TypeScript plugin compilation${colors.reset}`;
console.log();
console.log(` ${symbols.rocket} ${title}`);
console.log(` ${subtitle}`);
console.log();
},
section(title) {
const line = symbols.boxHeavy.repeat(60);
console.log(`\n${colors.brightBlue}${line}${colors.reset}`);
console.log(`${colors.bold}${colors.brightBlue} ${title}${colors.reset}`);
console.log(`${colors.brightBlue}${line}${colors.reset}\n`);
},
info(message, icon = symbols.info) {
console.log(`${colors.brightBlue}${icon}${colors.reset} ${message}`);
},
success(message, icon = symbols.success) {
console.log(`${colors.brightGreen}${icon}${colors.reset} ${message}`);
},
warning(message, icon = symbols.warning) {
console.log(`${colors.brightYellow}${icon}${colors.reset} ${message}`);
},
error(message, icon = symbols.error) {
console.log(`${colors.brightRed}${icon}${colors.reset} ${message}`);
},
plugin: {
building(name) {
return new Spinner(
`${colors.bold}${name}${colors.reset} ${colors.dim}Building...${colors.reset}`,
"pulse",
);
},
success(name, time, size) {
const timeStr = `${colors.dim}${time}${colors.reset}`;
const sizeStr = size ? `${colors.dim}${size}${colors.reset}` : "";
console.log(
`${colors.brightGreen}${symbols.success}${colors.reset} ${colors.bold}${name}${colors.reset} ${timeStr} ${sizeStr}`,
);
},
error(name, error) {
console.log(
`${colors.brightRed}${symbols.error}${colors.reset} ${colors.bold}${name}${colors.reset}`,
);
if (error) {
console.log(`${colors.red}${colors.dim} ${error}${colors.reset}`);
}
},
watching(name, time) {
const timeStr = `${colors.dim}${time}${colors.reset}`;
console.log(
`${colors.brightCyan}${symbols.lightning}${colors.reset} ${colors.bold}${name}${colors.reset} ${colors.dim}rebuilt${colors.reset} ${timeStr}`,
);
},
},
table(headers, rows) {
const colWidths = headers.map((header, i) =>
Math.max(
header.length,
...rows.map(
(row) =>
(row[i] || "").toString().replace(/\x1b\[[0-9;]*m/g, "").length,
),
),
);
const entry = "index.js";
const outPath = `./dist/${plugin}/${entry}`;
// Header
const headerRow = headers
.map(
(header, i) =>
`${colors.bold}${header.padEnd(colWidths[i])}${colors.reset}`,
)
.join(` ${colors.dim}│${colors.reset} `);
const options = {
input: `./plugins/${plugin}/${manifest.main}`,
external: (id) =>
id.startsWith("@vendetta") || ["react", "react-native"].includes(id),
output: {
file: outPath,
format: "iife",
compact: true,
exports: "named",
name: "plugin",
globals: (id) => {
if (id.startsWith("@vendetta"))
return id.substring(1).replace(/\//g, ".");
if (id === "react") return "window.React";
if (id === "react-native") return "vendetta.metro.common.ReactNative";
return null;
},
},
onwarn: () => {},
plugins: [
nodeResolve(),
commonjs(),
{
name: "swc",
transform: (code, id) =>
swc.transform(code, {
filename: id,
jsc: {
parser: { syntax: "typescript", tsx: true },
externalHelpers: true,
},
env: { targets: "defaults" },
}),
},
esbuild({ minify: true }),
console.log(` ${headerRow}`);
// Separator
const separator = colWidths
.map((width) => symbols.boxLight.repeat(width))
.join(`${colors.dim}─┼─${colors.reset}`);
console.log(` ${colors.dim}${separator}${colors.reset}`);
// Rows
rows.forEach((row) => {
const formattedRow = row
.map((cell, i) => {
const cleanCell = (cell || "")
.toString()
.replace(/\x1b\[[0-9;]*m/g, "");
const padding = colWidths[i] - cleanCell.length;
return cell + " ".repeat(padding);
})
.join(` ${colors.dim}│${colors.reset} `);
console.log(` ${formattedRow}`);
});
},
summary(stats) {
const { built, failed, total, totalTime, watchMode } = stats;
const rows = [
["Total Plugins", total.toString()],
["Built Successfully", `${colors.brightGreen}${built}${colors.reset}`],
[
"Failed",
failed > 0
? `${colors.brightRed}${failed}${colors.reset}`
: `${colors.dim}0${colors.reset}`,
],
};
["Total Time", formatTime(totalTime)],
[
"Mode",
watchMode
? `${colors.brightCyan}Watch${colors.reset}`
: `${colors.dim}Build${colors.reset}`,
],
];
const finalize = async () => {
const content = await readFile(outPath);
manifest.hash = createHash("sha256").update(content).digest("hex");
manifest.main = entry;
await writeFile(
`./dist/${plugin}/manifest.json`,
JSON.stringify(manifest),
this.table(["Metric", "Value"], rows);
},
};
// ═══════════════════════════════════════════════════════════════════════════════
// 🚀 MAIN EXECUTION
// ═══════════════════════════════════════════════════════════════════════════════
const args = argv.slice(2);
console.clear();
// Parse arguments
const inputPlugins = args.filter((x) => !x.startsWith("-"));
const flags = args.map((x) => x.toLowerCase()).filter((x) => x.startsWith("-"));
const isWatch = flags.includes("--watch") || flags.includes("-w");
const isVerbose = flags.includes("--verbose") || flags.includes("-v");
const toBuild = inputPlugins.length ? inputPlugins : await readdir("./plugins");
// Display banner
logger.banner();
// Build configuration
const config = {
plugins: toBuild.length,
mode: isWatch ? "Watch" : "Build",
verbose: isVerbose,
startTime: Date.now(),
};
logger.info(
`${colors.dim}Found ${colors.reset}${colors.bold}${config.plugins}${colors.reset}${colors.dim} plugin(s) to ${config.mode.toLowerCase()}${colors.reset}`,
);
if (isWatch) {
logger.info(
`${colors.dim}Running in watch mode - changes will trigger rebuilds${colors.reset}`,
);
}
// Build statistics
let built = 0;
let failed = 0;
const buildResults = [];
const startTime = Date.now();
// Build all plugins
logger.section("Building Plugins");
for (let i = 0; i < toBuild.length; i++) {
const plugin = toBuild[i];
if (!isWatch) {
console.log(
`\n ${createProgressBar(i, toBuild.length)} ${colors.dim}(${i}/${toBuild.length})${colors.reset}`,
);
}
const pluginStartTime = Date.now();
let spinner;
try {
if (!plugin.endsWith(".ts")) {
spinner = logger.plugin.building(plugin);
spinner.start();
const result = await buildPlugin(plugin);
const buildTime = Date.now() - pluginStartTime;
spinner.stop();
logger.plugin.success(
plugin,
formatTime(buildTime),
result.size ? formatSize(result.size) : "",
);
};
if (isWatch) {
const watcher = watch(options);
watcher.on("event", async (event) => {
if (event.code === "BUNDLE_END") {
await finalize();
event.result.close();
console.log(`[WATCH] Rebuilt ${plugin}`);
} else if (event.code === "ERROR") {
console.error(`[ERROR] ${plugin}: ${event.error.message}`);
}
buildResults.push({
name: plugin,
status: "success",
time: buildTime,
size: result.size || 0,
});
} else {
const bundle = await rollup(options);
await bundle.write(options.output);
await bundle.close();
await finalize();
successfulPlugins.push(plugin);
built++;
}
} catch (e) {
console.error(`[ERROR] ${plugin}: ${e.message}`);
if (spinner) spinner.stop();
logger.plugin.error(plugin, isVerbose ? e.stack : e.message);
buildResults.push({
name: plugin,
status: "error",
time: Date.now() - pluginStartTime,
error: e.message,
});
failed++;
}
}
// Start processing
const buildPromises = pluginsToBuild.map((p) => buildPlugin(p));
// Final progress
if (!isWatch) {
await Promise.all(buildPromises);
if (successfulPlugins.length > 0) {
console.log(`Successfully built: ${successfulPlugins.join(", ")}`);
}
console.log(
`\n ${createProgressBar(toBuild.length, toBuild.length)} ${colors.dim}(${toBuild.length}/${toBuild.length})${colors.reset}`,
);
}
// Summary
logger.section("Build Summary");
const totalTime = Date.now() - startTime;
logger.summary({
built,
failed,
total: toBuild.length,
totalTime,
watchMode: isWatch,
});
// Results table
if (buildResults.length > 0 && !isWatch) {
console.log();
const tableRows = buildResults.map((result) => [
result.name,
result.status === "success"
? `${colors.brightGreen}${symbols.success} Success${colors.reset}`
: `${colors.brightRed}${symbols.error} Failed${colors.reset}`,
formatTime(result.time),
result.size ? formatSize(result.size) : `${colors.dim}N/A${colors.reset}`,
]);
logger.table(["Plugin", "Status", "Time", "Size"], tableRows);
}
// Final status
console.log();
if (failed > 0) {
logger.error(
`Build completed with ${failed} error(s) ${symbols.bullet} ${built} succeeded`,
symbols.warning,
);
} else {
logger.success(
`All plugins built successfully! ${symbols.sparkles}`,
symbols.fire,
);
}
// Watch mode notification
if (isWatch) {
console.log();
logger.info(
`Watching for changes... ${colors.dim}Press ${colors.reset}${colors.bold}Ctrl+C${colors.reset}${colors.dim} to exit${colors.reset}`,
symbols.watching,
);
}
console.log();
// ═══════════════════════════════════════════════════════════════════════════════
// 🔧 BUILD FUNCTION
// ═══════════════════════════════════════════════════════════════════════════════
async function buildPlugin(plugin) {
if (plugin.endsWith(".ts")) return {};
const manifest = Object.assign(
JSON.parse(await readFile("./base_manifest.json")),
JSON.parse(await readFile(`./plugins/${plugin}/manifest.json`)),
);
const entry = "index.js";
const outPath = `./dist/${plugin}/${entry}`;
/** @type {import("rollup").RollupOptions} */
const options = {
input: `./plugins/${plugin}/${manifest.main}`,
output: {
file: outPath,
globals(id) {
if (id.startsWith("@vendetta"))
return id.substring(1).replace(/\//g, ".");
const map = {
react: "window.React",
"react-native": "vendetta.metro.common.ReactNative",
};
return map[id] || null;
},
format: "iife",
compact: true,
exports: "named",
inlineDynamicImports: true,
},
onwarn: (warning) => {
if (
![
"UNRESOLVED_IMPORT",
"MISSING_NAME_OPTION_FOR_IIFE_EXPORT",
"CIRCULAR_DEPENDENCY",
].includes(warning.code) &&
isVerbose
) {
logger.warning(`${plugin}: ${warning.message}`);
}
},
plugins: [
nodeResolve({
resolveOnly: (id) => !["react", "react-native"].includes(id),
}),
commonjs(),
{
name: "swc",
transform(code, id) {
return swc.transform(code, {
filename: id,
jsc: {
parser: {
syntax: "typescript",
tsx: true,
},
externalHelpers: true,
},
env: {
targets: "defaults",
include: ["transform-classes", "transform-arrow-functions"],
},
});
},
},
esbuild({ minify: true }),
],
};
const applyHash = async () => {
const content = await readFile(outPath);
return Object.assign(manifest, {
hash: createHash("sha256").update(content).digest("hex"),
main: entry,
});
};
if (!isWatch) {
const bundle = await rollup(options);
await bundle.write(options.output);
await bundle.close();
await applyHash();
await writeFile(`./dist/${plugin}/manifest.json`, JSON.stringify(manifest));
// Get file size
const stats = await stat(outPath);
return { size: stats.size };
}
const watcher = watch(options);
return await new Promise((resolve, reject) => {
watcher.on("event", async (event) => {
switch (event.code) {
case "BUNDLE_START":
// Silent in watch mode, spinner handles this
break;
case "BUNDLE_END": {
event.result.close();
await applyHash();
await writeFile(
`./dist/${plugin}/manifest.json`,
JSON.stringify(manifest),
);
const buildTime = formatTime(event.duration || 0);
logger.plugin.watching(plugin, buildTime);
resolve({ size: 0 });
break;
}
case "ERROR":
case "FATAL":
logger.plugin.error(
plugin,
isVerbose ? event.error.stack : event.error.message,
);
reject(event.error);
break;
}
});
});
}

View file

@ -1,18 +1,557 @@
#!/usr/bin/env node
import { spawn } from "child_process";
import { exec, spawn } from "child_process";
import { networkInterfaces } from "os";
import path from "path";
import { fileURLToPath } from "url";
import { readdir, stat, access } from "fs/promises";
import { createHash } from "crypto";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const distPath = path.resolve(__dirname, "..", "dist");
const port = process.env.PORT || 1400;
// ═══════════════════════════════════════════════════════════════════════════════
// 🎨 STYLING & ANIMATIONS
// ═══════════════════════════════════════════════════════════════════════════════
const server = spawn("npx", ["serve", "-s", distPath, "-l", port.toString()], {
stdio: "inherit",
shell: process.platform === "win32",
});
process.on("SIGINT", () => {
server.kill();
process.exit();
const colors = {
reset: "\x1b[0m",
bold: "\x1b[1m",
dim: "\x1b[2m",
italic: "\x1b[3m",
underline: "\x1b[4m",
// Standard colors
black: "\x1b[30m",
red: "\x1b[31m",
green: "\x1b[32m",
yellow: "\x1b[33m",
blue: "\x1b[34m",
magenta: "\x1b[35m",
cyan: "\x1b[36m",
white: "\x1b[37m",
// Bright colors
brightRed: "\x1b[91m",
brightGreen: "\x1b[92m",
brightYellow: "\x1b[93m",
brightBlue: "\x1b[94m",
brightMagenta: "\x1b[95m",
brightCyan: "\x1b[96m",
brightWhite: "\x1b[97m",
// Backgrounds
bgRed: "\x1b[41m",
bgGreen: "\x1b[42m",
bgYellow: "\x1b[43m",
bgBlue: "\x1b[44m",
bgMagenta: "\x1b[45m",
bgCyan: "\x1b[46m",
};
const symbols = {
// Status indicators
success: "✓",
error: "✗",
warning: "⚠",
info: "ℹ",
// Activities
server: "🚀",
folder: "📁",
network: "🌐",
local: "🏠",
external: "📡",
lightning: "⚡",
fire: "🔥",
sparkles: "✨",
eyes: "👀",
stop: "🛑",
gear: "⚙",
globe: "🌍",
// UI elements
arrow: "→",
bullet: "•",
star: "★",
diamond: "◆",
// Box drawing
boxLight: "─",
boxHeavy: "━",
boxDouble: "═",
cornerTL: "┌",
cornerTR: "┐",
cornerBL: "└",
cornerBR: "┘",
cross: "┼",
teeLeft: "├",
teeRight: "┤",
vertical: "│",
};
// ═══════════════════════════════════════════════════════════════════════════════
// 🛠 UTILITIES
// ═══════════════════════════════════════════════════════════════════════════════
function createGradient(text, startColor, endColor) {
return text
.split("")
.map((char, i) => {
const color = i % 2 === 0 ? startColor : endColor;
return `${color}${char}${colors.reset}`;
})
.join("");
}
function createBox(content, title = "", style = "light") {
const lines = content.split("\n");
const maxLength = Math.max(
...lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").length),
title.length,
);
const width = maxLength + 4;
const horizontal =
style === "heavy"
? symbols.boxHeavy
: style === "double"
? symbols.boxDouble
: symbols.boxLight;
let result = "";
// Top border
result += `${symbols.cornerTL}${horizontal.repeat(width - 2)}${symbols.cornerTR}\n`;
// Title
if (title) {
const padding = Math.floor((width - title.length - 4) / 2);
result += `${symbols.vertical} ${" ".repeat(padding)}${colors.bold}${title}${colors.reset}${" ".repeat(padding)} ${symbols.vertical}\n`;
result += `${symbols.teeLeft}${horizontal.repeat(width - 2)}${symbols.teeRight}\n`;
}
// Content
lines.forEach((line) => {
const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, "");
const padding = width - cleanLine.length - 4;
result += `${symbols.vertical} ${line}${" ".repeat(padding)} ${symbols.vertical}\n`;
});
// Bottom border
result += `${symbols.cornerBL}${horizontal.repeat(width - 2)}${symbols.cornerBR}`;
return result;
}
function formatFileSize(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
}
// ═══════════════════════════════════════════════════════════════════════════════
// 🌐 NETWORK UTILITIES
// ═══════════════════════════════════════════════════════════════════════════════
function getAllNetworkInterfaces() {
const nets = networkInterfaces();
const results = [];
for (const name of Object.keys(nets)) {
for (const net of nets[name]) {
if (net.family === "IPv4" && !net.internal) {
results.push({
interface: name,
address: net.address,
netmask: net.netmask,
mac: net.mac,
});
}
}
}
return results;
}
function getPrimaryIP() {
const interfaces = getAllNetworkInterfaces();
// Prefer WiFi/Ethernet interfaces
const preferred = interfaces.find(
(iface) =>
iface.interface.toLowerCase().includes("wlan") ||
iface.interface.toLowerCase().includes("wifi") ||
iface.interface.toLowerCase().includes("eth") ||
iface.interface.toLowerCase().includes("en"),
);
return preferred ? preferred.address : interfaces[0]?.address || "localhost";
}
// ═══════════════════════════════════════════════════════════════════════════════
// 📊 FILE SYSTEM UTILITIES
// ═══════════════════════════════════════════════════════════════════════════════
async function analyzeDistDirectory(distPath) {
try {
await access(distPath);
const entries = await readdir(distPath);
const plugins = [];
let totalSize = 0;
for (const entry of entries) {
try {
const entryPath = path.join(distPath, entry);
const entryStats = await stat(entryPath);
if (entryStats.isDirectory()) {
// Analyze plugin directory
const pluginFiles = await readdir(entryPath);
let pluginSize = 0;
for (const file of pluginFiles) {
const filePath = path.join(entryPath, file);
const fileStats = await stat(filePath);
if (fileStats.isFile()) {
pluginSize += fileStats.size;
}
}
plugins.push({
name: entry,
size: pluginSize,
files: pluginFiles.length,
});
totalSize += pluginSize;
}
} catch (error) {
// Skip entries that can't be read
}
}
return {
plugins,
totalSize,
totalPlugins: plugins.length,
};
} catch (error) {
return {
plugins: [],
totalSize: 0,
totalPlugins: 0,
error: error.message,
};
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 🎭 LOGGER
// ═══════════════════════════════════════════════════════════════════════════════
const logger = {
banner() {
const title = createGradient(
"VENDETTA DEV SERVER",
colors.brightCyan,
colors.brightMagenta,
);
const subtitle = `${colors.dim}Professional development server for Vendetta plugins${colors.reset}`;
console.log();
console.log(` ${symbols.server} ${title}`);
console.log(` ${subtitle}`);
console.log();
},
section(title) {
const line = symbols.boxHeavy.repeat(60);
console.log(`\n${colors.brightBlue}${line}${colors.reset}`);
console.log(`${colors.bold}${colors.brightBlue} ${title}${colors.reset}`);
console.log(`${colors.brightBlue}${line}${colors.reset}\n`);
},
info(message, icon = symbols.info) {
console.log(`${colors.brightBlue}${icon}${colors.reset} ${message}`);
},
success(message, icon = symbols.success) {
console.log(`${colors.brightGreen}${icon}${colors.reset} ${message}`);
},
warning(message, icon = symbols.warning) {
console.log(`${colors.brightYellow}${icon}${colors.reset} ${message}`);
},
error(message, icon = symbols.error) {
console.log(`${colors.brightRed}${icon}${colors.reset} ${message}`);
},
table(headers, rows) {
const colWidths = headers.map((header, i) =>
Math.max(
header.length,
...rows.map(
(row) =>
(row[i] || "").toString().replace(/\x1b\[[0-9;]*m/g, "").length,
),
),
);
// Header
const headerRow = headers
.map(
(header, i) =>
`${colors.bold}${header.padEnd(colWidths[i])}${colors.reset}`,
)
.join(` ${colors.dim}│${colors.reset} `);
console.log(` ${headerRow}`);
// Separator
const separator = colWidths
.map((width) => symbols.boxLight.repeat(width))
.join(`${colors.dim}─┼─${colors.reset}`);
console.log(` ${colors.dim}${separator}${colors.reset}`);
// Rows
rows.forEach((row) => {
const formattedRow = row
.map((cell, i) => {
const cleanCell = (cell || "")
.toString()
.replace(/\x1b\[[0-9;]*m/g, "");
const padding = colWidths[i] - cleanCell.length;
return cell + " ".repeat(padding);
})
.join(` ${colors.dim}│${colors.reset} `);
console.log(` ${formattedRow}`);
});
},
};
// ═══════════════════════════════════════════════════════════════════════════════
// 🚀 MAIN EXECUTION
// ═══════════════════════════════════════════════════════════════════════════════
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function main() {
console.clear();
// Configuration
const distPath = path.join(__dirname, "..", "dist");
const port = process.env.PORT || 1400;
const host = "0.0.0.0";
const primaryIP = getPrimaryIP();
const allInterfaces = getAllNetworkInterfaces();
// Display banner
logger.banner();
// Analyze distribution directory
logger.section("Analyzing Distribution");
const analysis = await analyzeDistDirectory(distPath);
if (analysis.error) {
logger.error(`Cannot access dist directory: ${analysis.error}`);
logger.info(
`Make sure to run ${colors.bold}npm run build${colors.reset} first`,
);
process.exit(1);
}
logger.info(
`${colors.dim}Serving from${colors.reset} ${colors.bold}${distPath}${colors.reset}`,
);
logger.success(
`Found ${colors.bold}${analysis.totalPlugins}${colors.reset} plugin(s) ${colors.dim}(${formatFileSize(analysis.totalSize)} total)${colors.reset}`,
);
// Plugin table
if (analysis.plugins.length > 0) {
console.log();
const pluginRows = analysis.plugins.map((plugin) => [
plugin.name,
plugin.files.toString(),
formatFileSize(plugin.size),
]);
logger.table(["Plugin", "Files", "Size"], pluginRows);
}
// Network information
logger.section("Network Configuration");
const localUrl = `http://localhost:${port}`;
const networkUrl = `http://${primaryIP}:${port}`;
logger.info(
`${colors.dim}Binding to${colors.reset} ${colors.bold}${host}:${port}${colors.reset}`,
);
// Network interfaces table
if (allInterfaces.length > 0) {
console.log();
const networkRows = allInterfaces.map((iface) => [
iface.interface,
iface.address,
`http://${iface.address}:${port}`,
]);
logger.table(["Interface", "IP Address", "URL"], networkRows);
}
// Access URLs
logger.section("Access Information");
logger.success(
`${symbols.local} Local: ${colors.bold}${colors.underline}${localUrl}${colors.reset}`,
);
logger.success(
`${symbols.network} Network: ${colors.bold}${colors.underline}${networkUrl}${colors.reset}`,
);
logger.info(
`${symbols.globe} External: ${colors.dim}Accessible from any device on your network${colors.reset}`,
);
// Server startup
logger.section("Starting Server");
logger.info(`${symbols.lightning} Initializing development server...`);
// Check if serve is available
try {
await new Promise((resolve, reject) => {
exec("npx serve --version", (error) => {
if (error) reject(error);
else resolve();
});
});
} catch (error) {
logger.warning("Installing serve package...");
}
const serverProcess = spawn(
"npx",
["serve", "-s", distPath, "-l", port.toString()],
{
stdio: ["pipe", "pipe", "pipe"],
shell: process.platform === "win32",
},
);
// Handle server output
let serverStarted = false;
serverProcess.stdout.on("data", (data) => {
const output = data.toString();
if (
!serverStarted &&
(output.includes("Accepting connections") || output.includes("Local:"))
) {
serverStarted = true;
logger.success(`${symbols.fire} Server is running!`);
console.log();
// Instructions
logger.info(
`${symbols.eyes} ${colors.dim}Watching for file changes...${colors.reset}`,
);
logger.info(
`${symbols.stop} ${colors.dim}Press ${colors.reset}${colors.bold}Ctrl+C${colors.reset}${colors.dim} to stop the server${colors.reset}`,
);
console.log();
console.log(
`${colors.brightGreen}${symbols.sparkles} Ready to develop! ${symbols.sparkles}${colors.reset}`,
);
console.log();
}
// Filter out serve's own output to avoid duplication
if (
!output.includes("Local:") &&
!output.includes("On Your Network:") &&
!output.includes("Copied local address") &&
!output.includes("Accepting connections") &&
output.trim()
) {
console.log(`${colors.dim}[serve] ${output.trim()}${colors.reset}`);
}
});
serverProcess.stderr.on("data", (data) => {
const error = data.toString().trim();
if (error && !error.includes("WARNING") && !error.includes("deprecated")) {
logger.error(`Server error: ${error}`);
}
});
serverProcess.on("error", (error) => {
logger.error(`Failed to start server: ${error.message}`);
process.exit(1);
});
serverProcess.on("exit", (code) => {
if (code !== 0 && code !== null) {
logger.error(`Server exited with code ${code}`);
process.exit(code);
}
});
// Graceful shutdown
const shutdown = (signal) => {
console.log(
`\n${colors.brightYellow}${symbols.stop} Received ${signal}, shutting down gracefully...${colors.reset}`,
);
if (serverProcess && !serverProcess.killed) {
serverProcess.kill("SIGTERM");
setTimeout(() => {
if (!serverProcess.killed) {
logger.warning("Force killing server process...");
serverProcess.kill("SIGKILL");
}
}, 5000);
}
setTimeout(() => {
logger.success("Server stopped successfully");
process.exit(0);
}, 1000);
};
process.on("SIGINT", () => shutdown("SIGINT"));
process.on("SIGTERM", () => shutdown("SIGTERM"));
// Handle uncaught exceptions
process.on("uncaughtException", (error) => {
logger.error(`Uncaught exception: ${error.message}`);
console.error(error.stack);
shutdown("UNCAUGHT_EXCEPTION");
});
process.on("unhandledRejection", (reason, promise) => {
logger.error(`Unhandled rejection at: ${promise}, reason: ${reason}`);
shutdown("UNHANDLED_REJECTION");
});
}
// Start the server
main().catch((error) => {
console.error(
`${colors.brightRed}${symbols.error} Fatal error:${colors.reset}`,
error.message,
);
console.error(error.stack);
process.exit(1);
});