Files
wordpress-theme-aksrt/tools/strip-fa5-legacy-fonts.mjs
kate522 4740078135 fix(icons): repair the FA5 generator and drop the 404 font references
generate-fa5-icons.php read fa-{brands,regular,solid}.svg from
webfonts/, but only woff/woff2 ship with the theme, so the generator
died on file_get_contents() and the committed fa5-icons.php could not
be regenerated from what the theme actually distributes.

Decode the style split from each shipped woff's cmap table instead.
The split has to come from the font: FA5 assigns different drawings to
the same codepoint per style, so fas/far/fab membership is unanswerable
from the CSS alone. Output is byte-identical to the committed catalog
(1001/151/456), so the picker is unaffected.

all.min.css also declared eot / eot?#iefix / ttf / svg fallbacks for all
three families while webfonts/ holds only woff2 + woff, so a browser
that stops at woff requested six files that do not exist. Add
tools/strip-fa5-legacy-fonts.mjs to keep the src: list to what ships;
it is idempotent and touches nothing but the @font-face blocks.
2026-11-01 11:52:14 +08:00

57 lines
2.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Strip the font formats we do not ship from the bundled Font Awesome 5 CSS.
*
* assets/fontawesome5/webfonts only contains woff2 + woff, but all.min.css
* still advertises eot / ttf / svg fallbacks. A browser that stops at woff
* (IE11, older Safari) requests files that were never distributed, so each of
* the three styles 404s once per page load.
*
* The rewrite is scoped to @font-face src: declarations only — glyph rules,
* utilities and the license header are left untouched.
*/
import { readFileSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const target = join(root, 'assets/fontawesome5/css/all.min.css');
const css = readFileSync(target, 'utf8');
// Each @font-face carries two src statements: the IE-only eot shim
// (`src:url(...eot);`) and the formatted source list (`src:url(...),url(...)`).
// Match the whole run of consecutive src statements up to the closing brace so
// both dangling statements collapse into one clean list.
const SHIPPED = new Set(['woff2', 'woff']);
const srcRun = /(?:src:[^;{}]*(?:;[^;{}]*?)?)*(?=\})/gi;
const rewritten = css.replace(srcRun, (run) => {
// Only touch runs that are actually src declarations, not other properties.
if (!/^src:/.test(run)) return run;
const sources = [...run.matchAll(/url\(([^)]+)\)\s*(?:format\(["']?([a-z0-9-]+)["']?\))?/gi)]
.map(([, url, format]) => ({ url: url.trim(), format: (format || '').toLowerCase() }));
// Drop every source whose format we do not ship; a source with no format()
// is the eot shim, which no browser that loads this file can use.
const kept = sources.filter((s) => SHIPPED.has(s.format));
if (!kept.length) return run;
const declaration = `src:${kept.map((s) => `url(${s.url}) format("${s.format}")`).join(',')}`;
// Already clean: re-running the script must stay a no-op rather than a failure.
return declaration === run ? run : declaration;
});
const dropped = [...css.matchAll(/url\(([^)]*webfonts[^)]*)\)/g)]
.map((m) => m[1])
.filter((url) => !/\.woff2?(\?|\)|$)/.test(url));
const alreadyClean = rewritten === css;
if (!alreadyClean) writeFileSync(target, rewritten);
if (dropped.length) {
console.log(`dropped ${dropped.length} non-shipped font references: ${[...new Set(dropped)].join(', ')}`);
} else {
console.log('all.min.css already ships only woff2 + woff — nothing to do');
}