77 lines
5.9 KiB
TypeScript
77 lines
5.9 KiB
TypeScript
#!/usr/bin/env bun
|
|
// Vendors icon files, copied byte for byte from their packages, plus the licences:
|
|
// site/img/icons/ Phosphor (@phosphor-icons/core, MIT, "regular" weight) for the site's icons and the
|
|
// App Store / Google Play marks from simple-icons (CC0 data)
|
|
// apps/desktop/src/ui/store-marks the same two store marks for the desktop app (the other desktop icons come from
|
|
// @phosphor-icons/react)
|
|
// media/teaser/icons Phosphor icons for the teaser video (the mouse cursor, the finding and fix icons, the terminal, send)
|
|
// templates/web the two store marks for the landing page `storebound web init --landing` writes
|
|
// Both show them as CSS masks (site/style.css .ic-*, desktop styles.css .store-mark), so they take the text colour in
|
|
// both themes and nothing is loaded from outside. The store marks only name the store next to its name; they are
|
|
// trademarks of Apple Inc. and Google LLC (docs/desktop.md, the site footer).
|
|
// bun tools/site-icons.ts copy (after a package bump or a new icon in the lists below)
|
|
// bun tools/site-icons.ts --check fail when a vendored file differs from its package (CI, when installed)
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
export const SITE_ICONS = ['caret-right', 'play', 'user', 'squares-four', 'briefcase'] as const;
|
|
export const STORE_MARKS = ['appstore', 'googleplay'] as const;
|
|
/** Teaser icons: [Phosphor weight, icon name], vendored as <name>-<weight>.svg (the package's own file name). */
|
|
export const TEASER_ICONS = [['fill', 'navigation-arrow'], ['bold', 'check'], ['fill', 'check-circle'],
|
|
['fill', 'warning-octagon'], ['fill', 'paper-plane-tilt'], ['bold', 'terminal-window']] as const;
|
|
|
|
const root = new URL('..', import.meta.url).pathname;
|
|
const phosphor = join(root, 'node_modules/@phosphor-icons/core');
|
|
const simple = join(root, 'node_modules/simple-icons');
|
|
export const ICON_DIR = join(root, 'site/img/icons');
|
|
export const MARK_DIR = join(root, 'apps/desktop/src/ui/store-marks');
|
|
export const TEASER_DIR = join(root, 'media/teaser/icons');
|
|
export const WEB_DIR = join(root, 'templates/web');
|
|
|
|
/** [vendored dir, file name, package file] for every vendored icon. */
|
|
function plan(): [string, string, string][] {
|
|
const marks = STORE_MARKS.map(n => [`${n}.svg`, join(simple, 'icons', `${n}.svg`)] as const);
|
|
return [
|
|
...SITE_ICONS.map(n => [ICON_DIR, `${n}.svg`, join(phosphor, 'assets/regular', `${n}.svg`)] as [string, string, string]),
|
|
...marks.map(([f, src]) => [ICON_DIR, f, src] as [string, string, string]),
|
|
...marks.map(([f, src]) => [MARK_DIR, f, src] as [string, string, string]),
|
|
...marks.map(([f, src]) => [WEB_DIR, f, src] as [string, string, string]),
|
|
...TEASER_ICONS.map(([w, n]) => [TEASER_DIR, `${n}-${w}.svg`, join(phosphor, 'assets', w, `${n}-${w}.svg`)] as [string, string, string]),
|
|
];
|
|
}
|
|
const rel = (p: string) => p.slice(root.length);
|
|
|
|
/** Problems of the vendored folders against the packages ([] when they match, or when the packages are not installed). */
|
|
export function checkSiteIcons(): string[] {
|
|
if (!existsSync(phosphor) || !existsSync(simple)) return [];
|
|
const out: string[] = [];
|
|
const p = plan();
|
|
for (const [dir, f, src] of p) {
|
|
const at = join(dir, f);
|
|
if (!existsSync(at)) out.push(`${rel(at)} missing (bun tools/site-icons.ts)`);
|
|
else if (readFileSync(at, 'utf8') !== readFileSync(src, 'utf8')) out.push(`${rel(at)} is not ${rel(src).replace('node_modules/', '')}`);
|
|
}
|
|
for (const dir of [ICON_DIR, MARK_DIR, TEASER_DIR, WEB_DIR]) {
|
|
if (!existsSync(dir)) continue;
|
|
for (const f of readdirSync(dir)) if (f.endsWith('.svg') && !p.some(([d, n]) => d === dir && n === f)) out.push(`${rel(join(dir, f))} is not a vendored icon`);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const version = (dir: string) => JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).version as string;
|
|
const simpleNote = () => `App Store and Google Play marks: simple-icons ${version(simple)} (https://simpleicons.org), copied unchanged from\nthe simple-icons package. The icon data is CC0; the marks are trademarks of Apple Inc. and Google LLC and are used\nonly to name the store next to its name. storebound is not affiliated with Apple or Google.\n\n${readFileSync(join(simple, 'LICENSE.md'), 'utf8')}`;
|
|
|
|
if (import.meta.main) {
|
|
if (!existsSync(phosphor) || !existsSync(simple)) { console.error('site-icons: @phosphor-icons/core or simple-icons is not installed (bun install)'); process.exit(1); }
|
|
if (process.argv.includes('--check')) {
|
|
const bad = checkSiteIcons();
|
|
for (const b of bad) console.error(`site-icons: ${b}`);
|
|
process.exit(bad.length ? 1 : 0);
|
|
}
|
|
for (const dir of [ICON_DIR, MARK_DIR, TEASER_DIR, WEB_DIR]) mkdirSync(dir, { recursive: true });
|
|
for (const [dir, f, src] of plan()) writeFileSync(join(dir, f), readFileSync(src));
|
|
writeFileSync(join(ICON_DIR, 'LICENSE.txt'), `Icons in this folder, copied unchanged by tools/site-icons.ts:\n\nPhosphor ${version(phosphor)} (https://phosphoricons.com), regular weight, from the @phosphor-icons/core\npackage.\n\n${readFileSync(join(phosphor, 'LICENSE'), 'utf8')}\n\n${simpleNote()}`);
|
|
writeFileSync(join(TEASER_DIR, 'LICENSE.txt'), `Icons in this folder, copied unchanged by tools/site-icons.ts:\n\nPhosphor ${version(phosphor)} (https://phosphoricons.com), from the @phosphor-icons/core package.\n\n${readFileSync(join(phosphor, 'LICENSE'), 'utf8')}`);
|
|
writeFileSync(join(MARK_DIR, 'LICENSE.txt'), `Store marks in this folder, copied unchanged by tools/site-icons.ts.\n\n${simpleNote()}`);
|
|
console.log(`site-icons: ${SITE_ICONS.length} Phosphor icons ${version(phosphor)} and ${STORE_MARKS.length} simple-icons ${version(simple)} store marks and ${TEASER_ICONS.length} teaser icons -> site/img/icons/, apps/desktop/src/ui/store-marks/, media/teaser/icons/, templates/web/`);
|
|
}
|