Skip to content

Commit

Permalink
fix: use seti ui repo to generate file icons (#2648)
Browse files Browse the repository at this point in the history
  • Loading branch information
HiDeoo authored Dec 11, 2024
1 parent cf12beb commit c27f128
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 32 deletions.
24 changes: 14 additions & 10 deletions packages/file-icons-generator/index.ts
Original file line number Diff line number Diff line change
@@ -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 `<FileTree>` 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);
}
10 changes: 4 additions & 6 deletions packages/file-icons-generator/utils/font.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> = {};

let font: Font;
Expand Down
69 changes: 53 additions & 16 deletions packages/file-icons-generator/utils/seti.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -7,33 +11,56 @@ import type { Definitions } from '../../starlight/user-components/rehype-file-tr
const mappingRegex =
/^\.icon-(?<type>(set|partial))\((?<quote>['"])(?<identifier>.+)\k<quote>, \k<quote>(?<lang>.+)\k<quote>, @.+\);$/;

/** 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,
});
}
}

Expand All @@ -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<string>(['folder']);
Expand Down Expand Up @@ -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 }
);
}
}

0 comments on commit c27f128

Please sign in to comment.