117 lines
2.8 KiB
TypeScript
Executable File
117 lines
2.8 KiB
TypeScript
Executable File
#!/usr/bin/env -S deno run --allow-env --allow-net --allow-read --allow-write
|
|
import "jsr:@std/dotenv/load";
|
|
import type { FigmaNodeFile } from "./figma.types.ts";
|
|
import { optimize } from "svgo";
|
|
|
|
const FIGMA_ICON_FILE_ID = Deno.env.get("FIGMA_ICON_FILE_ID");
|
|
const FIGMA_TOKEN = Deno.env.get("FIGMA_TOKEN");
|
|
const FRAME_ID = Deno.env.get("FRAME_ID");
|
|
const OUT_DIR = Deno.env.get("OUT_DIR");
|
|
|
|
if (!FIGMA_ICON_FILE_ID) {
|
|
console.error("env: FIGMA_ICON_FILE_ID is not set.");
|
|
Deno.exit(2);
|
|
}
|
|
if (!FRAME_ID) {
|
|
console.error("env: FRAME_ID is not set.");
|
|
Deno.exit(2);
|
|
}
|
|
if (!FIGMA_TOKEN) {
|
|
console.error("env: FIGMA_TOKEN is not set.");
|
|
Deno.exit(2);
|
|
}
|
|
|
|
const raw = await fetch(
|
|
`https://api.figma.com/v1/files/${FIGMA_ICON_FILE_ID}/nodes?ids=${FRAME_ID}`,
|
|
{
|
|
headers: {
|
|
"X-FIGMA-TOKEN": FIGMA_TOKEN,
|
|
},
|
|
},
|
|
);
|
|
|
|
const figmaFile: FigmaNodeFile = await raw.json();
|
|
|
|
// @ts-ignore: could be an error response
|
|
if (figmaFile.status) {
|
|
console.error(`Failed to fetch`, { figmaFile });
|
|
Deno.exit();
|
|
}
|
|
|
|
const nodeId = FRAME_ID.replace("-", ":");
|
|
const iconComponentIds = Object.keys(figmaFile.nodes[nodeId].components);
|
|
|
|
const images = await fetch(
|
|
`https://api.figma.com/v1/images/${FIGMA_ICON_FILE_ID}?ids=${iconComponentIds.join(
|
|
",",
|
|
)}&format=svg`,
|
|
{
|
|
headers: {
|
|
"X-FIGMA-TOKEN": FIGMA_TOKEN,
|
|
},
|
|
},
|
|
);
|
|
|
|
type ImageResonse =
|
|
| {
|
|
err: null;
|
|
images: {
|
|
[id: string]: string | null;
|
|
};
|
|
}
|
|
| {
|
|
err: string;
|
|
};
|
|
const urlSet: ImageResonse = await images.json();
|
|
|
|
if (urlSet.err !== null) {
|
|
console.error("Could not get image paths", { urlSet });
|
|
Deno.exit(1);
|
|
}
|
|
|
|
const getNamefromType = (str: string) => {
|
|
const [_type, name] = str.split("=");
|
|
if (!name) {
|
|
console.error("Icon doesnt have name", str);
|
|
Deno.exit(1);
|
|
}
|
|
return name;
|
|
};
|
|
|
|
const final = await Promise.all(
|
|
Object.entries(urlSet.images)
|
|
.filter(([, url]) => url !== null)
|
|
.map(async ([id, url]) => {
|
|
const entry = figmaFile.nodes[nodeId].components[id];
|
|
const rawSvg = await fetch(url as string);
|
|
let svgText = await rawSvg.text();
|
|
const replace = /fill=["']#?[0-9a-fA-F]{3,6}["']|fill=["'][a-zA-Z]+["']/g;
|
|
svgText = svgText.replaceAll(replace, 'fill="currentColor"');
|
|
|
|
const optimizedSvg = optimize(svgText, {}).data;
|
|
return {
|
|
name: getNamefromType(entry.name),
|
|
file: optimizedSvg,
|
|
};
|
|
}),
|
|
);
|
|
|
|
const path = OUT_DIR;
|
|
if (!path) {
|
|
console.error("OUT_DIR is not set");
|
|
Deno.exit(1);
|
|
}
|
|
|
|
console.log(`Will write icons to: ${path}/*`);
|
|
|
|
final.filter(Boolean).forEach((i) => {
|
|
const icon = i!;
|
|
Deno.mkdirSync(path, { recursive: true });
|
|
Deno.writeTextFileSync(`${path}/${icon.name}.svg`, icon.file);
|
|
console.log(`Wrote: ${path}/${icon.name}.svg`);
|
|
});
|
|
|
|
console.info("All icons up to date");
|
|
|
|
export {};
|