Files
KoalaSync/website/tools/subset-flag-font.mjs
T
KoalaDev af4ac943a9 Landing performance: flag font subset, responsive mascots, AVIF cache, HTML minify
- Flag font: ship a committed fontTools subset (43 KB, -45%) of
  TwemojiCountryFlags.woff2 with only the 15 flags the site uses, under a
  content-hashed name with a preload on every flag-bearing page. The build
  validates the subset against a manifest and fails when a new flag
  appears without regenerating (npm run subset-flags). JS subsetters
  (subset-font/harfbuzzjs) were rejected: their wasm builds silently drop
  the COLR/CPAL color tables, rendering all flags invisible.
- Responsive mascots: legal/join/404 pages served 434-500px sources for
  175-180px slots; the build now emits 180w/360w variants (AVIF 1x: ~7 KB
  vs ~22 KB) and the pages use srcset. Small logo spots (14-42px) now use
  the existing 64/128px variants instead of the 256px file.
- injectAvifPictures copies the img sizes attribute onto the AVIF
  <source>; without it Chrome mis-selects w-descriptor candidates and can
  drop the image entirely.
- AVIF conversion is cached by content hash (.avif-cache.json); the old
  mtime check never hit because copyDirSync refreshes mtimes, so every
  verify re-encoded all 26 files.
- HTML output is minified (comments + indentation, <pre> preserved,
  newlines kept for inline-script safety): index.html 121 KB -> 93 KB.
- unicode-range trimmed to U+1F1E6-1F1FF; tag-sequence flags fall through
  to the system emoji font.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 04:34:53 +02:00

74 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Regenerate the committed country-flag font subset.
*
* npm run subset-flags (or: node website/tools/subset-flag-font.mjs)
*
* Extracts every flag emoji used in website HTML/locales, subsets
* assets/TwemojiCountryFlags.woff2 down to exactly those flags via Python
* fontTools (harfbuzz-based JS subsetters drop the COLR color tables and
* produce invisible flags), and writes:
*
* assets/TwemojiCountryFlags.subset.woff2 (committed)
* assets/TwemojiCountryFlags.subset.json (committed manifest)
*
* The website build (build.cjs) is pure Node: it only validates the
* manifest and fails when a flag was added without re-running this tool.
*
* Requires python3 with fonttools + brotli. Override the interpreter with
* the PYTHON env var, e.g. PYTHON=.venv/bin/python npm run subset-flags
*/
import { execFileSync } from 'node:child_process';
import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const websiteDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const { collectFlagSourceFiles, extractUsedFlags, flagToCountryCode } = require(path.join(websiteDir, 'flag-font-utils.cjs'));
const sourceFont = path.join(websiteDir, 'assets', 'TwemojiCountryFlags.woff2');
const outputFont = path.join(websiteDir, 'assets', 'TwemojiCountryFlags.subset.woff2');
const manifestPath = path.join(websiteDir, 'assets', 'TwemojiCountryFlags.subset.json');
const python = process.env.PYTHON || 'python3';
try {
execFileSync(python, ['-c', 'import fontTools.subset, brotli'], { stdio: 'pipe' });
} catch {
console.error(`error: ${python} lacks fonttools/brotli.`);
console.error('Install them (e.g. "pip3 install fonttools brotli") or point PYTHON at an interpreter that has them:');
console.error(' PYTHON=/path/to/venv/bin/python npm run subset-flags');
process.exit(1);
}
const flags = extractUsedFlags(collectFlagSourceFiles(websiteDir));
if (flags.length === 0) {
console.error('error: no flag emoji found in website sources — extraction is broken');
process.exit(1);
}
const codes = flags.map(flagToCountryCode);
console.log(`Found ${codes.length} flags: ${codes.join(' ')}`);
execFileSync(python, [
path.join(websiteDir, 'tools', 'subset_flag_font.py'),
sourceFont,
outputFont,
codes.join(',')
], { stdio: 'inherit' });
const sha = buf => crypto.createHash('sha256').update(buf).digest('hex');
const manifest = {
comment: 'Generated by tools/subset-flag-font.mjs — do not edit. build.cjs fails when site flags and this manifest diverge.',
flags: codes,
sourceFontSha256: sha(fs.readFileSync(sourceFont)),
subsetSha256: sha(fs.readFileSync(outputFont))
};
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
const before = fs.statSync(sourceFont).size;
const after = fs.statSync(outputFont).size;
console.log(`Wrote ${path.relative(process.cwd(), outputFont)} (${(after / 1024).toFixed(1)} KB, -${((1 - after / before) * 100).toFixed(0)}% vs source) + manifest.`);
console.log('Commit both files.');