From e18641ff52dc242d05271b3fb58929b21d72f60e Mon Sep 17 00:00:00 2001 From: GuavaPopper Date: Fri, 5 Jun 2026 17:55:51 +0700 Subject: [PATCH] fix: run RODA with Vite dev --- groups/RODA/package-lock.json | 13 ----- groups/RODA/package.json | 15 +----- package.json | 2 +- scripts/dev-runner.js | 94 +++++++++++++++++++++++++++++++++++ scripts/dev.js | 3 ++ vite.config.js | 71 ++++++-------------------- 6 files changed, 116 insertions(+), 82 deletions(-) delete mode 100644 groups/RODA/package-lock.json create mode 100644 scripts/dev-runner.js create mode 100644 scripts/dev.js diff --git a/groups/RODA/package-lock.json b/groups/RODA/package-lock.json deleted file mode 100644 index 2155203..0000000 --- a/groups/RODA/package-lock.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "roda", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "roda", - "version": "1.0.0", - "license": "ISC" - } - } -} diff --git a/groups/RODA/package.json b/groups/RODA/package.json index b0606c6..5bbefff 100644 --- a/groups/RODA/package.json +++ b/groups/RODA/package.json @@ -1,14 +1,3 @@ { - "name": "roda", - "version": "1.0.0", - "description": "Project website untuk menampilkan sorotan dan prestasi karya mahasiswa program studi Informatika Universitas Tanjungpura.", - "main": "server.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server.js", - "dev": "node server.js" - }, - "keywords": [], - "author": "", - "license": "ISC" -} \ No newline at end of file + "type": "commonjs" +} diff --git a/package.json b/package.json index b8879f2..4c0e3a0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "description": "Student-safe static page assignment kit for Informatika UNTAN", "scripts": { - "dev": "node groups/RODA/server.js", + "dev": "node scripts/dev.js", "dev:vite": "vite", "dev:roda": "node groups/RODA/server.js", "build": "vite build", diff --git a/scripts/dev-runner.js b/scripts/dev-runner.js new file mode 100644 index 0000000..4a85906 --- /dev/null +++ b/scripts/dev-runner.js @@ -0,0 +1,94 @@ +import { spawn } from 'node:child_process'; + +export function createDevProcessSpecs({ + nodeCommand = process.execPath, + env = process.env, +} = {}) { + return { + roda: { + command: nodeCommand, + args: ['groups/RODA/server.js'], + options: { + env: { + ...env, + HOST: env.RODA_HOST || '127.0.0.1', + PORT: env.RODA_PORT || '3000', + }, + stdio: ['ignore', 'ignore', 'pipe'], + }, + }, + vite: { + command: nodeCommand, + args: ['node_modules/vite/bin/vite.js'], + options: { + env: { ...env }, + stdio: 'inherit', + }, + }, + }; +} + +export function startDevServers({ + spawnProcess = spawn, + specs = createDevProcessSpecs(), +} = {}) { + const roda = spawnProcess(specs.roda.command, specs.roda.args, specs.roda.options); + const vite = spawnProcess(specs.vite.command, specs.vite.args, specs.vite.options); + let shuttingDown = false; + let rodaErrorOutput = ''; + + roda.stderr?.on('data', (chunk) => { + const message = String(chunk); + rodaErrorOutput += message; + if (!message.includes('EADDRINUSE')) { + process.stderr.write(`[roda] ${message}`); + } + }); + + function stopChildren() { + shuttingDown = true; + if (!roda.killed) { + roda.kill(); + } + if (!vite.killed) { + vite.kill(); + } + } + + vite.on('exit', (code, signal) => { + shuttingDown = true; + if (!roda.killed) { + roda.kill(); + } + + if (signal) { + process.exitCode = 1; + return; + } + + process.exitCode = code ?? 0; + }); + + roda.on('exit', (code, signal) => { + if (shuttingDown) { + return; + } + + if (rodaErrorOutput.includes('EADDRINUSE')) { + process.stderr.write('[roda] Port 3000 is already in use; continuing with the existing RODA server.\n'); + return; + } + + const reason = signal ? `signal ${signal}` : `exit code ${code}`; + process.stderr.write(`[roda] server stopped unexpectedly (${reason})\n`); + if (!vite.killed) { + vite.kill(); + } + process.exitCode = code || 1; + }); + + process.once('SIGINT', stopChildren); + process.once('SIGTERM', stopChildren); + + return { roda, vite }; +} diff --git a/scripts/dev.js b/scripts/dev.js new file mode 100644 index 0000000..73df19f --- /dev/null +++ b/scripts/dev.js @@ -0,0 +1,3 @@ +import { startDevServers } from './dev-runner.js'; + +startDevServers(); diff --git a/vite.config.js b/vite.config.js index f076183..941ca2a 100644 --- a/vite.config.js +++ b/vite.config.js @@ -10,9 +10,11 @@ const rodaDashboardCacheTtlMs = 5 * 60 * 1000; const dashboardRequestHeaders = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36', }; +const rodaHost = process.env.RODA_HOST || '127.0.0.1'; +const rodaPort = process.env.RODA_PORT || '3000'; +const rodaTarget = `http://${rodaHost}:${rodaPort}`; let rodaDashboardHtmlCache; -let rodaDashboardDevCache = { html: '', timestamp: 0 }; function collectHtmlInputs(dir) { const inputs = {}; @@ -124,61 +126,20 @@ function embedRodaDashboardData() { }; } -function serveRodaDashboardApi() { - return { - name: 'serve-roda-dashboard-api', - configureServer(server) { - server.middlewares.use(async (req, res, next) => { - const requestUrl = new URL(req.url || '/', 'http://localhost'); - if (requestUrl.pathname !== '/api/dashboard') { - next(); - return; - } - - if (req.method !== 'GET' && req.method !== 'HEAD') { - res.writeHead(405, { 'Content-Type': 'text/plain; charset=utf-8' }); - res.end('METHOD_NOT_ALLOWED'); - return; - } - - const now = Date.now(); - try { - let html = rodaDashboardDevCache.html; - let cacheStatus = 'HIT'; - - if (!html || (now - rodaDashboardDevCache.timestamp) >= rodaDashboardCacheTtlMs) { - html = await requestRodaDashboardHtml(); - rodaDashboardDevCache = { html, timestamp: Date.now() }; - cacheStatus = 'MISS'; - } - - res.writeHead(200, { - 'Content-Type': 'text/html; charset=utf-8', - 'X-Cache': cacheStatus, - }); - res.end(req.method === 'HEAD' ? '' : html); - } catch (error) { - if (rodaDashboardDevCache.html) { - res.writeHead(200, { - 'Content-Type': 'text/html; charset=utf-8', - 'X-Cache': 'STALE', - }); - res.end(req.method === 'HEAD' ? '' : rodaDashboardDevCache.html); - return; - } - - const message = error instanceof Error ? error.message : String(error); - console.warn(`[roda-dashboard] Dev dashboard fetch failed: ${message}`); - res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' }); - res.end('DASHBOARD_FETCH_FAILED'); - } - }); - }, - }; -} - export default defineConfig({ - plugins: [serveRodaDashboardApi(), embedRodaDashboardData(), copyStaticAssets()], + plugins: [embedRodaDashboardData(), copyStaticAssets()], + server: { + proxy: { + '/groups/RODA/api/dashboard': { + target: rodaTarget, + changeOrigin: true, + }, + '/api/dashboard': { + target: rodaTarget, + changeOrigin: true, + }, + }, + }, build: { rollupOptions: { input: collectHtmlInputs(root),