import { readFileSync, writeFileSync, readdirSync } from 'node:fs' import { join } from 'node:path' const SRC_DIR = 'D:\\Code\\verify\\frontend\\src' function getAllFiles(dir, exts) { const results = [] const entries = readdirSync(dir, { withFileTypes: true }) for (const entry of entries) { const fullPath = join(dir, entry.name) if (entry.isDirectory()) { results.push(...getAllFiles(fullPath, exts)) } else if (exts.some(ext => entry.name.endsWith(ext))) { results.push(fullPath) } } return results } function lucideToPascal(kebab) { return kebab .split('-') .map(part => part.charAt(0).toUpperCase() + part.slice(1)) .join('') } function processFile(filePath) { let content = readFileSync(filePath, 'utf-8') const originalContent = content if (!content.includes('@iconify/vue') && !content.includes('icon="lucide:') && !content.includes("icon='lucide:") && !content.includes('h(Icon')) { return null } const usedIcons = new Set() // Step 1: Replace (single line) content = content.replace(/]*)\/>/g, (match, iconName, restAttrs) => { const pascalName = lucideToPascal(iconName) usedIcons.add(pascalName) const trimmedAttrs = restAttrs.trim() if (trimmedAttrs) { return `<${pascalName} ${trimmedAttrs} />` } return `<${pascalName} />` }) // Step 2: Replace multiline content = content.replace(/]*?)icon="lucide:([a-z0-9-]+)"([^>]*?)\/>/gs, (match, before, iconName, after) => { const pascalName = lucideToPascal(iconName) usedIcons.add(pascalName) const newAttrs = (before + after).replace(/\s+/g, ' ').trim() if (newAttrs) { return `<${pascalName} ${newAttrs} />` } return `<${pascalName} />` }) // Step 3: Replace h(Icon, { icon: 'lucide:xxx', ... }) in columns.ts files content = content.replace(/h\(\s*Icon\s*,\s*\{\s*icon:\s*['"]lucide:([a-z0-9-]+)['"]\s*,?\s*([^}]*)\}\s*\)/g, (match, iconName, restProps) => { const pascalName = lucideToPascal(iconName) usedIcons.add(pascalName) const trimmedProps = restProps.trim().replace(/,\s*$/, '') if (trimmedProps) { return `h(${pascalName}, { ${trimmedProps} })` } return `h(${pascalName})` }) // Step 4: Replace import { Icon } from '@iconify/vue' content = content.replace(/import\s*\{\s*Icon\s*\}\s*from\s*['"]@iconify\/vue['"]\s*\n?/g, '') // Step 5: Add lucide-vue-next import if we used any icons if (usedIcons.size > 0 && !content.includes("from 'lucide-vue-next'") && !content.includes('from "lucide-vue-next"')) { const sortedIcons = [...usedIcons].sort() const importLine = `import { ${sortedIcons.join(', ')} } from 'lucide-vue-next'\n` const firstImportMatch = content.match(/^import\s/m) if (firstImportMatch) { const insertPos = content.indexOf(firstImportMatch[0]) content = content.slice(0, insertPos) + importLine + content.slice(insertPos) } else { content = content.replace(/(]*>)/, `$1\n${importLine}`) } } else if (usedIcons.size > 0) { const existingImportMatch = content.match(/import\s*\{([^}]*)\}\s*from\s*['"]lucide-vue-next['"]/s) if (existingImportMatch) { const existingIcons = existingImportMatch[1] .split(',') .map(s => s.trim()) .filter(Boolean) const newIcons = [...usedIcons].filter(icon => !existingIcons.includes(icon)) if (newIcons.length > 0) { const allIcons = [...existingIcons, ...newIcons].sort() const newImport = `import { ${allIcons.join(', ')} } from 'lucide-vue-next'` content = content.replace(existingImportMatch[0], newImport) } } } if (content !== originalContent) { writeFileSync(filePath, content, 'utf-8') return { file: filePath, icons: [...usedIcons] } } return null } const files = getAllFiles(SRC_DIR, ['.vue', '.ts']) const results = [] for (const file of files) { const result = processFile(file) if (result) { results.push(result) } } console.log(`\nProcessed ${results.length} files:`) for (const r of results) { console.log(` ${r.file}: ${r.icons.join(', ')}`) }