You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
4.0 KiB
154 lines
4.0 KiB
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'fs';
|
|
import { join } from 'path';
|
|
|
|
const ICONS_SOURCE = join(process.cwd(), 'static/lucide/icons');
|
|
const ICONS_TARGET = join(process.cwd(), 'src/uix/air/icons/lib');
|
|
const INDEX_FILE = join(process.cwd(), 'src/uix/air/icons/sound.svelte.ts');
|
|
|
|
function getExistingIcons() {
|
|
const files = readdirSync(ICONS_TARGET);
|
|
return new Set(files.map((f) => f.replace('.svelte', '')));
|
|
}
|
|
|
|
function parseSvgContent(svgContent) {
|
|
// Extract all child elements (path, circle, rect, polyline, line, polygon, ellipse)
|
|
const elements = [];
|
|
|
|
const patterns = [
|
|
/<path\s+([^>]*)>/g,
|
|
/<circle\s+([^>]*)>/g,
|
|
/<rect\s+([^>]*)>/g,
|
|
/<polyline\s+([^>]*)>/g,
|
|
/<line\s+([^>]*)>/g,
|
|
/<polygon\s+([^>]*)>/g,
|
|
/<ellipse\s+([^>]*)>/g
|
|
];
|
|
|
|
for (const pattern of patterns) {
|
|
let match;
|
|
while ((match = pattern.exec(svgContent)) !== null) {
|
|
const tagName = pattern.source.replace('\\s+([^>]*)>', '').replace('<', '').replace('\\', '');
|
|
const attrs = match[1];
|
|
elements.push(`<${tagName} ${attrs}>`);
|
|
}
|
|
}
|
|
|
|
return elements.join('\n\t');
|
|
}
|
|
|
|
function toPascalCase(str) {
|
|
return str
|
|
.split(/[-_]/)
|
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join('');
|
|
}
|
|
|
|
function convertIcon(iconName) {
|
|
const svgPath = join(ICONS_SOURCE, `${iconName}.svg`);
|
|
|
|
if (!existsSync(svgPath)) {
|
|
return null;
|
|
}
|
|
|
|
const svgContent = readFileSync(svgPath, 'utf-8');
|
|
const innerContent = parseSvgContent(svgContent);
|
|
|
|
// Generate Svelte component
|
|
const component = `<script lang="ts">
|
|
import Icon from '../icon.svelte';
|
|
import type { AirIconProps } from '../types';
|
|
|
|
let props: AirIconProps = $props();
|
|
</script>
|
|
|
|
<Icon {...props}>
|
|
${innerContent}
|
|
</Icon>
|
|
`;
|
|
|
|
return component;
|
|
}
|
|
|
|
function updateIndex(icons) {
|
|
const existingIndex = readFileSync(INDEX_FILE, 'utf-8');
|
|
const lines = existingIndex.split('\n');
|
|
|
|
// Find the last export line (before export type)
|
|
const lastExportLine = lines.findIndex((l) => l.startsWith('export type'));
|
|
|
|
// Collect existing exports
|
|
const existingExports = new Set();
|
|
lines.forEach((line) => {
|
|
const match = line.match(/export \{ default as (\w+) \}/);
|
|
if (match) existingExports.add(match[1]);
|
|
});
|
|
|
|
// Add new exports
|
|
const newExports = [];
|
|
for (const icon of icons) {
|
|
const pascalName = toPascalCase(icon);
|
|
if (!existingExports.has(pascalName)) {
|
|
newExports.push(`export { default as ${pascalName} } from './lib/${icon}.svelte';`);
|
|
existingExports.add(pascalName);
|
|
}
|
|
}
|
|
|
|
if (newExports.length === 0) return;
|
|
|
|
// Insert new exports before the export type line
|
|
lines.splice(lastExportLine, 0, ...newExports);
|
|
|
|
// Sort exports (keep TooltipContext first, then alphabetically)
|
|
const rootLine = lines[0];
|
|
const otherExports = lines.slice(1, lastExportLine + newExports.length);
|
|
const typeLine = lines[lastExportLine + newExports.length];
|
|
|
|
otherExports.sort();
|
|
|
|
const newContent = [
|
|
rootLine,
|
|
...otherExports,
|
|
typeLine,
|
|
...lines.slice(lastExportLine + newExports.length + 1)
|
|
].join('\n');
|
|
|
|
writeFileSync(INDEX_FILE, newContent);
|
|
}
|
|
|
|
function main() {
|
|
const existingIcons = getExistingIcons();
|
|
const allSvgFiles = readdirSync(ICONS_SOURCE).filter((f) => f.endsWith('.svg'));
|
|
const missingIcons = allSvgFiles
|
|
.map((f) => f.replace('.svg', ''))
|
|
.filter((name) => !existingIcons.has(name));
|
|
|
|
console.log(`Found ${missingIcons.length} icons to convert...`);
|
|
|
|
if (missingIcons.length === 0) {
|
|
console.log('No new icons to convert!');
|
|
return;
|
|
}
|
|
|
|
// Limit to first 100 for testing
|
|
const toConvert = process.argv[2] === 'all' ? missingIcons : missingIcons.slice(0, 100);
|
|
|
|
console.log(`Converting ${toConvert.length} icons...`);
|
|
|
|
const converted = [];
|
|
|
|
for (const iconName of toConvert) {
|
|
const component = convertIcon(iconName);
|
|
if (component) {
|
|
const targetPath = join(ICONS_TARGET, `${iconName}.svelte`);
|
|
writeFileSync(targetPath, component);
|
|
converted.push(iconName);
|
|
console.log(`✓ ${iconName}`);
|
|
}
|
|
}
|
|
|
|
updateIndex(converted);
|
|
console.log(`\nDone! Converted ${converted.length} icons.`);
|
|
}
|
|
|
|
main();
|