website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


src/_helpers/fileIcons.js
import iconSet from "@iconify-json/vscode-icons/icons.json" with { type: "json" };

// The old Astro version resolved per-extension file/folder icons from the
// full vscode-icons manifest (see git history at 7325531^:src/utils/files.ts).
// That manifest is itself a large VS Code extension repo installed as a git
// dependency — overkill for a static build. This is a curated subset of the
// same @iconify-json/vscode-icons icon set (already used to render both),
// covering the extensions that actually show up in these repos, with a
// generic fallback for anything else.
const FILENAME_TO_ICON = {
  "cargo.toml": "file-type-cargo",
  "cargo.lock": "file-type-cargo",
  "license": "file-type-license",
  "license.md": "file-type-license",
  ".gitignore": "file-type-git",
  ".gitattributes": "file-type-git",
  ".npmignore": "file-type-npm",
  "package.json": "file-type-npm",
  "package-lock.json": "file-type-npm",
  "bun.lock": "file-type-npm",
  "tsconfig.json": "file-type-typescript",
};

const EXT_TO_ICON = {
  rs: "file-type-rust",
  toml: "file-type-toml",
  json: "file-type-json",
  jsonc: "file-type-json",
  md: "file-type-markdown",
  mdx: "file-type-mdx",
  css: "file-type-css",
  html: "file-type-html",
  js: "file-type-js",
  mjs: "file-type-js",
  jsx: "file-type-reactjs",
  ts: "file-type-typescript",
  tsx: "file-type-reactts",
  zig: "file-type-zig",
  svg: "file-type-svg",
  png: "file-type-image",
  jpg: "file-type-image",
  jpeg: "file-type-image",
  gif: "file-type-image",
  webp: "file-type-webp",
  pdf: "file-type-pdf2",
  txt: "file-type-text",
  sh: "file-type-shell",
  py: "file-type-python",
  kt: "file-type-kotlin",
  java: "file-type-java",
  go: "file-type-go",
  c: "file-type-c",
  h: "file-type-cheader",
  yml: "file-type-yaml",
  yaml: "file-type-yaml",
};

const FOLDER_TO_ICON = {
  src: "folder-type-src",
  test: "folder-type-test",
  tests: "folder-type-test",
  public: "folder-type-public",
  images: "folder-type-images",
  docs: "folder-type-docs",
  dist: "folder-type-dist",
  ".github": "folder-type-github",
  node_modules: "folder-type-node",
};

// Every icon id this module can ever emit, deduped — used to build the
// sprite once rather than guessing which subset a given page needs.
const ICON_NAMES = new Set([
  ...Object.values(FILENAME_TO_ICON),
  ...Object.values(EXT_TO_ICON),
  ...Object.values(FOLDER_TO_ICON),
  "default-file",
  "default-folder",
]);

/**
 * A <svg><symbol> sprite holding every file/folder icon this repo browser
 * can use, written once to assets/icons/sprite.svg (see eleventy.config.js)
 * rather than inlined per page. fileIcon()/folderIcon() emit a tiny <use>
 * that points at that shared file, so the ~30 icon bodies are fetched
 * (and cached by the browser) once for the whole site instead of being
 * duplicated in every one of the thousands of generated repo pages.
 */
export const iconSpriteDefs = () => {
  const symbols = [...ICON_NAMES]
    .map((name) => {
      const icon = iconSet.icons[name];
      return `<symbol id="icon-${name}" viewBox="0 0 ${iconSet.width} ${iconSet.height}">${icon.body}</symbol>`;
    })
    .join("");
  return `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true">${symbols}</svg>`;
};

const useIcon = (name) => `<svg width="16" height="16"><use href="/assets/icons/sprite.svg#icon-${name}"></use></svg>`;

export const fileIcon = (filename, ext) => {
  const key = FILENAME_TO_ICON[filename.toLowerCase()] ?? EXT_TO_ICON[ext] ?? "default-file";
  return useIcon(key);
};

export const folderIcon = (foldername) => {
  const key = FOLDER_TO_ICON[foldername.toLowerCase()] ?? "default-folder";
  return useIcon(key);
};