debug-time-machine
Version:
제로 설정 React 디버깅 도구
409 lines (394 loc) • 16.7 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/cli.ts
var cli_exports = {};
__export(cli_exports, {
startDebugTimeMachine: () => startDebugTimeMachine
});
module.exports = __toCommonJS(cli_exports);
var import_child_process = require("child_process");
var import_fs = require("fs");
var import_path = __toESM(require("path"));
var colors = {
red: (text) => `\x1B[31m${text}\x1B[0m`,
green: (text) => `\x1B[32m${text}\x1B[0m`,
yellow: (text) => `\x1B[33m${text}\x1B[0m`,
blue: (text) => `\x1B[34m${text}\x1B[0m`,
cyan: (text) => `\x1B[36m${text}\x1B[0m`,
gray: (text) => `\x1B[90m${text}\x1B[0m`,
bold: (text) => `\x1B[1m${text}\x1B[0m`
};
var runningProcesses = [];
async function findProjectPath() {
const possiblePaths = [
import_path.default.join(__dirname, "..", "..", ".."),
// packages/unified에서 3단계 위
process.cwd(),
// 현재 작업 디렉토리
import_path.default.join(process.cwd(), "..", "debugTimeMachine"),
// 상위 폴더의 debugTimeMachine
import_path.default.join(process.env.HOME || "", "Desktop", "Project", "debugTimeMachine")
// 일반적인 위치
];
for (const testPath of possiblePaths) {
try {
const backendExists = await import_fs.promises.stat(import_path.default.join(testPath, "apps", "backend", "package.json")).then((stat) => stat.isFile()).catch(() => false);
const frontendExists = await import_fs.promises.stat(import_path.default.join(testPath, "apps", "frontend", "package.json")).then((stat) => stat.isFile()).catch(() => false);
if (backendExists && frontendExists) {
console.log(colors.gray(` \u2705 \uD504\uB85C\uC81D\uD2B8 \uACBD\uB85C \uBC1C\uACAC: ${testPath}`));
return testPath;
}
} catch (e) {
}
}
throw new Error("Debug Time Machine \uD504\uB85C\uC81D\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. apps/backend\uC640 apps/frontend \uD3F4\uB354\uAC00 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.");
}
async function waitForServer(port, maxAttempts = 60) {
console.log(colors.gray(` ${port} \uD3EC\uD2B8\uC5D0\uC11C \uC11C\uBC84 \uC751\uB2F5 \uB300\uAE30 \uC911...`));
for (let i = 0; i < maxAttempts; i++) {
try {
const checkPath = port === 4e3 ? "/health" : "/";
const response = await fetch(`http://localhost:${port}${checkPath}`, {
signal: AbortSignal.timeout(2e3)
});
if (response.ok) {
console.log(colors.green(` \u2705 \uD3EC\uD2B8 ${port} \uC900\uBE44 \uC644\uB8CC!`));
return true;
}
} catch (error) {
if (i > 0 && i % 15 === 0) {
console.log(colors.gray(` \uC544\uC9C1 \uB300\uAE30 \uC911... (${i}/${maxAttempts}\uCD08)`));
}
}
await new Promise((resolve) => setTimeout(resolve, 1e3));
}
console.log(colors.red(` \u274C \uD3EC\uD2B8 ${port} \uC11C\uBC84\uAC00 ${maxAttempts}\uCD08 \uD6C4\uC5D0\uB3C4 \uC900\uBE44\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`));
return false;
}
async function openBrowser(url) {
try {
const platform = process.platform;
let command;
if (platform === "darwin") {
command = "open";
} else if (platform === "win32") {
command = "start";
} else {
command = "xdg-open";
}
(0, import_child_process.spawn)(command, [url], { detached: true, stdio: "ignore" });
} catch (error) {
console.log(colors.yellow(`\uBE0C\uB77C\uC6B0\uC800\uB97C \uC790\uB3D9\uC73C\uB85C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC218\uB3D9\uC73C\uB85C \uC5F4\uC5B4\uC8FC\uC138\uC694: ${url}`));
}
}
function setupProcessCleanup() {
const cleanup = () => {
console.log(colors.yellow("\n\u{1F9F9} \uD504\uB85C\uC138\uC2A4 \uC815\uB9AC \uC911..."));
runningProcesses.forEach(({ name, process: process2 }) => {
if (process2 && !process2.killed) {
console.log(colors.gray(` \u274C ${name} \uC885\uB8CC \uC911...`));
process2.kill("SIGTERM");
setTimeout(() => {
if (!process2.killed) {
process2.kill("SIGKILL");
}
}, 5e3);
}
});
console.log(colors.green("\u2705 \uBAA8\uB4E0 \uD504\uB85C\uC138\uC2A4\uAC00 \uC815\uB9AC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
process.exit(0);
};
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
process.on("beforeExit", cleanup);
}
async function startBackendServer(projectPath) {
console.log(colors.blue("\u{1F680} \uBC31\uC5D4\uB4DC \uC11C\uBC84 \uC2DC\uC791 \uC911..."));
const backendPath = import_path.default.join(projectPath, "apps", "backend");
try {
await import_fs.promises.stat(import_path.default.join(backendPath, "package.json"));
} catch (error) {
throw new Error(`\uBC31\uC5D4\uB4DC \uD504\uB85C\uC81D\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${backendPath}`);
}
const backendProcess = (0, import_child_process.spawn)("npm", ["run", "dev"], {
cwd: backendPath,
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env, PORT: "4000" },
shell: true
});
const processInfo = {
name: "Backend Server",
process: backendProcess,
port: 4e3,
url: "http://localhost:4000"
};
backendProcess.stdout?.on("data", (data) => {
const message = data.toString().trim();
if (message) {
console.log(colors.gray(`[Backend] ${message}`));
}
});
backendProcess.stderr?.on("data", (data) => {
const message = data.toString().trim();
if (message && !message.includes("warning")) {
console.log(colors.red(`[Backend Error] ${message}`));
}
});
backendProcess.on("error", (error) => {
console.error(colors.red(`\uBC31\uC5D4\uB4DC \uC11C\uBC84 \uC624\uB958: ${error.message}`));
});
backendProcess.on("exit", (code) => {
if (code !== 0 && code !== null) {
console.log(colors.yellow(`\uBC31\uC5D4\uB4DC \uC11C\uBC84\uAC00 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCF54\uB4DC: ${code}`));
}
});
runningProcesses.push(processInfo);
return processInfo;
}
async function startFrontendUI(projectPath) {
console.log(colors.blue("\u{1F3A8} Debug UI \uC2DC\uC791 \uC911..."));
const serverCode = `
const express = require('express');
const path = require('path');
const cors = require('cors');
const fs = require('fs');
const app = express();
const port = process.env.PORT || 8080;
app.use(cors());
// \uC5EC\uB7EC \uAC00\uB2A5\uD55C frontend \uACBD\uB85C \uC2DC\uB3C4
const possiblePaths = [
path.join(__dirname, '..', '..', '..', 'apps', 'frontend', 'dist'),
path.join(process.cwd(), 'apps', 'frontend', 'dist'),
path.join(process.cwd(), '..', 'debugTimeMachine', 'apps', 'frontend', 'dist'),
path.join(process.env.HOME, 'Desktop', 'Project', 'debugTimeMachine', 'apps', 'frontend', 'dist')
];
let frontendDistPath = null;
for (const testPath of possiblePaths) {
try {
if (fs.existsSync(testPath) && fs.existsSync(path.join(testPath, 'index.html'))) {
frontendDistPath = testPath;
console.log('Frontend \uACBD\uB85C \uBC1C\uACAC: ' + frontendDistPath);
break;
}
} catch (e) {
// \uACBD\uB85C \uC811\uADFC \uC2E4\uD328, \uB2E4\uC74C \uACBD\uB85C \uC2DC\uB3C4
}
}
if (frontendDistPath) {
// \uC2E4\uC81C frontend \uC571 \uC11C\uBE59
app.use(express.static(frontendDistPath));
app.get('*', (req, res) => {
res.sendFile(path.join(frontendDistPath, 'index.html'));
});
console.log('\u2705 Frontend \uC571\uC744 \uC11C\uBE59 \uC911: ' + frontendDistPath);
} else {
// Fallback HTML \uC81C\uACF5
const fallbackHTML = \`
<!DOCTYPE html>
<html>
<head>
<title>Debug Time Machine</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
margin: 0; padding: 40px; background: #f5f5f5; text-align: center;
}
.container {
max-width: 600px; margin: 0 auto; background: white;
border-radius: 12px; padding: 40px; box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.error { color: #e74c3c; margin: 20px 0; }
.info { color: #3498db; margin: 20px 0; line-height: 1.6; }
code { background: #f8f9fa; padding: 4px 8px; border-radius: 4px; font-family: monospace; }
.paths { text-align: left; margin: 20px 0; background: #f8f9fa; padding: 20px; border-radius: 8px; }
.paths h4 { margin-top: 0; }
</style>
</head>
<body>
<div class="container">
<h1>\u{1F570}\uFE0F Debug Time Machine</h1>
<div class="error">
<h3>Frontend \uBE4C\uB4DC \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4</h3>
</div>
<div class="paths">
<h4>\uD655\uC778\uB41C \uACBD\uB85C\uB4E4:</h4>
\${possiblePaths.map(p => '<div><code>' + p + '</code></div>').join('')}
</div>
<div class="info">
<p><strong>\uBC31\uC5D4\uB4DC\uB294 \uC815\uC0C1\uC801\uC73C\uB85C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4:</strong></p>
<p>WebSocket: <code>ws://localhost:4000/ws</code></p>
<p>Health Check: <code>http://localhost:4000/health</code></p>
<p>React Hook\uC744 \uC0AC\uC6A9\uD558\uBA74 \uBC31\uC5D4\uB4DC\uC5D0 \uC790\uB3D9\uC73C\uB85C \uC5F0\uACB0\uB429\uB2C8\uB2E4.</p>
</div>
</div>
</body>
</html>
\`;
app.get('*', (req, res) => {
res.send(fallbackHTML);
});
console.log('\u26A0\uFE0F Frontend \uBE4C\uB4DC \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 fallback UI\uB97C \uC81C\uACF5\uD569\uB2C8\uB2E4.');
}
app.listen(port, () => {
console.log('\u{1F3A8} Debug Time Machine UI\uAC00 \uD3EC\uD2B8 ' + port + '\uC5D0\uC11C \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4.');
console.log('URL: http://localhost:' + port);
});
`;
const tempUIPath = import_path.default.join(process.cwd(), ".debug-time-machine-ui.js");
await import_fs.promises.writeFile(tempUIPath, serverCode);
const frontendProcess = (0, import_child_process.spawn)("node", [tempUIPath], {
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env, PORT: "8080" },
shell: false
});
const processInfo = {
name: "Debug UI",
process: frontendProcess,
port: 8080,
url: "http://localhost:8080"
};
frontendProcess.stdout?.on("data", (data) => {
const message = data.toString().trim();
if (message) {
console.log(colors.gray(`[Debug UI] ${message}`));
}
});
frontendProcess.stderr?.on("data", (data) => {
const message = data.toString().trim();
if (message && !message.includes("warning")) {
console.log(colors.red(`[Debug UI Error] ${message}`));
}
});
frontendProcess.on("error", (error) => {
console.error(colors.red(`Debug UI \uC624\uB958: ${error.message}`));
});
frontendProcess.on("exit", (code) => {
import_fs.promises.unlink(tempUIPath).catch(() => {
});
if (code !== 0 && code !== null) {
console.log(colors.yellow(`Debug UI\uAC00 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCF54\uB4DC: ${code}`));
}
});
runningProcesses.push(processInfo);
return processInfo;
}
async function startDebugTimeMachine() {
console.log(colors.bold(colors.green("\u{1F570}\uFE0F Debug Time Machine \uC2DC\uC791!")));
console.log("");
try {
setupProcessCleanup();
const projectPath = await findProjectPath();
const backend = await startBackendServer(projectPath);
const frontend = await startFrontendUI(projectPath);
console.log(colors.blue("\u23F3 \uC11C\uBC84 \uC900\uBE44 \uB300\uAE30 \uC911..."));
const backendReady = await waitForServer(backend.port);
if (!backendReady) {
throw new Error("\uBC31\uC5D4\uB4DC \uC11C\uBC84\uAC00 \uC900\uBE44\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
}
const frontendReady = await waitForServer(frontend.port);
if (!frontendReady) {
throw new Error("Debug UI \uC11C\uBC84\uAC00 \uC900\uBE44\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
}
console.log(colors.blue("\u{1F310} Debug UI \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uAE30..."));
await openBrowser(frontend.url);
console.log("");
console.log(colors.bold(colors.green("\u{1F389} Debug Time Machine\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC2DC\uC791\uB418\uC5C8\uC2B5\uB2C8\uB2E4!")));
console.log("");
console.log(colors.bold("\u{1F4CD} \uC11C\uBE44\uC2A4 \uC8FC\uC18C:"));
console.log(` \u{1F41B} Debug UI: ${colors.cyan(frontend.url)}`);
console.log(` \u{1F527} Backend: ${colors.cyan(backend.url)}`);
console.log("");
console.log(colors.bold("\u{1F4A1} \uC0AC\uC6A9\uBC95:"));
console.log(` \u2022 React \uC571\uC5D0\uC11C ${colors.cyan("useDebugTimeMachine()")} Hook\uC744 \uC0AC\uC6A9\uD558\uC138\uC694`);
console.log(" \u2022 localhost:4000\uC5D0 \uC790\uB3D9\uC73C\uB85C \uC5F0\uACB0\uB429\uB2C8\uB2E4");
console.log(" \u2022 Debug UI\uC5D0\uC11C \uC2E4\uC2DC\uAC04 \uC774\uBCA4\uD2B8\uB97C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
console.log("");
console.log(colors.gray("\u26A0\uFE0F \uC885\uB8CC\uD558\uB824\uBA74 Ctrl+C\uB97C \uB204\uB974\uC138\uC694."));
console.log("");
await new Promise((resolve) => {
process.on("SIGINT", () => resolve());
process.on("SIGTERM", () => resolve());
});
} catch (error) {
console.error(colors.red("\u274C Error:"), error instanceof Error ? error.message : String(error));
runningProcesses.forEach(({ process: process2 }) => {
if (process2 && !process2.killed) {
process2.kill("SIGTERM");
}
});
process.exit(1);
}
}
function showHelp() {
console.log(colors.bold("\u{1F570}\uFE0F Debug Time Machine CLI"));
console.log("");
console.log(colors.bold("\uC0AC\uC6A9\uBC95:"));
console.log(" debug-time-machine [start] Debug Time Machine \uC11C\uBC84 \uC2DC\uC791");
console.log(" debug-time-machine --help \uB3C4\uC6C0\uB9D0 \uBCF4\uAE30");
console.log("");
console.log(colors.bold("\uC608\uC2DC:"));
console.log(" debug-time-machine # \uC11C\uBC84 \uC2DC\uC791");
console.log(" debug-time-machine start # \uC11C\uBC84 \uC2DC\uC791");
console.log("");
console.log(colors.bold("\uC11C\uBC84 \uC8FC\uC18C:"));
console.log(" Backend: http://localhost:4000");
console.log(" Debug UI: http://localhost:8080");
console.log("");
console.log(colors.bold("\uC694\uAD6C\uC0AC\uD56D:"));
console.log(" \u2022 Node.js 18+");
console.log(" \u2022 npm \uB610\uB294 pnpm");
console.log(" \u2022 apps/backend\uC640 apps/frontend \uD3F4\uB354");
console.log("");
}
function main() {
const args = process.argv.slice(2);
const command = args[0];
switch (command) {
case void 0:
case "start":
startDebugTimeMachine();
break;
case "--help":
case "-h":
showHelp();
break;
default:
console.error(colors.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${command}`));
console.log(colors.yellow("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBA85\uB839\uC5B4: start, --help"));
process.exit(1);
}
}
if (typeof require !== "undefined" && require.main === module) {
main();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
startDebugTimeMachine
});