Files
nadir-webui/scripts/replace-lucide-icons.ts
T

113 lines
2.7 KiB
TypeScript

// scripts/replace-lucide-imports.ts
const lucidePath = '@lucide/svelte';
function parseSpec(spec: string) {
const parts = spec.split(/\s+as\s+/).map((s) => s.trim());
const name = parts[0];
const alias = parts[1] ?? name;
return { alias, name };
}
async function processFile(path: string) {
const file = Bun.file(path);
const text = await file.text();
const regex = /import\s*\{[^}]+\}\s*from\s*["']@lucide\/svelte["'];?/g;
const matches = [...text.matchAll(regex)];
if (matches.length === 0) return;
let updatedText = text;
for (const match of matches) {
const block = match[0];
const inside = block.match(/\{([^}]+)\}/);
if (!inside) continue;
const items = inside[1]
.split(',')
.map((s) => s.trim())
.filter(Boolean);
const resolved: string[] = [];
for (const item of items) {
const { name } = parseSpec(item);
const path = await resolveIconPath(name);
resolved.push(path);
}
const replacement = transformImportBlock(block, resolved);
updatedText = updatedText.replace(block, replacement);
}
if (updatedText !== text) {
console.log(`Updated ${path}`);
await Bun.write(path, updatedText);
}
}
/**
* Safe import builder with fallback prompt
*/
async function resolveIconPath(name: string): Promise<string> {
let cleanName = name;
if (cleanName.endsWith('Icon')) {
cleanName = cleanName.slice(0, -4);
}
const kebab = toKebab(cleanName);
const candidate = `${lucidePath}/icons/${kebab}`;
// Bun runtime check: verify physical file exists in dist/icons
const file = Bun.file(`node_modules/${lucidePath}/dist/icons/${kebab}.js`);
if (await file.exists()) return candidate;
// fallback interactive prompt
const input = await prompt(
`Icon not found: "${name}" → "${kebab}". Enter correct kebab name (or press enter to skip): `
);
if (!input) return candidate; // fallback anyway
return `${lucidePath}/icons/${input}`;
}
function toKebab(input: string): string {
return input
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
.replace(/([a-zA-Z])([0-9])/g, '$1-$2') // handle digit transitions like Trash2 -> trash-2
.toLowerCase();
}
function transformImportBlock(block: string, resolved: string[]): string {
const inside = block.match(/\{([^}]+)\}/);
if (!inside) return block;
const items = inside[1]
.split(',')
.map((s) => s.trim())
.filter(Boolean);
const imports: string[] = [];
for (const item of items) {
const { alias } = parseSpec(item);
const iconPath = resolved.shift();
imports.push(`import ${alias} from "${iconPath}";`);
}
return imports.join('\n');
}
const glob = new Bun.Glob('src/**/*.{ts,tsx,js,jsx,svelte}');
for await (const file of glob.scan('.')) {
await processFile(file);
}