Files
sunrise/scripts/building-compression.ts
T

30 lines
1.8 KiB
TypeScript

import { createReadStream, statSync } from 'node:fs';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { pipeline } from 'node:stream';
import { BUILDINGS } from '../src/constants/buildings.ts';
// Only serves the precomputed building buffer; other assets use Vite's defaults.
export function buildingCompressionMiddleware(publicDirectory: URL) {
const path = new URL(BUILDINGS.assetBufferUrl.slice(1) + '.gz', publicDirectory);
return (request: IncomingMessage, response: ServerResponse, next: () => void) => {
if (request.url?.split('?')[0] !== BUILDINGS.assetBufferUrl || !['GET', 'HEAD'].includes(request.method ?? '')) return next();
const vary = response.getHeader('Vary');
response.setHeader('Vary', vary ? `${vary}, Accept-Encoding` : 'Accept-Encoding');
const encodings = (request.headers['accept-encoding'] ?? '').toLowerCase().split(',').map(entry => {
const [name, ...parameters] = entry.trim().split(';');
const quality = parameters.find(value => value.trim().startsWith('q='));
return { name, quality: quality ? Number(quality.trim().slice(2)) : 1 };
});
const accepted = encodings.find(entry => entry.name === 'gzip') ?? encodings.find(entry => entry.name === '*');
if (!accepted || !(accepted.quality > 0) || request.headers.range) return next();
let size: number;
try { size = statSync(path).size; } catch { return next(); }
response.setHeader('Content-Encoding', 'gzip');
response.setHeader('Content-Type', 'application/octet-stream');
response.setHeader('Content-Length', size);
response.setHeader('Cache-Control', 'no-cache');
if (request.method === 'HEAD') { response.end(); return; }
pipeline(createReadStream(path), response, error => { if (error) response.destroy(error); });
};
}