Skip to content

Instantly share code, notes, and snippets.

@mcansh
Last active November 14, 2023 16:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mcansh/ade07941189286f4a0870bc35f9e8c20 to your computer and use it in GitHub Desktop.
Save mcansh/ade07941189286f4a0870bc35f9e8c20 to your computer and use it in GitHub Desktop.
convert heroicons to an svg <symbol> so you can do `<svg><use href="..."></svg>` with them and not have them all inlined everytime
import path from "node:path";
import fse from "fs-extra";
import svgstore from "svgstore";
import { glob } from "glob";
import prettier from "prettier";
let HEROICONS_PATH = path.join(process.cwd(), "node_modules/heroicons");
let ASSETS_PATH = path.join(process.cwd(), "assets");
let OUTFILE = path.join(process.cwd(), "app/components/sprite/index.svg");
let COMPONENT_FILE = path.join(process.cwd(), "app/components/sprite/index.tsx");
let js = String.raw;
await fse.ensureDir(path.dirname(OUTFILE));
let [heroicons, custom] = await Promise.all([
glob("./**/*.svg", { absolute: true, cwd: HEROICONS_PATH, nodir: true }),
glob("./**/*.svg", { absolute: true, cwd: ASSETS_PATH, nodir: true }),
]);
function getIconInfo(fp, cwd) {
let rel = path.relative(cwd, fp);
let dir = rel.split(path.sep);
let base = path.basename(fp).replace(".svg", "");
let name = dir.slice(0, dir.length - 1).join(":") + ":" + base;
return { path: fp, name };
}
let icons = [
...heroicons.map((icon) => getIconInfo(icon, HEROICONS_PATH)),
...custom.map(icon => getIconInfo(icon, ASSETS_PATH)),
];
let sprites = svgstore();
let union = [];
for (let icon of icons) {
let content = await fse.readFile(icon.path, "utf-8");
sprites.add(icon.name, content);
union.push(icon.name);
}
let component = js`
import iconsHref from "./index.svg";
export type SpriteName = ${[...union]
.map((icon) => `"${icon}"`)
.join(" | ")};
export type SpriteProps = { name: SpriteName; } & JSX.IntrinsicElements["svg"];
export function Svg({ name, ...svgProps }: SpriteProps) {
return (
<svg {...svgProps}>
<use href={iconsHref + "#" + name} />
</svg>
);
}
`;
await fse.writeFile(
OUTFILE,
await prettier.format(sprites.toString(), { parser: "html" }),
);
await fse.writeFile(
COMPONENT_FILE,
await prettier.format(component, { parser: "typescript" }),
);
@mcansh
Copy link
Author

mcansh commented Nov 7, 2022

usage:

import { Svg } from '~/components/heroicons';

export default function SomeComponent() {
  return <Svg name="solid:24:x-mark" />
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment