1import fs from 'node:fs';
2import path from 'node:path';
3
4const buildDir = path.join(import.meta.dirname, '..', 'build');
5const outputFile = path.join(import.meta.dirname, '..', 'server', 'clientEmbed.js');
6
7if (!fs.existsSync(buildDir)) {
8 console.error('Build directory not found. Run "bun run build" first.');
9 process.exit(1);
10}
11
12const MIME_TYPES = {
13 '.html': 'text/html',
14 '.js': 'application/javascript',
15 '.css': 'text/css',
16 '.json': 'application/json',
17 '.map': 'application/json',
18 '.png': 'image/png',
19 '.jpg': 'image/jpeg',
20 '.jpeg': 'image/jpeg',
21 '.gif': 'image/gif',
22 '.svg': 'image/svg+xml',
23 '.ico': 'image/x-icon',
24 '.webp': 'image/webp',
25 '.webm': 'video/webm',
26 '.mp4': 'video/mp4',
27 '.woff': 'font/woff',
28 '.woff2': 'font/woff2',
29 '.ttf': 'font/ttf',
30 '.eot': 'application/vnd.ms-fontobject',
31 '.txt': 'text/plain',
32 '.xml': 'application/xml',
33};
34
35const walkDir = (dir, base = '') => {
36 const result = [];
37 for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
38 const rel = base ? `${base}/${entry.name}` : entry.name;
39 if (entry.isDirectory()) result.push(...walkDir(path.join(dir, entry.name), rel));
40 else if (entry.isFile()) result.push(rel);
41 }
42 return result;
43};
44
45const files = walkDir(buildDir);
46
47if (files.length === 0) {
48 console.error('No files found in build directory.');
49 process.exit(1);
50}
51
52const imports = files
53 .map((file, i) => `import f${i} from '../build/${file}' with { type: 'file' };`)
54 .join('\n');
55
56const entries = files.map((file, i) => {
57 const mime = MIME_TYPES[path.extname(file)] || 'application/octet-stream';
58 return ` ['/${file}', { path: f${i}, mime: '${mime}' }],`;
59}).join('\n');
60
61const indexIdx = files.indexOf('index.html');
62const indexRef = indexIdx !== -1 ? `f${indexIdx}` : 'null';
63
64const output = `import fs from 'node:fs';
65
66${imports}
67
68const cache = new Map([
69${entries}
70].map(([url, { path, mime }]) => [url, { content: fs.readFileSync(path), mime }]));
71
72const indexHtml = ${indexRef === 'null' ? 'null' : `fs.readFileSync(${indexRef})`};
73
74export const createEmbeddedMiddleware = () => (req, res, next) => {
75 const entry = cache.get(req.path);
76 if (!entry) return next();
77 res.type(entry.mime).send(entry.content);
78};
79
80export const createEmbeddedFallback = () => (req, res) => {
81 if (indexHtml) res.type('text/html').send(indexHtml);
82 else res.status(404).send('Not found');
83};
84`;
85
86fs.writeFileSync(outputFile, output);
87console.log(`Generated ${outputFile} with ${files.length} embedded file(s)`);
88