feat: expand dashboard with storage, network, and package management features while enhancing UI components and remote services
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user