#!/usr/bin/env node /** * Build CSS bundles with Lightning CSS. * * - base.min.css: resolves the @import chain in style.css into a single minified file. * This eliminates ~17 sequential @import requests on every page. * - admin.min.css: minifies admin.css * - form.min.css: bundles form-base.css + form-admin.css + filepond vendor CSS * * All other page-specific CSS files (public.css, tfe.css, repertoire.css, etc.) * are minified in-place as individual files. * * Output: app/public/assets/dist/ */ import { bundleAsync } from "lightningcss"; import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; import { resolve, dirname, basename } from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = dirname(fileURLToPath(import.meta.url)); const root = resolve(__dirname, ".."); const cssDir = resolve(root, "app/public/assets/css"); const distDir = resolve(root, "app/public/assets/dist"); mkdirSync(distDir, { recursive: true }); const targets = { chrome: 115 << 16, firefox: 115 << 16, safari: 16 << 16, }; function makeResolver() { return { resolve(specifier, from) { return resolve(dirname(from), specifier); }, read(filePath) { return readFileSync(filePath, "utf8"); }, }; } async function bundleCss(filename, outName) { const entryPath = resolve(cssDir, filename); const result = await bundleAsync({ filename: entryPath, resolver: makeResolver(), minify: true, sourceMap: false, targets, }); const outPath = resolve(distDir, outName); writeFileSync(outPath, result.code); const size = Buffer.byteLength(result.code, "utf8"); console.log(` ✓ ${outName} (${size.toLocaleString()} bytes)`); return size; } async function concatBundle(filenames, outName) { const parts = []; for (const f of filenames) { const fp = resolve(cssDir, f); parts.push(readFileSync(fp, "utf8")); } const combined = parts.join("\n"); // Use lightningcss to minify the combined content const result = await bundleAsync({ filename: resolve(cssDir, filenames[0]), resolver: { resolve() { throw new Error("unexpected @import in concat bundle"); }, read(fp) { if (fp === resolve(cssDir, filenames[0])) { return combined; } // Allow vendor CSS @imports within the combined content throw new Error(`unexpected file in concat: ${fp}`); }, }, minify: true, sourceMap: false, targets, }); const outPath = resolve(distDir, outName); writeFileSync(outPath, result.code); const size = Buffer.byteLength(result.code, "utf8"); console.log(` ✓ ${outName} (${size.toLocaleString()} bytes)`); return size; } async function main() { console.log("🎨 Building CSS bundles…\n"); let total = 0; // 1. Base bundle: resolve style.css @import chain total += await bundleCss("style.css", "base.min.css"); // 2. Admin bundle: just minify admin.css (standalone) total += await bundleCss("admin.css", "admin.min.css"); // 3. Form bundle: concat + minify form-base.css + form-admin.css + filepond vendor CSS total += await concatBundle( [ "form-base.css", "form-admin.css", "filepond.min.css", "filepond-plugin-image-preview.min.css", ], "form.min.css" ); // 4. Individual page-specific CSS files: minify each const individualFiles = [ "public.css", "tfe.css", "repertoire.css", "content-page.css", "system.css", "file-access.css", ]; for (const f of individualFiles) { const outName = f.replace(/\.css$/, ".min.css"); total += await bundleCss(f, outName); } // 5. Form-base standalone (for partage pages without FilePond) total += await bundleCss("form-base.css", "form-base.min.css"); // 6. Partage bundle: form-base + filepond (for partage pages with FilePond) total += await concatBundle( [ "form-base.css", "filepond.min.css", "filepond-plugin-image-preview.min.css", ], "partage-form.min.css" ); console.log(`\n✅ CSS bundles done — ${total.toLocaleString()} bytes total\n`); } main().catch((err) => { console.error("❌ CSS build failed:", err); process.exit(1); });