| Server IP : 159.203.156.69 / Your IP : 216.73.217.172 Web Server : nginx/1.24.0 System : Linux main-ubuntu 6.8.0-71-generic #71-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 22 16:52:38 UTC 2025 x86_64 User : root ( 0) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/tanviranik.com/node_modules/vinext/dist/build/ |
Upload File : |
{"version":3,"file":"precompress.js","names":[],"sources":["../../src/build/precompress.ts"],"sourcesContent":["/**\n * Build-time precompression for hashed static assets.\n *\n * Generates .br (brotli q5), .gz (gzip l8), and .zst (zstd l8) files\n * alongside compressible assets in dist/client/assets/. Served directly by\n * the production server — no per-request compression needed for immutable\n * build output.\n *\n * Only targets assets/ (hashed, immutable) — public directory files use\n * on-the-fly compression since they may change between deploys.\n */\nimport fsp from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\n\nconst brotliCompress = promisify(zlib.brotliCompress);\nconst gzip = promisify(zlib.gzip);\nconst zstdCompress = typeof zlib.zstdCompress === \"function\" ? promisify(zlib.zstdCompress) : null;\n\n/** File extensions worth compressing (text-based, not already compressed). */\nconst COMPRESSIBLE_EXTENSIONS = new Set([\n \".js\",\n \".mjs\",\n \".css\",\n \".html\",\n \".json\",\n \".xml\",\n \".svg\",\n \".txt\",\n \".map\",\n \".wasm\",\n]);\n\n/** Below this size, compression overhead exceeds savings. */\nconst MIN_SIZE = 1024;\n\n/**\n * Past ~8 parallel files, mixed-size asset sets spend more time queueing zlib\n * work than making forward progress. Keep the batch size bounded even on\n * higher-core machines.\n */\nconst CONCURRENCY = Math.min(os.availableParallelism(), 8);\n\ntype PrecompressResult = {\n filesCompressed: number;\n totalOriginalBytes: number;\n /** Sum of brotli-compressed sizes (used for compression ratio reporting). */\n totalBrotliBytes: number;\n};\n\n/**\n * Walk a directory recursively, yielding relative paths for regular files.\n */\nasync function* walkFiles(dir: string, base: string = dir): AsyncGenerator<string> {\n let entries;\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return; // directory doesn't exist\n }\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n yield* walkFiles(fullPath, base);\n } else if (entry.isFile()) {\n yield path.relative(base, fullPath);\n }\n }\n}\n\n/**\n * Precompress all compressible hashed assets under `clientDir/assets/`.\n *\n * Writes `.br`, `.gz`, and `.zst` files alongside each original.\n * Safe to re-run — overwrites existing compressed variants with identical\n * output, and never compresses `.br`, `.gz`, or `.zst` files themselves.\n */\nexport async function precompressAssets(\n clientDir: string,\n onProgress?: (completed: number, total: number, file: string) => void,\n): Promise<PrecompressResult> {\n const assetsDir = path.join(clientDir, \"assets\");\n const result: PrecompressResult = {\n filesCompressed: 0,\n totalOriginalBytes: 0,\n totalBrotliBytes: 0,\n };\n\n // Collect compressible file paths, then read + compress in bounded chunks\n // to keep peak memory at O(CONCURRENCY * max_file_size) instead of\n // O(total_assets).\n const filePaths: string[] = [];\n\n for await (const relativePath of walkFiles(assetsDir)) {\n const ext = path.extname(relativePath).toLowerCase();\n\n if (!COMPRESSIBLE_EXTENSIONS.has(ext)) continue;\n // .br/.gz/.zst are intentionally absent from COMPRESSIBLE_EXTENSIONS, so\n // precompressed variants generated by a previous run are never re-compressed.\n\n filePaths.push(path.join(assetsDir, relativePath));\n }\n\n let processed = 0;\n for (let i = 0; i < filePaths.length; i += CONCURRENCY) {\n const chunk = filePaths.slice(i, i + CONCURRENCY);\n await Promise.all(\n chunk.map(async (fullPath) => {\n const content = await fsp.readFile(fullPath);\n // readFile already done before this check — stat()-first would save\n // the read for tiny files but costs an extra syscall per file;\n // sub-1KB hashed assets are rare enough that read-first is cheaper.\n if (content.length < MIN_SIZE) return;\n\n // Compress all variants concurrently within each file\n const compressions: Promise<Buffer>[] = [\n brotliCompress(content, {\n params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 5 },\n }),\n gzip(content, { level: 8 }),\n ];\n if (zstdCompress) {\n compressions.push(\n zstdCompress(content, {\n params: { [zlib.constants.ZSTD_c_compressionLevel]: 8 },\n }),\n );\n }\n\n const results = await Promise.all(compressions);\n const [brContent, gzContent, zstdContent] = results;\n\n const writes = [\n fsp.writeFile(fullPath + \".br\", brContent),\n fsp.writeFile(fullPath + \".gz\", gzContent),\n ];\n if (zstdContent) {\n writes.push(fsp.writeFile(fullPath + \".zst\", zstdContent));\n }\n await Promise.all(writes);\n\n // Increment counters only after all writes succeed, so partial\n // failures (e.g. ENOSPC mid-write) don't inflate the reported totals.\n result.filesCompressed++;\n result.totalOriginalBytes += content.length;\n result.totalBrotliBytes += brContent.length;\n }),\n );\n // Report progress once per chunk to avoid non-deterministic ordering\n // within Promise.all (smaller files complete before larger ones).\n // Progress tracks all files (including skipped ones below MIN_SIZE),\n // which differs from filesCompressed (only files actually compressed).\n processed += chunk.length;\n onProgress?.(processed, filePaths.length, path.basename(chunk[chunk.length - 1]));\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,MAAM,iBAAiB,UAAU,KAAK,eAAe;AACrD,MAAM,OAAO,UAAU,KAAK,KAAK;AACjC,MAAM,eAAe,OAAO,KAAK,iBAAiB,aAAa,UAAU,KAAK,aAAa,GAAG;;AAG9F,MAAM,0BAA0B,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,MAAM,WAAW;;;;;;AAOjB,MAAM,cAAc,KAAK,IAAI,GAAG,sBAAsB,EAAE,EAAE;;;;AAY1D,gBAAgB,UAAU,KAAa,OAAe,KAA6B;CACjF,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,IAAI,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;SACnD;EACN;;CAEF,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;EAC3C,IAAI,MAAM,aAAa,EACrB,OAAO,UAAU,UAAU,KAAK;OAC3B,IAAI,MAAM,QAAQ,EACvB,MAAM,KAAK,SAAS,MAAM,SAAS;;;;;;;;;;AAYzC,eAAsB,kBACpB,WACA,YAC4B;CAC5B,MAAM,YAAY,KAAK,KAAK,WAAW,SAAS;CAChD,MAAM,SAA4B;EAChC,iBAAiB;EACjB,oBAAoB;EACpB,kBAAkB;EACnB;CAKD,MAAM,YAAsB,EAAE;CAE9B,WAAW,MAAM,gBAAgB,UAAU,UAAU,EAAE;EACrD,MAAM,MAAM,KAAK,QAAQ,aAAa,CAAC,aAAa;EAEpD,IAAI,CAAC,wBAAwB,IAAI,IAAI,EAAE;EAIvC,UAAU,KAAK,KAAK,KAAK,WAAW,aAAa,CAAC;;CAGpD,IAAI,YAAY;CAChB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,aAAa;EACtD,MAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,YAAY;EACjD,MAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,aAAa;GAC5B,MAAM,UAAU,MAAM,IAAI,SAAS,SAAS;GAI5C,IAAI,QAAQ,SAAS,UAAU;GAG/B,MAAM,eAAkC,CACtC,eAAe,SAAS,EACtB,QAAQ,GAAG,KAAK,UAAU,uBAAuB,GAAG,EACrD,CAAC,EACF,KAAK,SAAS,EAAE,OAAO,GAAG,CAAC,CAC5B;GACD,IAAI,cACF,aAAa,KACX,aAAa,SAAS,EACpB,QAAQ,GAAG,KAAK,UAAU,0BAA0B,GAAG,EACxD,CAAC,CACH;GAIH,MAAM,CAAC,WAAW,WAAW,eAAe,MADtB,QAAQ,IAAI,aAAa;GAG/C,MAAM,SAAS,CACb,IAAI,UAAU,WAAW,OAAO,UAAU,EAC1C,IAAI,UAAU,WAAW,OAAO,UAAU,CAC3C;GACD,IAAI,aACF,OAAO,KAAK,IAAI,UAAU,WAAW,QAAQ,YAAY,CAAC;GAE5D,MAAM,QAAQ,IAAI,OAAO;GAIzB,OAAO;GACP,OAAO,sBAAsB,QAAQ;GACrC,OAAO,oBAAoB,UAAU;IACrC,CACH;EAKD,aAAa,MAAM;EACnB,aAAa,WAAW,UAAU,QAAQ,KAAK,SAAS,MAAM,MAAM,SAAS,GAAG,CAAC;;CAGnF,OAAO"}