rusty-ssr 0.1.0

High-performance SSR engine for Rust with V8 isolate pool and multi-tier caching
Documentation
#!/usr/bin/env node
/**
 * Example: Preact SSR Bundle Builder
 *
 * This example shows how to wrap a Preact SSR bundle for use with Rusty SSR.
 *
 * Prerequisites:
 *   1. Build your Preact app with Vite in IIFE format:
 *      vite.config.ts: build.rollupOptions.output.format = 'iife'
 *   2. Set IIFE name: build.rollupOptions.output.name = 'SSRBundle'
 *   3. Export renderToString from your entry point
 *
 * Usage:
 *   node examples/build-preact-bundle.js path/to/server-entry.js output/ssr-bundle.js
 *
 * Your Preact entry-server.tsx should look like:
 *
 *   import { h } from 'preact';
 *   import renderToString from 'preact-render-to-string';
 *   import App from './App';
 *
 *   export async function render(context) {
 *       const html = renderToString(<App url={context.url} data={context.data} />);
 *       return { html, head: '', initialData: context.data };
 *   }
 *
 *   // Re-export for IIFE bundle
 *   export { render as renderToString };
 */

import fs from 'fs';
import path from 'path';

// Parse arguments
const args = process.argv.slice(2);

if (args.length < 2) {
    console.log(`
Preact SSR Bundle Builder

Usage:
  node examples/build-preact-bundle.js <input> <output>

Arguments:
  input   Path to Vite SSR build output (IIFE format)
  output  Output path for Rusty SSR bundle

Example:
  node examples/build-preact-bundle.js dist/server/entry.js ssr-bundle.js
`);
    process.exit(1);
}

const inputPath = args[0];
const outputPath = args[1];

if (!fs.existsSync(inputPath)) {
    console.error(`Error: Input file not found: ${inputPath}`);
    process.exit(1);
}

// Read Vite SSR output (IIFE format)
const ssrCode = fs.readFileSync(inputPath, 'utf-8');

// Create wrapper that exposes renderPage globally
const wrappedBundle = `
// ============ Preact SSR Bundle ============
// Generated by: node examples/build-preact-bundle.js
// Source: ${inputPath}
// ============================================

${ssrCode}

// ============ Rusty SSR Integration ============

/**
 * Global render function called by Rust V8 engine
 *
 * @param {string} url - URL path to render
 * @param {object|string} data - Data from Rust (JSON)
 * @returns {string} Complete HTML document
 */
globalThis.renderPage = async function(url, data) {
    try {
        // Parse data if string
        const props = typeof data === 'string' ? JSON.parse(data) : (data || {});

        // Build context for Preact SSR
        const context = {
            url: url,
            data: props,
            headers: {},
            userAgent: 'Rusty-SSR/1.0',
        };

        // Call your SSR render function
        // Assumes SSRBundle.renderToString returns { html, head?, initialData? }
        const result = await SSRBundle.renderToString(context);

        // Build complete HTML document
        // CUSTOMIZE THIS for your project!
        const html = \`<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Preact App</title>
    \${result.head || ''}
    <!-- Add your CSS links here -->
</head>
<body>
    <div id="app">\${result.html}</div>
    <script>window.__INITIAL_DATA__ = \${JSON.stringify(result.initialData || props)}</script>
    <!-- Add your client bundle here -->
</body>
</html>\`;

        return html;
    } catch (error) {
        console.error('[Preact SSR] Error:', error);
        // Return error page instead of crashing
        return \`<!DOCTYPE html>
<html>
<head><title>SSR Error</title></head>
<body>
    <h1>Server Render Error</h1>
    <pre>\${error.stack || error.message}</pre>
</body>
</html>\`;
    }
};

console.log('[Preact SSR] Bundle loaded and ready');
`;

// Write output
const outputDir = path.dirname(outputPath);
if (outputDir && outputDir !== '.' && !fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
}

fs.writeFileSync(outputPath, wrappedBundle);

console.log(`
Preact SSR Bundle Created
  Input:  ${inputPath} (${(ssrCode.length / 1024).toFixed(2)} KB)
  Output: ${outputPath} (${(wrappedBundle.length / 1024).toFixed(2)} KB)
  Global: renderPage(url, data)

Next steps:
  1. Customize the HTML template in the generated bundle
  2. Add your CSS/JS asset paths
  3. Start Rusty SSR: cargo run --example basic
`);