remove the junk from the files and use pylix build file
This commit is contained in:
parent
034fa33ae5
commit
283a9f8f39
2 changed files with 45 additions and 1075 deletions
|
|
@ -1,528 +1,46 @@
|
||||||
import commonjs from "@rollup/plugin-commonjs";
|
import commonjs from "@rollup/plugin-commonjs";
|
||||||
import nodeResolve from "@rollup/plugin-node-resolve";
|
import nodeResolve from "@rollup/plugin-node-resolve";
|
||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
import { readFile, readdir, writeFile, stat } from "fs/promises";
|
import { readFile, readdir, writeFile } from "fs/promises";
|
||||||
import { argv } from "process";
|
import { argv } from "process";
|
||||||
import { rollup, watch } from "rollup";
|
import { rollup, watch } from "rollup";
|
||||||
|
|
||||||
import swc from "@swc/core";
|
import swc from "@swc/core";
|
||||||
import esbuild from "rollup-plugin-esbuild";
|
import esbuild from "rollup-plugin-esbuild";
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// 🎨 STYLING & ANIMATIONS
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
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 = {
|
|
||||||
// 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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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}`);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
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}`,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
this.table(["Metric", "Value"], rows);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// 🚀 MAIN EXECUTION
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
const args = argv.slice(2);
|
const args = argv.slice(2);
|
||||||
console.clear();
|
console.clear();
|
||||||
|
|
||||||
// Parse arguments
|
|
||||||
const inputPlugins = args.filter((x) => !x.startsWith("-"));
|
const inputPlugins = args.filter((x) => !x.startsWith("-"));
|
||||||
const flags = args.map((x) => x.toLowerCase()).filter((x) => x.startsWith("-"));
|
const flags = args.map((x) => x.toLowerCase()).filter((x) => x.startsWith("-"));
|
||||||
|
|
||||||
const isWatch = flags.includes("--watch") || flags.includes("-w");
|
const isWatch = flags.includes("--watch") || flags.includes("-w");
|
||||||
const isVerbose = flags.includes("--verbose") || flags.includes("-v");
|
|
||||||
const toBuild = inputPlugins.length ? inputPlugins : await readdir("./plugins");
|
const toBuild = inputPlugins.length ? inputPlugins : await readdir("./plugins");
|
||||||
|
|
||||||
// Display banner
|
console.log(`Building ${toBuild.length} plugin(s)...`);
|
||||||
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;
|
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;
|
|
||||||
|
|
||||||
|
for (const plugin of toBuild) {
|
||||||
try {
|
try {
|
||||||
if (!plugin.endsWith(".ts")) {
|
await buildPlugin(plugin);
|
||||||
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) : "",
|
|
||||||
);
|
|
||||||
|
|
||||||
buildResults.push({
|
|
||||||
name: plugin,
|
|
||||||
status: "success",
|
|
||||||
time: buildTime,
|
|
||||||
size: result.size || 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
built++;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (spinner) spinner.stop();
|
console.error(e.stack || e);
|
||||||
|
|
||||||
logger.plugin.error(plugin, isVerbose ? e.stack : e.message);
|
|
||||||
|
|
||||||
buildResults.push({
|
|
||||||
name: plugin,
|
|
||||||
status: "error",
|
|
||||||
time: Date.now() - pluginStartTime,
|
|
||||||
error: e.message,
|
|
||||||
});
|
|
||||||
|
|
||||||
failed++;
|
failed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final progress
|
console.log(
|
||||||
if (!isWatch) {
|
"\n" +
|
||||||
console.log(
|
(failed
|
||||||
`\n ${createProgressBar(toBuild.length, toBuild.length)} ${colors.dim}(${toBuild.length}/${toBuild.length})${colors.reset}`,
|
? "\x1b[31m" + `Failed to build ${failed} plugin(s)` + "\x1b[0m"
|
||||||
);
|
: "\x1b[32m" + "All plugin(s) built successfully!" + "\x1b[0m"),
|
||||||
}
|
);
|
||||||
|
|
||||||
// Summary
|
isWatch && console.log("\nWatching for changes...");
|
||||||
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) {
|
async function buildPlugin(plugin) {
|
||||||
if (plugin.endsWith(".ts")) return {};
|
if (plugin.endsWith(".ts")) return;
|
||||||
|
|
||||||
const manifest = Object.assign(
|
const manifest = Object.assign(
|
||||||
JSON.parse(await readFile("./base_manifest.json")),
|
JSON.parse(await readFile("./base_manifest.json")),
|
||||||
|
|
@ -554,16 +72,11 @@ async function buildPlugin(plugin) {
|
||||||
inlineDynamicImports: true,
|
inlineDynamicImports: true,
|
||||||
},
|
},
|
||||||
onwarn: (warning) => {
|
onwarn: (warning) => {
|
||||||
if (
|
![
|
||||||
![
|
"UNRESOLVED_IMPORT",
|
||||||
"UNRESOLVED_IMPORT",
|
"MISSING_NAME_OPTION_FOR_IIFE_EXPORT",
|
||||||
"MISSING_NAME_OPTION_FOR_IIFE_EXPORT",
|
"CIRCULAR_DEPENDENCY",
|
||||||
"CIRCULAR_DEPENDENCY",
|
].includes(warning.code) && console.warn(warning);
|
||||||
].includes(warning.code) &&
|
|
||||||
isVerbose
|
|
||||||
) {
|
|
||||||
logger.warning(`${plugin}: ${warning.message}`);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
nodeResolve({
|
nodeResolve({
|
||||||
|
|
@ -593,13 +106,13 @@ async function buildPlugin(plugin) {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyHash = async () => {
|
const applyHash = async () =>
|
||||||
const content = await readFile(outPath);
|
Object.assign(manifest, {
|
||||||
return Object.assign(manifest, {
|
hash: createHash("sha256")
|
||||||
hash: createHash("sha256").update(content).digest("hex"),
|
.update(await readFile(outPath))
|
||||||
|
.digest("hex"),
|
||||||
main: entry,
|
main: entry,
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
if (!isWatch) {
|
if (!isWatch) {
|
||||||
const bundle = await rollup(options);
|
const bundle = await rollup(options);
|
||||||
|
|
@ -609,9 +122,8 @@ async function buildPlugin(plugin) {
|
||||||
await applyHash();
|
await applyHash();
|
||||||
await writeFile(`./dist/${plugin}/manifest.json`, JSON.stringify(manifest));
|
await writeFile(`./dist/${plugin}/manifest.json`, JSON.stringify(manifest));
|
||||||
|
|
||||||
// Get file size
|
console.log(`${plugin}: ` + "\x1b[32m" + "Build succeed!" + "\x1b[0m");
|
||||||
const stats = await stat(outPath);
|
return;
|
||||||
return { size: stats.size };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const watcher = watch(options);
|
const watcher = watch(options);
|
||||||
|
|
@ -619,10 +131,6 @@ async function buildPlugin(plugin) {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
watcher.on("event", async (event) => {
|
watcher.on("event", async (event) => {
|
||||||
switch (event.code) {
|
switch (event.code) {
|
||||||
case "BUNDLE_START":
|
|
||||||
// Silent in watch mode, spinner handles this
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "BUNDLE_END": {
|
case "BUNDLE_END": {
|
||||||
event.result.close();
|
event.result.close();
|
||||||
|
|
||||||
|
|
@ -632,17 +140,18 @@ async function buildPlugin(plugin) {
|
||||||
JSON.stringify(manifest),
|
JSON.stringify(manifest),
|
||||||
);
|
);
|
||||||
|
|
||||||
const buildTime = formatTime(event.duration || 0);
|
console.log(
|
||||||
logger.plugin.watching(plugin, buildTime);
|
`${plugin}: ` +
|
||||||
resolve({ size: 0 });
|
"\x1b[32m" +
|
||||||
|
`Build succeed! (${event.duration}ms)` +
|
||||||
|
"\x1b[0m",
|
||||||
|
);
|
||||||
|
resolve();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "ERROR":
|
case "ERROR":
|
||||||
case "FATAL":
|
case "FATAL":
|
||||||
logger.plugin.error(
|
console.error(`${plugin}: ` + "\x1b[31m", "Failed! :(", "\x1b[0m");
|
||||||
plugin,
|
|
||||||
isVerbose ? event.error.stack : event.error.message,
|
|
||||||
);
|
|
||||||
reject(event.error);
|
reject(event.error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,557 +1,18 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
import { spawn } from "child_process";
|
||||||
import { exec, spawn } from "child_process";
|
|
||||||
import { networkInterfaces } from "os";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { readdir, stat, access } from "fs/promises";
|
|
||||||
import { createHash } from "crypto";
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
// 🎨 STYLING & ANIMATIONS
|
const distPath = path.resolve(__dirname, "..", "dist");
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
const port = process.env.PORT || 1400;
|
||||||
|
|
||||||
const colors = {
|
const server = spawn("npx", ["serve", "-s", distPath, "-l", port.toString()], {
|
||||||
reset: "\x1b[0m",
|
stdio: "inherit",
|
||||||
bold: "\x1b[1m",
|
shell: process.platform === "win32",
|
||||||
dim: "\x1b[2m",
|
});
|
||||||
italic: "\x1b[3m",
|
|
||||||
underline: "\x1b[4m",
|
process.on("SIGINT", () => {
|
||||||
|
server.kill();
|
||||||
// Standard colors
|
process.exit();
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue