import fs from 'fs';
import path from 'path';
const args = process.argv.slice(2);
if (args.length < 2 || args.includes('--help') || args.includes('-h')) {
console.log(`
Rusty SSR Bundle Builder
Usage:
node scripts/build-bundle.js <input> <output> [options]
Arguments:
input Path to your SSR bundle (from Vite/webpack/esbuild)
output Output path for Rusty SSR compatible bundle
Options:
--iife <name> IIFE global name (e.g., --iife SSRBundle)
--render <name> Render function name in bundle (default: renderToString)
--fn <name> Global function name to expose (default: renderPage)
--help, -h Show this help
Examples:
# Wrap IIFE bundle (Preact/React)
node scripts/build-bundle.js dist/server.js ssr-bundle.js --iife SSRBundle
# Simple passthrough (already has globalThis.renderPage)
node scripts/build-bundle.js dist/server.js ssr-bundle.js
`);
process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
}
const inputPath = args[0];
const outputPath = args[1];
const options = {
iife: null,
render: 'renderToString',
fn: 'renderPage',
};
for (let i = 2; i < args.length; i++) {
if (args[i] === '--iife' && args[i + 1]) {
options.iife = args[++i];
} else if (args[i] === '--render' && args[i + 1]) {
options.render = args[++i];
} else if (args[i] === '--fn' && args[i + 1]) {
options.fn = args[++i];
}
}
if (!fs.existsSync(inputPath)) {
console.error(`Error: Input file not found: ${inputPath}`);
process.exit(1);
}
const inputCode = fs.readFileSync(inputPath, 'utf-8');
let outputCode;
if (options.iife) {
outputCode = `
// ============ SSR Bundle (IIFE wrapped) ============
${inputCode}
// ============ End SSR Bundle ============
// Expose render function globally for Rusty SSR
if (typeof ${options.iife} !== 'undefined' && typeof ${options.iife}.${options.render} === 'function') {
globalThis.${options.fn} = ${options.iife}.${options.render};
console.log('[Rusty SSR] Bundle loaded: ${options.iife}.${options.render} -> globalThis.${options.fn}');
} else {
console.error('[Rusty SSR] Error: ${options.iife}.${options.render} not found in bundle');
}
`;
} else {
if (inputCode.includes(`globalThis.${options.fn}`)) {
outputCode = inputCode;
console.log(`Bundle already has globalThis.${options.fn}, using as-is`);
} else {
outputCode = `
// ============ SSR Bundle ============
${inputCode}
// ============ End SSR Bundle ============
// Note: Ensure your bundle exposes globalThis.${options.fn}
// See examples/bundles/ for reference implementations
`;
}
}
const outputDir = path.dirname(outputPath);
if (outputDir && !fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputPath, outputCode);
const inputSize = (inputCode.length / 1024).toFixed(2);
const outputSize = (outputCode.length / 1024).toFixed(2);
console.log(`
Rusty SSR Bundle Created
Input: ${inputPath} (${inputSize} KB)
Output: ${outputPath} (${outputSize} KB)
Global: ${options.fn}()
`);