import { serve } from 'bun';
import { readFileSync, existsSync } from 'fs';
import { join, extname } from 'path';
const PORT = process.env.PORT || 3000;
const isDev = process.env.NODE_ENV !== 'production';
const mimeTypes = {
'.html': 'text/html',
'.js': 'application/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.ttf': 'font/ttf',
'.eot': 'application/vnd.ms-fontobject',
};
const htmlTemplate = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scribe Frontend Development</title>
<style>
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #f5f5f5;
}
.dev-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.dev-header {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.dev-content {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
min-height: 400px;
}
.status {
color: #28a745;
font-weight: bold;
}
.error {
color: #dc3545;
background: #f8d7da;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
color: #6c757d;
}
</style>
</head>
<body>
<div class="dev-container">
<div class="dev-header">
<h1>Scribe Frontend Development Server</h1>
<p class="status">✅ Development server running on port ${PORT}</p>
<p>This server provides hot reloading for React Arborist component development.</p>
</div>
<div class="dev-content">
<div id="root">
<div class="loading">Loading React components...</div>
</div>
</div>
</div>
<!-- Load the compiled bundle -->
<script type="module" src="/dist/index.js"></script>
${isDev ? `
<!-- Development hot reload script -->
<script>
// Simple hot reload for development
let lastModified = Date.now();
function checkForUpdates() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
if (data.lastModified > lastModified) {
console.log('🔄 Reloading due to file changes...');
window.location.reload();
}
lastModified = data.lastModified;
})
.catch(error => {
console.warn('Hot reload check failed:', error);
});
}
// Check for updates every 1 second in development
setInterval(checkForUpdates, 1000);
// Also reload on focus (in case file changed while away)
window.addEventListener('focus', checkForUpdates);
console.log('🚀 Development mode: Hot reload enabled');
</script>
` : ''}
</body>
</html>
`;
let lastModified = Date.now();
const server = serve({
port: PORT,
async fetch(request) {
const url = new URL(request.url);
const pathname = url.pathname;
if (pathname === '/api/status') {
return new Response(JSON.stringify({
status: 'ok',
lastModified,
timestamp: Date.now(),
}), {
headers: { 'Content-Type': 'application/json' },
});
}
if (isDev) {
lastModified = Date.now();
}
if (pathname === '/' || pathname === '/index.html') {
return new Response(htmlTemplate, {
headers: { 'Content-Type': 'text/html' },
});
}
if (pathname.startsWith('/dist/')) {
const filePath = join(process.cwd(), pathname.slice(1));
try {
if (existsSync(filePath)) {
const file = readFileSync(filePath);
const ext = extname(filePath);
const mimeType = mimeTypes[ext] || 'application/octet-stream';
return new Response(file, {
headers: {
'Content-Type': mimeType,
'Cache-Control': isDev ? 'no-cache' : 'public, max-age=31536000',
},
});
}
} catch (error) {
console.error('Error serving file:', error);
}
}
if (isDev && pathname.startsWith('/src/')) {
const filePath = join(process.cwd(), pathname.slice(1));
try {
if (existsSync(filePath)) {
const file = readFileSync(filePath);
const ext = extname(filePath);
const mimeType = mimeTypes[ext] || 'text/plain';
return new Response(file, {
headers: {
'Content-Type': mimeType,
'Cache-Control': 'no-cache',
},
});
}
} catch (error) {
console.error('Error serving source file:', error);
}
}
return new Response('Not Found', {
status: 404,
headers: { 'Content-Type': 'text/plain' },
});
},
error(error) {
console.error('Server error:', error);
return new Response('Internal Server Error', {
status: 500,
headers: { 'Content-Type': 'text/plain' },
});
},
});
console.log(`🚀 Development server running at http://localhost:${PORT}`);
console.log(`📁 Serving from: ${process.cwd()}`);
console.log(`🔥 Hot reload: ${isDev ? 'enabled' : 'disabled'}`);
process.on('SIGINT', () => {
console.log('\n🛑 Shutting down development server...');
server.stop();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\n🛑 Shutting down development server...');
server.stop();
process.exit(0);
});