Files
windwiderstand/scripts/generate-icons.mjs
T
Peter Meier c9ba88ab80
Deploy / verify (push) Successful in 1m5s
Deploy / deploy (push) Successful in 1m13s
feat: OrganisationsMapBlock — Karte der Bürgerinitiativen
Neuer Block-Typ `organisations_map`: Leaflet-Karte mit CircleMarkern für alle
Organisationen die ein location.lat/lng haben. Klick auf Marker öffnet
Detail-Panel (Desktop: rechte Sidebar / Mobile: Bottom Sheet) mit Name,
Standort, Beschreibung und Website-Link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 12:03:26 +02:00

152 lines
4.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Tree-shake @iconify-json/mdi: scan src/ for `icon="mdi:..."` references and
* emit a minimal IconifyJSON containing only those icons. Saves ~3 MB in the
* client bundle vs. shipping the full 7000+ icon collection.
*
* Dynamic icons (from CMS data, conditional names) cannot be detected
* statically — list them under EXTRA_ICONS below. Anything missing falls
* through to the iconify API at runtime, which adds a network roundtrip but
* does not break.
*
* Run via `npm run generate:icons`. Hooked into `prebuild`.
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const SRC_DIR = path.join(ROOT, 'src');
const ICONS_JSON = path.join(ROOT, 'node_modules', '@iconify-json', 'mdi', 'icons.json');
const OUT_FILE = path.join(ROOT, 'src', 'lib', 'iconify-mdi-subset.generated.json');
/**
* Icons that appear via dynamic bindings (CMS data, conditional expressions)
* and would otherwise miss the static scan. Add new ones here when editors
* pick icons that appear blank — better than shipping the full collection.
*/
const EXTRA_ICONS = [
// PostActions: dynamic share/copy state
'share-variant-outline',
'link-variant',
// Search: file-type fallback
'file-outline',
// Common social icons editors pick (Header.svelte: social.icon)
'facebook',
'instagram',
'twitter',
'youtube',
'linkedin',
'mastodon',
'github',
'email',
'email-outline',
'phone',
'whatsapp',
'telegram',
'import-contacts',
'arrow-top-right',
'open-in-new',
'card-account-details-outline',
'qrcode',
'sort-alphabetical-ascending',
'account-group-outline',
// Common file-type icons (FilesBlock: f.iconName)
'file-pdf-box',
'file-word-box',
'file-excel-box',
'file-image',
'file-video',
'file-music',
// Tag/link/banner fallbacks (LinkListBlock, Tags, DeadlineBannerBlock)
'link',
'tag-outline',
'flag-outline',
'information-outline',
];
const STATIC_PATTERN = /icon="mdi:([a-z0-9-]+)"/g;
function walk(dir) {
const out = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
out.push(...walk(full));
} else if (/\.(svelte|ts|tsx|js|mjs)$/.test(entry.name)) {
out.push(full);
}
}
return out;
}
function scanStatic() {
const found = new Set();
for (const file of walk(SRC_DIR)) {
const content = fs.readFileSync(file, 'utf8');
let m;
while ((m = STATIC_PATTERN.exec(content)) !== null) {
found.add(m[1]);
}
}
return found;
}
function main() {
if (!fs.existsSync(ICONS_JSON)) {
console.error(
`[generate-icons] missing ${ICONS_JSON} — run \`npm install\` first.`,
);
process.exit(1);
}
const collection = JSON.parse(fs.readFileSync(ICONS_JSON, 'utf8'));
const wanted = new Set([...scanStatic(), ...EXTRA_ICONS]);
const icons = {};
const aliases = {};
const missing = [];
for (const name of wanted) {
if (collection.icons[name]) {
icons[name] = collection.icons[name];
} else if (collection.aliases?.[name]) {
aliases[name] = collection.aliases[name];
const parent = collection.aliases[name].parent;
if (parent && collection.icons[parent] && !icons[parent]) {
icons[parent] = collection.icons[parent];
}
} else {
missing.push(name);
}
}
const subset = {
prefix: collection.prefix,
width: collection.width,
height: collection.height,
icons,
...(Object.keys(aliases).length ? { aliases } : {}),
};
fs.writeFileSync(OUT_FILE, JSON.stringify(subset, null, 2) + '\n');
const sizeBefore = fs.statSync(ICONS_JSON).size;
const sizeAfter = fs.statSync(OUT_FILE).size;
console.log(
`[generate-icons] ${Object.keys(icons).length} icons + ${Object.keys(aliases).length} aliases → ${path.relative(ROOT, OUT_FILE)}`,
);
console.log(
`[generate-icons] ${(sizeBefore / 1024 / 1024).toFixed(2)} MB → ${(sizeAfter / 1024).toFixed(1)} KB (${((1 - sizeAfter / sizeBefore) * 100).toFixed(1)}% smaller)`,
);
if (missing.length) {
console.warn(
`[generate-icons] ${missing.length} icon(s) not found in MDI: ${missing.join(', ')}`,
);
}
}
main();