cloudflare/kumo

Public

mirrored fromhttps://github.com/cloudflare/kumoAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
5260f1a5703bb69e6c7f7cf0ce8033a561cac8b5

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

packages/kumo-figma/src/build-phosphor-icons.ts

170lines · modecode

1#!/usr/bin/env npx tsx
2/**
3 * Build Phosphor Icons Data
4 *
5 * Extracts specific Phosphor icons needed by Figma component generators.
6 * This runs at BUILD TIME before bundling the plugin.
7 *
8 * Usage:
9 * pnpm --filter @cloudflare/kumo-figma build:data
10 *
11 * Output:
12 * packages/kumo-figma/src/generated/phosphor-icons.json
13 */
14
15import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
16import { join, dirname } from "node:path";
17import { fileURLToPath } from "node:url";
18
19const __filename = fileURLToPath(import.meta.url);
20const __dirname = dirname(__filename);
21
22/**
23 * Icons used by Figma component generators.
24 * Maps our icon IDs (ph-*) to Phosphor icon names.
25 */
26const REQUIRED_ICONS: Record<string, string> = {
27 // DEFAULT_ICONS from icon-utils.ts
28 "ph-plus": "plus",
29 "ph-arrows-clockwise": "arrows-clockwise",
30 "ph-check": "check",
31 "ph-minus": "minus",
32 "ph-arrow-right": "arrow-right",
33
34 // Navigation icons
35 "ph-caret-down": "caret-down",
36 "ph-caret-up": "caret-up",
37 "ph-caret-left": "caret-left",
38 "ph-caret-right": "caret-right",
39 "ph-caret-double-left": "caret-double-left",
40 "ph-caret-double-right": "caret-double-right",
41 "ph-caret-up-down": "caret-up-down",
42
43 // UI icons
44 "ph-x": "x",
45 "ph-eye": "eye",
46 "ph-eye-slash": "eye-slash",
47 "ph-clipboard": "clipboard",
48 "ph-copy": "copy",
49 "ph-trash": "trash",
50 "ph-pencil": "pencil",
51 "ph-download": "download",
52 "ph-share": "share",
53 "ph-sign-out": "sign-out",
54 "ph-user": "user",
55 "ph-gear": "gear",
56 "ph-info": "info",
57 "ph-warning": "warning",
58 "ph-magnifying-glass": "magnifying-glass",
59 "ph-house": "house",
60 "ph-bell": "bell",
61 "ph-database": "database",
62 "ph-globe-hemisphere-west": "globe-hemisphere-west",
63};
64
65/**
66 * Icon data structure for Figma generator
67 */
68type IconData = {
69 id: string;
70 viewBox: string;
71 content: string;
72};
73
74/**
75 * Find the @phosphor-icons/core package in node_modules
76 */
77function findPhosphorCore(): string {
78 // Try common locations
79 const possiblePaths = [
80 // pnpm structure
81 join(
82 __dirname,
83 "../../../node_modules/.pnpm/@phosphor-icons+core@2.1.1/node_modules/@phosphor-icons/core",
84 ),
85 // Standard node_modules
86 join(__dirname, "../../node_modules/@phosphor-icons/core"),
87 join(__dirname, "../node_modules/@phosphor-icons/core"),
88 ];
89
90 for (const path of possiblePaths) {
91 if (existsSync(path)) {
92 return path;
93 }
94 }
95
96 throw new Error(
97 "@phosphor-icons/core not found. Run: pnpm add -D @phosphor-icons/core --filter @cloudflare/kumo-figma",
98 );
99}
100
101/**
102 * Parse SVG content to extract viewBox and inner content
103 */
104function parseSvg(
105 svgContent: string,
106): { viewBox: string; content: string } | null {
107 const viewBoxMatch = svgContent.match(/viewBox="([^"]+)"/);
108 const contentMatch = svgContent.match(/<svg[^>]*>([\s\S]*?)<\/svg>/);
109
110 if (!viewBoxMatch || !contentMatch) {
111 return null;
112 }
113
114 return {
115 viewBox: viewBoxMatch[1],
116 content: contentMatch[1].trim(),
117 };
118}
119
120// Main execution
121console.log("📖 Building Phosphor icons data...");
122
123const phosphorPath = findPhosphorCore();
124const assetsPath = join(phosphorPath, "assets/regular");
125
126console.log(`✅ Found @phosphor-icons/core at ${phosphorPath}`);
127
128const icons: IconData[] = [];
129const missingIcons: string[] = [];
130
131for (const [iconId, phosphorName] of Object.entries(REQUIRED_ICONS)) {
132 const svgPath = join(assetsPath, `${phosphorName}.svg`);
133
134 if (!existsSync(svgPath)) {
135 console.warn(`⚠️ Missing icon: ${phosphorName} (${iconId})`);
136 missingIcons.push(iconId);
137 continue;
138 }
139
140 const svgContent = readFileSync(svgPath, "utf-8");
141 const parsed = parseSvg(svgContent);
142
143 if (!parsed) {
144 console.warn(`⚠️ Failed to parse: ${phosphorName}`);
145 missingIcons.push(iconId);
146 continue;
147 }
148
149 icons.push({
150 id: iconId,
151 viewBox: parsed.viewBox,
152 content: parsed.content,
153 });
154}
155
156console.log(`✅ Extracted ${icons.length} icons`);
157
158if (missingIcons.length > 0) {
159 console.warn(`⚠️ Missing icons: ${missingIcons.join(", ")}`);
160}
161
162// Ensure generated directory exists
163const generatedDir = join(__dirname, "generated");
164mkdirSync(generatedDir, { recursive: true });
165
166// Write icon data as JSON
167const outputPath = join(generatedDir, "phosphor-icons.json");
168writeFileSync(outputPath, JSON.stringify(icons, null, 2));
169
170console.log(`✅ Wrote ${outputPath}`);
171