Files
securebit-chat/tests/icon-subset.test.mjs
T
lockbitchat 414c27fda6 v6.7.3: faster loading, and pages search engines can read
The bundles carried all thirteen translations at once and a page fetched them a
third time as raw source; each page now loads only its own language. Alongside
that: JavaScript is minified, the eight stylesheets are served as one file, the
QR scanner is fetched after the app is up instead of on every visit, Inter ships
once rather than five copies of the same file, and Font Awesome is subset to the
82 icons this app draws instead of all 2468.

1.85 MB across 43 requests becomes under 700 KB across 33. On mobile the page
starts drawing in 1.6 s instead of 6.3 s and is usable in 4.5 s instead of 11 s.

Pages also carry their text in the HTML now. Everything was drawn by JavaScript
into an empty div, so crawlers saw correct metadata around nothing, and twelve of
the thirteen language pages had never been shown to anyone. The documentation is
published under /docs/ with a new FAQ, and unknown addresses return a real 404.

Separately: the localized shells were served with the year-long immutable cache
header meant for static assets, which pinned anyone who opened /de/ or /ru/ to
that build. The header is fixed and the service worker refreshes what it cached.

Claude-Session: https://claude.ai/code/session_014KjzTXxrhzYoDDWChYQ4u2
2026-09-04 00:41:46 -04:00

65 lines
3.2 KiB
JavaScript

// The icon fonts are subset to what this interface draws: 82 of Font Awesome's 2468,
// which took 299 KB of woff2 down to 12 KB. The saving comes with a failure mode that
// is completely silent — add an icon to a component and it renders as an empty box,
// with no error anywhere, because the glyph is simply not in the shipped font.
//
// So the manifest scripts/subset-icons.py writes is checked against the source here.
// A new icon fails the build with the name of the icon and the command to run.
import assert from 'node:assert/strict';
import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const ROOT = fileURLToPath(new URL('..', import.meta.url));
const read = (rel) => readFileSync(path.join(ROOT, rel), 'utf8');
const manifest = JSON.parse(read('assets/fontawesome/subset-icons.json'));
const covered = new Set(manifest.icons);
const notIcons = new Set(manifest.notIcons);
// Every fa- class the interface names, from the same files the subsetter reads.
function sourceFiles(dir) {
const out = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) out.push(...sourceFiles(full));
else if (/\.(js|jsx|css|html)$/.test(entry.name)) out.push(full);
}
return out;
}
const used = new Set();
for (const file of [...sourceFiles(path.join(ROOT, 'src')), path.join(ROOT, 'index.html')]) {
for (const match of readFileSync(file, 'utf8').matchAll(/\bfa-([a-z0-9-]+)/g)) {
if (!notIcons.has(match[1])) used.add(match[1]);
}
}
const missing = [...used].filter((name) => !covered.has(name)).sort();
assert.deepEqual(missing, [],
`these icons are used but not in the subset, so they render as empty boxes: ${missing.join(', ')}\n` +
' fix: pip install fonttools brotli && python3 scripts/subset-icons.py && npm run build');
// The subset fonts and the stylesheet that names them have to exist and be small — the
// point of the exercise is the size, and a regenerate that quietly fell back to the full
// font would still pass every assertion above.
const bundle = read('assets/app.css');
for (const family of ['fa-solid-900', 'fa-regular-400', 'fa-brands-400']) {
const rel = `assets/fontawesome/webfonts/${family}.subset.woff2`;
assert.ok(existsSync(path.join(ROOT, rel)), `${rel} is missing — run scripts/subset-icons.py`);
const size = statSync(path.join(ROOT, rel)).size;
assert.ok(size < 40_000, `${rel} is ${size} B — that is the full font, not a subset`);
assert.ok(bundle.includes(`${family}.subset.woff2`), `assets/app.css does not use the ${family} subset`);
}
// And the full stylesheet must not come back: it is 102 KB for icons that are not here.
assert.equal(bundle.includes('/assets/fontawesome/css/all.min.css'), false,
'the full Font Awesome stylesheet is being loaded again alongside the subset');
for (const shell of ['index.html', 'ru/index.html']) {
assert.equal(read(shell).includes('fontawesome/css/all.min.css'), false,
`${shell} still links the full Font Awesome stylesheet`);
}
console.log(`icon-subset.test.mjs: ${used.size} icons used, all covered by the subset`);