| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- #!/usr/bin/env node
- /**
- * Checks that every icon a node asks for is one Lucide actually has.
- *
- * Nodes declare an icon with `@icon <name>` and the editor resolves the name to
- * a Lucide component. A name Lucide does not have does not fail anything - it
- * quietly becomes the default icon - so the only way to notice is to look at
- * every node on a canvas. This looks instead:
- *
- * node scripts/gen-node-icons.js
- *
- * Exits non-zero and names the nodes if any icon cannot be resolved, so it can
- * go in a pre-commit hook or CI.
- */
- const fs = require('fs')
- const path = require('path')
- const ROOT = path.resolve(__dirname, '..')
- const NODES_DIR = path.join(ROOT, 'nodes')
- // Kept in step with ICON_ALIASES in webui/src/config/nodeCategories.ts - this
- // script has no way to read the TypeScript one.
- const ALIASES = {
- images: 'GalleryHorizontalEnd',
- 'octagon-x': 'XOctagon',
- }
- function pascal(name) {
- return name.split('-').map((w) => (w ? w[0].toUpperCase() + w.slice(1) : '')).join('')
- }
- function collectNodeFiles(dir) {
- const out = []
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
- const full = path.join(dir, entry.name)
- if (entry.isDirectory()) out.push(...collectNodeFiles(full))
- else if (entry.name.endsWith('.js')) out.push(full)
- }
- return out
- }
- function iconsInUse() {
- const found = new Map() // icon name -> [node ids]
- for (const file of collectNodeFiles(NODES_DIR)) {
- const code = fs.readFileSync(file, 'utf8')
- const icon = code.match(/@icon\s+([a-z0-9-]+)/i)
- const id = code.match(/@node\s+([a-z0-9-]+)/i)
- if (!icon) continue
- const name = icon[1].toLowerCase()
- if (!found.has(name)) found.set(name, [])
- found.get(name).push(id ? id[1] : path.basename(file))
- }
- return found
- }
- function main() {
- const inUse = iconsInUse()
- let lucide
- try {
- lucide = require(path.join(ROOT, 'webui/node_modules/lucide-react'))
- } catch (e) {
- console.error('Could not load lucide-react from webui/node_modules - run npm install first')
- process.exit(2)
- }
- const unresolved = []
- for (const name of [...inUse.keys()].sort()) {
- const component = ALIASES[name] || pascal(name)
- if (!lucide[component]) unresolved.push([name, component, inUse.get(name)])
- }
- if (unresolved.length) {
- console.error('These icon names do not exist in lucide-react:')
- for (const [name, component, nodes] of unresolved) {
- console.error(` @icon ${name} (tried ${component}) - used by ${nodes.join(', ')}`)
- }
- console.error('\nAdd an alias in webui/src/config/nodeCategories.ts, or change the')
- console.error('node to a name Lucide has: https://lucide.dev/icons')
- process.exit(1)
- }
- const files = collectNodeFiles(NODES_DIR)
- const withIcon = [...inUse.values()].reduce((n, ids) => n + ids.length, 0)
- console.log(`${inUse.size} distinct icons across ${withIcon} nodes - all resolve`)
- if (withIcon < files.length) {
- console.log(`${files.length - withIcon} node file(s) declare no @icon and will show their initial`)
- }
- }
- main()
|