build-icons.js
6.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
* This is an advanced example for creating icon bundles for Iconify SVG Framework.
*
* It creates a bundle from:
* - All SVG files in a directory.
* - Custom JSON files.
* - Iconify icon sets.
* - SVG framework.
*
* This example uses Iconify Tools to import and clean up icons.
* For Iconify Tools documentation visit https://docs.iconify.design/tools/tools2/
*/
import { promises as fs } from 'node:fs';
import { dirname, join } from 'node:path';
// Installation: npm install --save-dev @iconify/tools @iconify/utils @iconify/json @iconify/iconify
import { cleanupSVG, importDirectory, isEmptyColor, parseColors, runSVGO } from '@iconify/tools';
import { getIcons, getIconsCSS, stringToIcon } from '@iconify/utils';
const sources = {
svg: [
// {
// dir: 'src/assets/images/iconify-svg',
// monotone: true,
// prefix: 'custom',
// },
// {
// dir: 'emojis',
// monotone: false,
// prefix: 'emoji',
// },
],
icons: [
// 'mdi:home',
// 'mdi:account',
// 'mdi:login',
// 'mdi:logout',
// 'octicon:book-24',
// 'octicon:code-square-24',
],
json: [
// Custom JSON file
// 'json/gg.json',
// Iconify JSON file (@iconify/json is a package name, /json/ is directory where files are, then filename)
require.resolve('@iconify-json/ri/icons.json'),
{
filename: require.resolve('@iconify-json/mdi/icons.json'),
icons: [
'language-typescript',
'language-javascript',
],
},
{
filename: require.resolve('@iconify-json/bxl/icons.json'),
icons: [
'facebook',
'twitter',
'github',
'google',
'linkedin',
],
},
// Custom file with only few icons
// {
// filename: require.resolve('@iconify-json/line-md/icons.json'),
// icons: [
// 'home-twotone-alt',
// 'github',
// 'document-list',
// 'document-code',
// 'image-twotone',
// ],
// },
],
};
// File to save bundle to
const target = join(__dirname, 'icons.css');
(async function () {
// Create directory for output if missing
const dir = dirname(target);
try {
await fs.mkdir(dir, {
recursive: true,
});
}
catch (err) {
//
}
const allIcons = [];
/**
* Convert sources.icons to sources.json
*/
if (sources.icons) {
const sourcesJSON = sources.json ? sources.json : (sources.json = []);
// Sort icons by prefix
const organizedList = organizeIconsList(sources.icons);
for (const prefix in organizedList) {
const filename = require.resolve(`@iconify/json/json/${prefix}.json`);
sourcesJSON.push({
filename,
icons: organizedList[prefix],
});
}
}
/**
* Bundle JSON files and collect icons
*/
if (sources.json) {
for (let i = 0; i < sources.json.length; i++) {
const item = sources.json[i];
// Load icon set
const filename = typeof item === 'string' ? item : item.filename;
const content = JSON.parse(await fs.readFile(filename, 'utf8'));
// Filter icons
if (typeof item !== 'string' && item.icons?.length) {
const filteredContent = getIcons(content, item.icons);
if (!filteredContent)
throw new Error(`Cannot find required icons in ${filename}`);
// Collect filtered icons
allIcons.push(filteredContent);
}
else {
// Collect all icons from the JSON file
allIcons.push(content);
}
}
}
/**
* Bundle custom SVG icons and collect icons
*/
if (sources.svg) {
for (let i = 0; i < sources.svg.length; i++) {
const source = sources.svg[i];
// Import icons
const iconSet = await importDirectory(source.dir, {
prefix: source.prefix,
});
// Validate, clean up, fix palette, etc.
await iconSet.forEach(async (name, type) => {
if (type !== 'icon')
return;
// Get SVG instance for parsing
const svg = iconSet.toSVG(name);
if (!svg) {
// Invalid icon
iconSet.remove(name);
return;
}
// Clean up and optimise icons
try {
// Clean up icon code
await cleanupSVG(svg);
if (source.monotone) {
// Replace color with currentColor, add if missing
// If icon is not monotone, remove this code
await parseColors(svg, {
defaultColor: 'currentColor',
callback: (attr, colorStr, color) => {
return !color || isEmptyColor(color) ? colorStr : 'currentColor';
},
});
}
// Optimise
await runSVGO(svg);
}
catch (err) {
// Invalid icon
console.error(`Error parsing ${name} from ${source.dir}:`, err);
iconSet.remove(name);
return;
}
// Update icon from SVG instance
iconSet.fromSVG(name, svg);
});
// Collect the SVG icon
allIcons.push(iconSet.export());
}
}
// Generate CSS from collected icons
const cssContent = allIcons
.map(iconSet => getIconsCSS(iconSet, Object.keys(iconSet.icons), { iconSelector: '.{prefix}-{name}' }))
.join('\n');
// Save the CSS to a file
await fs.writeFile(target, cssContent, 'utf8');
console.log(`Saved CSS to ${target}!`);
})().catch(err => {
console.error(err);
});
/**
* Sort icon names by prefix
*/
function organizeIconsList(icons) {
const sorted = Object.create(null);
icons.forEach(icon => {
const item = stringToIcon(icon);
if (!item)
return;
const prefix = item.prefix;
const prefixList = sorted[prefix] ? sorted[prefix] : (sorted[prefix] = []);
const name = item.name;
if (!prefixList.includes(name))
prefixList.push(name);
});
return sorted;
}