70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
'use strict';
|
|
|
|
// Copies frontend libraries from node_modules into public/vendor so the app
|
|
// loads no remote scripts (works on a locked-down network, reproducible).
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const OUT = path.join(ROOT, 'public', 'vendor');
|
|
|
|
const FILES = [
|
|
['beercss/dist/cdn/beer.min.css', 'beer.min.css'],
|
|
['beercss/dist/cdn/beer.min.js', 'beer.min.js'],
|
|
// Material Symbols icon fonts. beer.min.css references these by relative URL
|
|
// (with a CDN fallback our CSP blocks), so they must sit beside it in /vendor.
|
|
['beercss/dist/cdn/material-symbols-outlined.woff2', 'material-symbols-outlined.woff2'],
|
|
['beercss/dist/cdn/material-symbols-rounded.woff2', 'material-symbols-rounded.woff2'],
|
|
['beercss/dist/cdn/material-symbols-sharp.woff2', 'material-symbols-sharp.woff2'],
|
|
['beercss/dist/cdn/material-symbols-subset.woff2', 'material-symbols-subset.woff2'],
|
|
['material-dynamic-colors/dist/cdn/material-dynamic-colors.min.js', 'material-dynamic-colors.min.js'],
|
|
['alpinejs/dist/cdn.min.js', 'alpine.min.js'],
|
|
];
|
|
|
|
fs.mkdirSync(OUT, { recursive: true });
|
|
|
|
let ok = 0;
|
|
for (const [src, dest] of FILES) {
|
|
const from = path.join(ROOT, 'node_modules', src);
|
|
const to = path.join(OUT, dest);
|
|
try {
|
|
fs.copyFileSync(from, to);
|
|
console.log(`vendored ${dest}`);
|
|
ok++;
|
|
} catch (e) {
|
|
console.error(`FAILED to vendor ${src}: ${e.message}`);
|
|
}
|
|
}
|
|
// Strip Beer CSS's remote @font-face fallbacks (jsdelivr CDN) so the icon font
|
|
// resolves only to our local /vendor copy — no CDN load, no CSP violation.
|
|
const beerCss = path.join(OUT, 'beer.min.css');
|
|
try {
|
|
const before = fs.readFileSync(beerCss, 'utf8');
|
|
const after = before.replace(/,url\(https:\/\/cdn\.jsdelivr\.net[^)]*\)\s*format\("woff2"\)/g, '');
|
|
if (after !== before) {
|
|
fs.writeFileSync(beerCss, after);
|
|
console.log('stripped remote CDN font fallbacks from beer.min.css');
|
|
}
|
|
} catch (e) {
|
|
console.error(`WARN: could not post-process beer.min.css: ${e.message}`);
|
|
}
|
|
|
|
// material-dynamic-colors ships as an ES module: it sets the
|
|
// globalThis.materialDynamicColors global (which Beer CSS's ui("theme") needs)
|
|
// but ALSO ends with `export default ...`, which breaks classic <script> loading
|
|
// ("Unexpected token 'export'"). Strip the export so it loads as a plain script.
|
|
const mdc = path.join(OUT, 'material-dynamic-colors.min.js');
|
|
try {
|
|
const before = fs.readFileSync(mdc, 'utf8');
|
|
const after = before.replace(/\s*export\s+default[^;]*;\s*$/, '\n');
|
|
if (after !== before) {
|
|
fs.writeFileSync(mdc, after);
|
|
console.log('stripped ES export from material-dynamic-colors.min.js');
|
|
}
|
|
} catch (e) {
|
|
console.error(`WARN: could not post-process material-dynamic-colors.min.js: ${e.message}`);
|
|
}
|
|
|
|
console.log(`\n${ok}/${FILES.length} files vendored into public/vendor`);
|
|
if (ok < FILES.length) process.exitCode = 1;
|