mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
- package.json with biome, rolldown, lightningcss devDependencies
- biome.json: add CSS formatter support
- scripts/build-css.mjs: lightningcss resolves @import chain, bundles/minifies CSS
- scripts/build-js.mjs: rolldown per-entry JS bundling (no code splitting)
- scripts/build.mjs: orchestrator for both CSS + JS
- scripts/check-build.mjs: staleness checker for CI/deploy guard
- justfile: add build, build-css, build-js, build-install, build-check recipes
- justfile: deploy recipe now runs build before deploy-code
- head.php + form-page.php: use dist/base.min.css instead of style.css
- All controllers + FormBootstrap: reference dist/*.min.{css,js}
- admin footer: load admin.min.js for all admin pages
- repertoire: use public.min.js instead of individual app JS files
- Fix stray '}' syntax error in admin.css line 305
- .gitignore: add app/public/assets/dist/
153 lines
4.1 KiB
JavaScript
153 lines
4.1 KiB
JavaScript
#!/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);
|
|
});
|