From c27f12893092acb81c1272d29b79e65ba60e9725 Mon Sep 17 00:00:00 2001 From: HiDeoo <494699+HiDeoo@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:42:33 +0100 Subject: [PATCH] fix: use seti ui repo to generate file icons (#2648) --- packages/file-icons-generator/index.ts | 24 ++++--- packages/file-icons-generator/utils/font.ts | 10 ++- packages/file-icons-generator/utils/seti.ts | 69 ++++++++++++++++----- 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/packages/file-icons-generator/index.ts b/packages/file-icons-generator/index.ts index 195d3165aea..11642ceebec 100644 --- a/packages/file-icons-generator/index.ts +++ b/packages/file-icons-generator/index.ts @@ -1,24 +1,28 @@ import { writeDefinitionsAndSVGs } from './utils/file'; import { getIconSvgPaths } from './utils/font'; -import { fetchFont, fetchMapping, parseMapping } from './utils/seti'; +import { deleteRepo, parseMapping, setupRepo } from './utils/seti'; /** * Script generating definitions used by the Starlight `` component and associated SVGs. * - * To do so, it fetches the Seti UI icon mapping file and font from GitHub, parses the mapping to - * generate the definitions and a list of icons to extract as SVGs, and finally extracts the SVGs - * from the font and writes the definitions and SVGs to the Starlight package in a file ready to be - * consumed by Starlight. + * To do so, it clones the Seti UI repository, installs dependencies, generates icons, parses the + * mapping to generate the definitions and a list of icons to extract as SVGs, and finally extracts + * the SVGs from the font and writes the definitions and SVGs to the Starlight package in a file + * ready to be consumed by Starlight. * * @see {@link file://./config.ts} for the configuration used by this script. * @see {@link file://../starlight/user-components/file-tree-icons.ts} for the generated file. * @see {@link https://opentype.js.org/glyph-inspector.html} for a font glyph inspector. */ -const mapping = await fetchMapping(); -const { definitions, icons } = parseMapping(mapping); +const repoPath = await setupRepo(); -const font = await fetchFont(); -const svgPaths = getIconSvgPaths(icons, definitions, font); +try { + const { definitions, icons } = await parseMapping(repoPath); -await writeDefinitionsAndSVGs(definitions, svgPaths); + const svgPaths = await getIconSvgPaths(repoPath, icons, definitions); + + await writeDefinitionsAndSVGs(definitions, svgPaths); +} finally { + await deleteRepo(repoPath); +} diff --git a/packages/file-icons-generator/utils/font.ts b/packages/file-icons-generator/utils/font.ts index a9518e1b8c1..64ce32cf4aa 100644 --- a/packages/file-icons-generator/utils/font.ts +++ b/packages/file-icons-generator/utils/font.ts @@ -1,17 +1,15 @@ import opentype, { type Font, Glyph } from 'opentype.js'; import { seti, starlight } from '../config'; import type { Definitions } from '../../starlight/user-components/rehype-file-tree'; -import { getSetiIconName } from './seti'; +import { getFont, getSetiIconName } from './seti'; // This matches the default precision used by the SVGO default preset. const pathDecimalPrecision = 3; /** Extract SVG paths from the Seti UI icon font from a list of icon names matching font glyphs. */ -export function getIconSvgPaths( - icons: string[], - definitions: Definitions, - fontBuffer: ArrayBuffer -) { +export async function getIconSvgPaths(repoPath: string, icons: string[], definitions: Definitions) { + const fontBuffer = await getFont(repoPath); + const iconSvgs: Record = {}; let font: Font; diff --git a/packages/file-icons-generator/utils/seti.ts b/packages/file-icons-generator/utils/seti.ts index 283353e596e..fa4d46421eb 100644 --- a/packages/file-icons-generator/utils/seti.ts +++ b/packages/file-icons-generator/utils/seti.ts @@ -1,3 +1,7 @@ +import { spawnSync, type SpawnSyncOptions } from 'node:child_process'; +import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; import { seti, starlight } from '../config'; import type { Definitions } from '../../starlight/user-components/rehype-file-tree.ts'; @@ -7,33 +11,56 @@ import type { Definitions } from '../../starlight/user-components/rehype-file-tr const mappingRegex = /^\.icon-(?(set|partial))\((?['"])(?.+)\k, \k(?.+)\k, @.+\);$/; -/** Fetch the Seti UI icon mapping file from GitHub. */ -export async function fetchMapping() { +/** Clone the Seti UI repository, install dependencies, and generate the Seti UI icons. */ +export async function setupRepo() { try { - const result = await fetch(getGitHubDownloadLink(seti.repo, seti.mapping)); - return await result.text(); + const repoPath = await fs.mkdtemp(path.join(os.tmpdir(), 'starlight-file-icons-')); + + const spawnOptions: SpawnSyncOptions = { + cwd: repoPath, + encoding: 'utf8', + }; + + let result = spawnSync('git', ['clone', `https://github.com/${seti.repo}`, '.'], spawnOptions); + if (result.error) throw new Error('Failed to clone the Seti UI repository.'); + + result = spawnSync('npm', ['install'], spawnOptions); + if (result.error) throw new Error('Failed to install the Seti UI dependencies.'); + + result = spawnSync('npm', ['run', 'createIcons'], spawnOptions); + if (result.error) throw new Error('Failed to generate the Seti UI icons.'); + + return repoPath; } catch (error) { throw new Error( - 'Failed to download Seti UI icon mapping file. Make sure the repository URL and mapping path are correct.', + 'Failed to setup the Seti UI repo. Make sure the repository URL and font path are correct.', { cause: error } ); } } +/** Delete the Seti UI repository. */ +export async function deleteRepo(repoPath: string) { + try { + await fs.rm(repoPath, { force: true, recursive: true }); + } catch (error) { + throw new Error('Failed to remove the Seti UI repo.', { cause: error }); + } +} + /** - * Fetch the Seti UI icon font from GitHub. + * Get the Seti UI icon font from a local repository. * Note that the `woff` font format is used and not `woff2` as we would manually need to decompress * it and we do not need the compression benefits for this use case. */ -export async function fetchFont() { +export async function getFont(repoPath: string) { try { - const result = await fetch(getGitHubDownloadLink(seti.repo, seti.font)); - return await result.arrayBuffer(); + const result = await fs.readFile(path.join(repoPath, seti.font)); + return new Uint8Array(result).buffer; } catch (error) { - throw new Error( - 'Failed to download Seti UI font. Make sure the repository URL and font path are correct.', - { cause: error } - ); + throw new Error('Failed to read Seti UI font. Make sure the font path is correct.', { + cause: error, + }); } } @@ -42,7 +69,9 @@ export async function fetchFont() { * component and a list of Seti UI icons to extract as SVGs. * @see https://github.com/elviswolcott/seti-icons/blob/master/build/extract.ts */ -export function parseMapping(mapping: string) { +export async function parseMapping(repoPath: string) { + const mapping = await getMapping(repoPath); + const lines = mapping.split('\n'); // Include the `folder` icon by default as it is not defined in the mapping file. const icons = new Set(['folder']); @@ -87,6 +116,14 @@ export function getSetiIconName(icon: string) { return `${starlight.prefix}${name}`; } -function getGitHubDownloadLink(repo: string, path: string) { - return `https://raw.githubusercontent.com/${repo}/${seti.branch}/${path}`; +/** Get the Seti UI icon mapping file from a local repository. */ +async function getMapping(repoPath: string) { + try { + return await fs.readFile(path.join(repoPath, seti.mapping), 'utf8'); + } catch (error) { + throw new Error( + 'Failed to read Seti UI icon mapping file. Make sure the mapping file path is correct.', + { cause: error } + ); + } }