#!/usr/bin/env node import { execSync, spawn } from 'child_process'; import { setTimeout } from 'timers/promises'; import fs from 'fs'; import path from 'path'; const isWin = process.platform === 'win32'; const npmCmd = isWin ? 'npm.cmd' : 'npm'; const npxCmd = isWin ? 'npx.cmd' : 'npx'; // Skip build if SKIP_BUILD is set OR if dist/index.html already exists (called from npm run build) if (!process.env.SKIP_BUILD && !fs.existsSync(path.join('dist', 'index.html'))) { console.log('Building the React app...'); try { execSync(`${npmCmd} run build`, { stdio: 'inherit', shell: isWin }); } catch (error) { console.error('Build failed:', error.message); process.exit(1); } } else { console.log('Skipping build (already built or SKIP_BUILD=1)'); } // Copy server.js and server folder to dist for iisnode console.log('Copying server files to dist...'); try { // Create dist-specific server.js for iisnode (serves from __dirname, not dist subfolder) const distServerJs = `import express from 'express'; import path from 'path'; import { fileURLToPath } from 'url'; import seoMiddleware from './server/seo-middleware.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); const PORT = process.env.PORT || 3000; // Configure MIME types for JSX files app.use((req, res, next) => { if (req.path.endsWith('.jsx')) { res.setHeader('Content-Type', 'application/javascript'); } next(); }); // Serve static assets FIRST (CSS, JS, images, fonts) - these MUST work app.use('/assets', express.static(path.join(__dirname, 'assets'))); app.use('/src', express.static(path.join(__dirname, '../src'))); app.use('/og', express.static(path.join(__dirname, 'og'))); app.use(express.static(__dirname, { index: false, // Don't serve index.html automatically setHeaders: (res, filePath) => { // Only block HTML files - let everything else through if (filePath.endsWith('.html')) { res.status(404).end(); return; } } })); // CRITICAL: Apply SEO middleware AFTER static files // This ensures HTML requests go through SEO middleware // But static assets are served first app.use(seoMiddleware); app.listen(PORT, () => { console.log(\`Server running on port \${PORT}\`); console.log('Dynamic SEO enabled!'); }); export default app; `; fs.writeFileSync(path.join('dist', 'server.js'), distServerJs, 'utf8'); // Copy server folder to dist if (!fs.existsSync(path.join('dist', 'server'))) { fs.mkdirSync(path.join('dist', 'server')); } // Copy server.js from server folder (if it exists) if (fs.existsSync(path.join('server', 'server.js'))) { fs.copyFileSync( path.join('server', 'server.js'), path.join('dist', 'server', 'server.js') ); } // Create dist-specific seo-middleware.js with correct paths const seoMiddlewareJs = fs.readFileSync(path.join('server', 'seo-middleware.js'), 'utf8'); // Replace the path from ../dist to .. for dist deployment (handle all variations) const distSeoMiddlewareJs = seoMiddlewareJs .replace(/path\.join\(__dirname, '\.\.\/dist'/g, "path.join(__dirname, '..'") .replace(/path\.join\(__dirname, '\.\.', 'dist'/g, "path.join(__dirname, '..'") .replace(/path\.join\(__dirname, '\.\.\/dist',/g, "path.join(__dirname, '..',") .replace(/path\.join\(__dirname, '\.\.\/dist"/g, "path.join(__dirname, '..\"") .replace(/path\.join\(__dirname, '\.\.\/dist',\s*req\.path/g, "path.join(__dirname, '..', req.path"); fs.writeFileSync(path.join('dist', 'server', 'seo-middleware.js'), distSeoMiddlewareJs, 'utf8'); console.log('Server files copied successfully!'); } catch (error) { console.error('Failed to copy server files:', error.message); process.exit(1); } if (process.env.PRERENDER === '1') { let server; try { console.log('Starting development server for prerender...'); server = spawn(npmCmd, ['run', 'dev'], { stdio: 'inherit', shell: isWin }); // Wait for server to start await setTimeout(5000); console.log('Prerendering pages with react-snap...'); execSync(`${npxCmd} react-snap`, { stdio: 'inherit', shell: isWin }); } catch (error) { console.warn('Prerendering skipped due to error (continuing to SEO):', error?.message || error); } finally { if (server) { console.log('Stopping development server...'); server.kill(); } } } else { console.log('Skipping react-snap prerender (set PRERENDER=1 to enable)'); } console.log('Applying SEO optimizations...'); try { execSync('node scripts/generate-seo.js', { stdio: 'inherit', shell: isWin }); } catch (error) { console.error('SEO application failed:', error.message); process.exit(1); } console.log('JSX to HTML conversion completed successfully!');