mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-19 07:36:52 +00:00
12ba0d0080
Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Alex Grozav <alex@grozav.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
139 lines
4.7 KiB
JavaScript
139 lines
4.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Generates lucideIconData.ts with search metadata (keywords + categories) for Lucide icons.
|
|
* SVG bodies are NOT included — they are loaded via generated chunks at runtime by lucideIconsPlugin
|
|
* (packages/frontend/@n8n/design-system/src/icons/lucide/vite.ts).
|
|
*
|
|
* Usage: node scripts/generate-lucide-icon-data.mjs
|
|
*
|
|
* Output: packages/frontend/@n8n/design-system/src/components/N8nIconPicker/lucideIconData.ts
|
|
*/
|
|
|
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
import { resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const ROOT = resolve(__dirname, '..');
|
|
const COMPONENTS_ROOT = resolve(
|
|
ROOT,
|
|
'packages/frontend/@n8n/design-system/src/components',
|
|
);
|
|
|
|
const LUCIDE_JSON_PATH = resolve(ROOT, 'node_modules/@iconify/json/json/lucide.json');
|
|
const OUTPUT_PATH = resolve(COMPONENTS_ROOT, 'N8nIconPicker/lucideIconData.ts');
|
|
const CACHE_PATH = resolve(ROOT, 'scripts/.lucide-tags-cache.json');
|
|
|
|
// Lucide GitHub raw URL for per-icon metadata
|
|
const LUCIDE_GITHUB_BASE =
|
|
'https://raw.githubusercontent.com/lucide-icons/lucide/main/icons';
|
|
|
|
async function fetchIconMeta(iconName) {
|
|
const url = `${LUCIDE_GITHUB_BASE}/${iconName}.json`;
|
|
try {
|
|
const res = await fetch(url);
|
|
if (!res.ok) return { tags: [], categories: [] };
|
|
const data = await res.json();
|
|
return {
|
|
tags: data.tags ?? [],
|
|
categories: data.categories ?? [],
|
|
};
|
|
} catch {
|
|
return { tags: [], categories: [] };
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log('Reading @iconify/json lucide data...');
|
|
const lucideJson = JSON.parse(readFileSync(LUCIDE_JSON_PATH, 'utf-8'));
|
|
const icons = lucideJson.icons;
|
|
const iconNames = Object.keys(icons).sort();
|
|
console.log(`Found ${iconNames.length} Lucide icons`);
|
|
|
|
// Load or initialize cache
|
|
let cache = {};
|
|
if (existsSync(CACHE_PATH)) {
|
|
try {
|
|
cache = JSON.parse(readFileSync(CACHE_PATH, 'utf-8'));
|
|
console.log(`Loaded ${Object.keys(cache).length} cached icon metadata entries`);
|
|
} catch {
|
|
cache = {};
|
|
}
|
|
}
|
|
|
|
// Fetch metadata for uncached icons
|
|
const uncachedNames = iconNames.filter((name) => !cache[name]);
|
|
if (uncachedNames.length > 0) {
|
|
console.log(`Fetching metadata for ${uncachedNames.length} icons from Lucide GitHub...`);
|
|
const BATCH_SIZE = 50;
|
|
for (let i = 0; i < uncachedNames.length; i += BATCH_SIZE) {
|
|
const batch = uncachedNames.slice(i, i + BATCH_SIZE);
|
|
const results = await Promise.all(batch.map(fetchIconMeta));
|
|
for (let j = 0; j < batch.length; j++) {
|
|
cache[batch[j]] = results[j];
|
|
}
|
|
const progress = Math.min(i + BATCH_SIZE, uncachedNames.length);
|
|
process.stdout.write(`\r Fetched ${progress}/${uncachedNames.length}`);
|
|
}
|
|
console.log('\nSaving cache...');
|
|
writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));
|
|
}
|
|
|
|
// Build the output data
|
|
const allCategories = new Set();
|
|
const entries = [];
|
|
|
|
for (const name of iconNames) {
|
|
const meta = cache[name] ?? { tags: [], categories: [] };
|
|
const body = icons[name]?.body;
|
|
if (!body) continue;
|
|
|
|
// Keywords: icon name parts + tags
|
|
const nameParts = name.split('-').filter((p) => p.length > 0);
|
|
const keywords = [...new Set([...nameParts, ...meta.tags])];
|
|
const categories = meta.categories ?? [];
|
|
categories.forEach((c) => allCategories.add(c));
|
|
|
|
entries.push({ name, keywords, categories });
|
|
}
|
|
|
|
const sortedCategories = [...allCategories].sort();
|
|
|
|
console.log(`Generating TypeScript file with ${entries.length} icons and ${sortedCategories.length} categories...`);
|
|
|
|
// Generate TypeScript output — metadata only, no SVG bodies
|
|
let output = `// AUTO-GENERATED by scripts/generate-lucide-icon-data.mjs — DO NOT EDIT
|
|
// Source: Lucide GitHub (tags/categories). SVG bodies are loaded via generated chunks at runtime.
|
|
// Icons: ${entries.length} | Categories: ${sortedCategories.length}
|
|
|
|
export interface LucideIconMeta {
|
|
\t/** Searchable keywords: icon name parts + Lucide tags */
|
|
\tkeywords: string[];
|
|
\t/** Lucide categories this icon belongs to */
|
|
\tcategories: string[];
|
|
}
|
|
|
|
export const lucideIcons: Record<string, LucideIconMeta> = {\n`;
|
|
|
|
for (const entry of entries) {
|
|
const kw = JSON.stringify(entry.keywords);
|
|
const cats = JSON.stringify(entry.categories);
|
|
output += `\t'${entry.name}': { keywords: ${kw}, categories: ${cats} },\n`;
|
|
}
|
|
|
|
output += `};\n\n`;
|
|
|
|
output += `/** All unique Lucide icon categories, sorted alphabetically */\nexport const lucideCategories: string[] = ${JSON.stringify(sortedCategories, null, '\t')};\n`;
|
|
|
|
writeFileSync(OUTPUT_PATH, output);
|
|
|
|
const sizeKB = Math.round(Buffer.byteLength(output) / 1024);
|
|
console.log(`\nDone! Written to: ${OUTPUT_PATH}`);
|
|
console.log(`File size: ${sizeKB} KB`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|