forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import type { IconifyJSON } from '@iconify-json/lucide'
2import { promises as fs } from 'node:fs'
3import { fileURLToPath } from 'node:url'
4import path from 'node:path'
5import {
6 ADDITIONAL_ICONS,
7 EXTENSION_ICONS,
8 FILENAME_ICONS,
9 COMPOUND_EXTENSIONS,
10 DEFAULT_ICON,
11} from '../app/utils/file-icons.ts'
12
13const rootDir = process.cwd()
14const outputDevPath = path.join(rootDir, 'public', 'file-tree-sprite.svg')
15const outputStagePath = path.join(rootDir, 'public-dev', 'file-tree-sprite.svg')
16const outputProdPath = path.join(rootDir, 'public-prod', 'file-tree-sprite.svg')
17
18const COLLECTION_NAMES = ['lucide', 'simple-icons', 'svg-spinners', 'vscode-icons']
19
20const COLLECTION_REGEXP = new RegExp(`^(${COLLECTION_NAMES.join('|')})-(.+)$`)
21
22async function loadCollections() {
23 const collections: { [key: string]: IconifyJSON } = {}
24 for (const name of COLLECTION_NAMES) {
25 const filePathUrl = import.meta.resolve(`@iconify-json/${name}/icons.json`)
26 const filePath = fileURLToPath(filePathUrl)
27 const raw = await fs.readFile(filePath, 'utf8')
28 collections[name] = JSON.parse(raw)
29 }
30 return collections
31}
32
33function groupByCollection(iconNames: string[]) {
34 const grouped: { [key: string]: string[] } = {}
35 for (const name of iconNames) {
36 const [, group, iconName] = name.match(COLLECTION_REGEXP) || []
37 if (group && iconName) {
38 grouped[group] ||= []
39 grouped[group].push(iconName)
40 }
41 }
42 return grouped
43}
44
45function buildSprite(
46 grouped: { [key: string]: string[] },
47 collections: { [key: string]: IconifyJSON },
48) {
49 let symbols = ''
50 Object.entries(grouped).forEach(([prefix, iconNames]) => {
51 const collection = collections[prefix]
52
53 if (!collection?.icons) return
54
55 const defaultWidth = collection.width ?? 16
56 const defaultHeight = collection.height ?? 16
57
58 iconNames.forEach(name => {
59 const icon = collection.icons[name]
60
61 if (!icon?.body) return
62
63 const width = icon.width ?? defaultWidth
64 const height = icon.height ?? defaultHeight
65 const viewBox = `0 0 ${width} ${height}`
66 const id = `${collection.prefix}-${name}`
67 symbols += `<symbol id="${id}" viewBox="${viewBox}">${icon.body}</symbol>`
68 })
69 })
70 return `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">${symbols}</svg>\n`
71}
72
73async function main() {
74 const collections = await loadCollections()
75 const iconNames = [
76 ...Object.values(EXTENSION_ICONS),
77 ...Object.values(FILENAME_ICONS),
78 ...Object.values(COMPOUND_EXTENSIONS),
79 ...Object.values(ADDITIONAL_ICONS),
80 DEFAULT_ICON,
81 ]
82 const grouped = groupByCollection(iconNames)
83 const sprite = buildSprite(grouped, collections)
84 await Promise.all([
85 fs.mkdir(path.dirname(outputDevPath), { recursive: true }),
86 fs.mkdir(path.dirname(outputStagePath), { recursive: true }),
87 fs.mkdir(path.dirname(outputProdPath), { recursive: true }),
88 ])
89 await Promise.all([
90 fs.writeFile(outputDevPath, sprite, 'utf8'),
91 fs.writeFile(outputStagePath, sprite, 'utf8'),
92 fs.writeFile(outputProdPath, sprite, 'utf8'),
93 ])
94}
95
96main().catch(error => {
97 console.error(error)
98 process.exitCode = 1
99})