// Owner rule: no hand-written SVG. Charts come from visx, icons from Phosphor, the store marks from simple-icons, the // journey diagram from Mermaid, the fixture art from tools/fixture-art.ts. This guard fails on inline SVG drawing in // the desktop webview, the site and the teaser video (media/teaser), and on any SVG file that is not a known asset. // The brand mark is a PNG (tools/brand-assets.py from brand/master/logo-mark.png): no inline brand SVG anywhere. // Allowed: the root a visx chart renders into (files that import @visx/*), the vendored icon // files (site/img/icons: Phosphor and simple-icons; apps/desktop/src/ui/store-marks and templates/web: simple-icons; // media/teaser/icons: Phosphor), each byte-equal to // its package (tools/site-icons.ts), and the Mermaid output (docs/img, site/img journey*.svg). import { describe, expect, test } from 'bun:test'; import { readdirSync, readFileSync, statSync } from 'node:fs'; import { join, relative } from 'node:path'; import { checkSiteIcons, ICON_DIR, MARK_DIR, SITE_ICONS, STORE_MARKS, TEASER_DIR, TEASER_ICONS, WEB_DIR } from '../tools/site-icons'; const ROOT = join(import.meta.dir, '..'); const walk = (dir: string, keep: (f: string) => boolean): string[] => readdirSync(join(ROOT, dir)).flatMap(n => { const rel = join(dir, n); if (n === 'node_modules' || n === 'dist') return []; return statSync(join(ROOT, rel)).isDirectory() ? walk(rel, keep) : keep(n) ? [rel] : []; }); const read = (f: string) => readFileSync(join(ROOT, f), 'utf8'); /** SVG drawing elements and inline SVG images. */ const SHAPES = /<(path|circle|rect|ellipse|line|polyline|polygon)[\s/>]|data:image\/svg\+xml|]/g; /** Every hand-drawn hit in a source text: "file:line { test('desktop webview: charts through visx, icons through Phosphor, no inline SVG at all (the brand mark is a PNG)', () => { const files = walk('apps/desktop/src', n => /\.(tsx?|css|html)$/.test(n)); expect(files.length).toBeGreaterThan(30); const hits = files.flatMap(f => { const s = read(f); return svgHits(f, s, { visxRoot: /from '@visx\//.test(s) }); }); expect(hits).toEqual([]); // icons only through Phosphor's per-icon modules (tree-shaken), never the lucide set or the whole Phosphor index const bad = files.filter(f => /from '(lucide-react|@phosphor-icons\/react)'/.test(read(f))); expect(bad).toEqual([]); }); test('the guard itself catches a drawn icon and an inline SVG image', () => { expect(svgHits('x.tsx', 'const a = ;')).toHaveLength(2); expect(svgHits('x.ts', "const u = 'data:image/svg+xml;base64,AAAA';")).toHaveLength(1); expect(svgHits('x.tsx', '', { visxRoot: true })).toEqual([]); expect(svgHits('x.tsx', '', { visxRoot: true })).toHaveLength(1); }); test('site pages: icons are vendored Phosphor / simple-icons files; no inline SVG, the brand mark is a PNG', () => { const pages = walk('site', n => n.endsWith('.html')); expect(pages.length).toBeGreaterThanOrEqual(2); const hits = pages.flatMap(f => svgHits(f, read(f))); expect(hits).toEqual([]); expect(checkSiteIcons()).toEqual([]); for (const f of ['site/style.css', 'site/main.js']) expect(svgHits(f, read(f)), f).toEqual([]); }); test('teaser video (media/teaser): icons and the mouse cursor are vendored Phosphor files, no inline SVG', () => { const files = walk('media/teaser', n => /\.(html|js|ts|css)$/.test(n)).filter(f => !f.startsWith('media/teaser/out/')); expect(files.length).toBeGreaterThanOrEqual(5); expect(files.flatMap(f => svgHits(f, read(f)))).toEqual([]); // every icon the teaser references is one of the vendored Phosphor files const used = files.flatMap(f => [...read(f).matchAll(/icons\/([\w-]+\.svg)/g)].map(m => m[1])); expect(used.length).toBeGreaterThanOrEqual(TEASER_ICONS.length); const vendored: string[] = TEASER_ICONS.map(([w, i]) => `${i}-${w}.svg`); for (const n of used) expect(vendored, n).toContain(n ?? ''); }); test('templates (the web init landing page): store marks are the vendored simple-icons files, no inline SVG', () => { const files = walk('templates', n => /\.(html|css|js)$/.test(n)); expect(files).toContain('templates/web/landing.html'); expect(files.flatMap(f => svgHits(f, read(f)))).toEqual([]); }); test('SVG files: vendored Phosphor / simple-icons files and Mermaid diagrams only', () => { const svgs = ['site', 'docs', 'apps/desktop/src', 'media', 'templates'].flatMap(d => walk(d, n => n.endsWith('.svg'))).map(f => relative(ROOT, join(ROOT, f))); const vendored = new Set([ ...[...SITE_ICONS, ...STORE_MARKS].map(n => relative(ROOT, join(ICON_DIR, `${n}.svg`))), ...STORE_MARKS.map(n => relative(ROOT, join(MARK_DIR, `${n}.svg`))), ...STORE_MARKS.map(n => relative(ROOT, join(WEB_DIR, `${n}.svg`))), ...TEASER_ICONS.map(([w, n]) => relative(ROOT, join(TEASER_DIR, `${n}-${w}.svg`))), ]); // byte equality with the packages is checked by checkSiteIcons (above); here: no other SVG file sneaks in for (const f of svgs) { if (vendored.has(f)) continue; expect(f, `${f}: not a known asset`).toMatch(/^(docs|site)\/img\/journey(-tall)?\.svg$/); expect(read(f), `${f}: render it with bun tools/diagrams.ts`).toContain('aria-roledescription="flowchart-v2"'); } }); test('brand: the mark is the PNG set from tools/brand-assets.py, no SVG brand file', () => { const all = ['site', 'apps/desktop/src', 'apps/desktop/src-tauri/icons', 'brand'].flatMap(d => walk(d, n => n.endsWith('.svg'))); expect(all.filter(f => !/^(site\/img\/icons|apps\/desktop\/src\/ui\/store-marks)\//.test(f) && !/journey(-tall)?\.svg$/.test(f))).toEqual([]); for (const f of ['brand/master/logo-mark.png', 'site/favicon.ico', 'site/img/logo-mark.png', 'apps/desktop/src/ui/logo-mark.png', 'apps/desktop/src-tauri/icons/icon.icns']) { expect(statSync(join(ROOT, f)).size, f).toBeGreaterThan(0); } }); });