Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /**
* CSV parser for icon metadata
* Pure CSV-to-ParsedIcon[] parser with no I/O side effects
*/
export type IconType = 'standard' | 'utility' | 'action' | 'doctype' | 'custom';
export interface IconRow {
icon_name: string;
icon_type: string;
synonyms: string;
color: string;
has_ltr_rtl: string;
}
export interface ParsedIcon {
id: string;
icon_name: string;
icon_type: IconType;
file_name: string;
synonyms: string[];
color: string;
color_hex?: string | null;
has_ltr_rtl: 'yes' | 'no' | '';
ltr_svg: string | null;
rtl_svg: string | null;
common_svg: string | null;
}
/**
* Parse CSV content into structured icon objects
* @param csvContent - Raw CSV string content
* @param colorPalette - Palette for resolving color_hex during parse
* @returns Array of parsed icon objects
*/
export function parseIconsCsv(csvContent: string, colorPalette: Record<string, string>): ParsedIcon[] {
const rows = csvContent.trim().split('\n');
if (rows.length === 0) {
return [];
}
const headers = rows[0].split(',').map((header) => header.trim());
const result = rows.slice(1).map((row) => {
// Normalize all quote types to straight quotes
let normalizedRow = row.replace(/[""]/g, '"');
// Handle comma within quoted synonyms field
const synonymStart = normalizedRow.indexOf('"');
const synonymEnd = normalizedRow.lastIndexOf('"');
if (synonymStart !== -1 && synonymEnd !== -1 && synonymStart < synonymEnd) {
const synonymSub = normalizedRow.substring(synonymStart, synonymEnd + 1);
normalizedRow = normalizedRow.replace(synonymSub, synonymSub.replaceAll(',', '|'));
}
const values = normalizedRow.split(',');
const iconRow = headers.reduce(
(obj, header, index) => {
const value = (values[index] || '').trim();
if (header === 'has_ltr_rtl') {
obj[header] = value.toLowerCase();
} else if (header === 'icon_type') {
const lowerValue = value.toLowerCase();
if (lowerValue.includes('standard')) obj[header] = 'standard';
else if (lowerValue.includes('utility')) obj[header] = 'utility';
else if (lowerValue.includes('action')) obj[header] = 'action';
else if (lowerValue.includes('doctype')) obj[header] = 'doctype';
else if (lowerValue.includes('custom')) obj[header] = 'custom';
else obj[header] = value;
} else if (header === 'synonyms') {
const synonyms = value
.split('|')
.map((syn) =>
syn
.trim()
.replace(/[^a-zA-Z0-9_\s]/g, '')
.toLowerCase()
.replace(/_/g, ' ')
.trim(),
)
.filter((syn) => syn.length > 0);
obj[header] = [...new Set(synonyms)];
} else {
obj[header] = value;
}
return obj;
},
{} as Record<string, any>,
);
return iconRow;
});
// Transform into ParsedIcon format with derived attributes
const parsedIcons: ParsedIcon[] = result.map((icon) => ({
id: `${icon.icon_name}--${icon.icon_type}`,
icon_name: icon.icon_name || '',
icon_type: icon.icon_type || 'standard',
file_name: icon.icon_name ? `${icon.icon_name}.svg` : '',
synonyms: icon.synonyms || [],
color: icon.color || '',
color_hex: resolveColor(icon.color || '', colorPalette),
has_ltr_rtl: icon.has_ltr_rtl || '',
ltr_svg: null, // Will be set by caller
rtl_svg: null, // Will be set by caller
common_svg: null, // Will be set by caller
}));
return parsedIcons;
}
/**
* Resolve color string to hex value using color palette
* @param colorString - Color name from CSV (e.g., "blue-50" or "#1b96ff")
* @param colorPalette - Color palette mapping
* @returns Hex color string or null if not found
*/
export function resolveColor(colorString: string, colorPalette: Record<string, string>): string | null {
const trimmed = colorString.trim();
if (trimmed.startsWith('#')) {
return trimmed;
}
if (!trimmed) {
return null;
}
const paletteKey = `PALETTE_${trimmed.toUpperCase().replaceAll('-', '_')}`;
return colorPalette[paletteKey] || null;
}
|