@epiled/icon-font-builder 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License Copyright (c) 2026 Felipe De Andrade
2
+
3
+ Permission is hereby granted,
4
+ free of charge, to any person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use, copy, modify, merge,
7
+ publish, distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice
12
+ (including the next paragraph) shall be included in all copies or substantial
13
+ portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # icon-font-builder
2
+
3
+ Generate icon fonts from SVG files.
4
+
5
+ ## Install
6
+
7
+ npm install @epiled/icon-font-builder
8
+
9
+ ## CLI
10
+
11
+ npx @epiled/icon-font-builder
12
+
13
+ ## Node API
14
+
15
+ import { buildIcons } from "icon-font-builder";
16
+
17
+ await buildIcons({
18
+ inputDir: "src/icons",
19
+ outputDir: "dist/fonts",
20
+ cssClass: "icon",
21
+ fontName: "icons"
22
+ });
package/bin/cli.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { buildIcons } from "../src/build-icons.js";
4
+
5
+ try {
6
+ await buildIcons();
7
+ console.log("✓ Icon font generated successfully");
8
+ } catch (error) {
9
+ console.error("✖ Error generating icons:");
10
+ console.error(error);
11
+ process.exit(1);
12
+ }
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <defs>
5
+ <font id="Epiled" horiz-adv-x="1000">
6
+ <font-face font-family="Epiled"
7
+ units-per-em="1000" ascent="1000"
8
+ descent="0" />
9
+ <missing-glyph horiz-adv-x="0" />
10
+ <glyph glyph-name="add-user"
11
+ unicode="&#xE001;"
12
+ horiz-adv-x="1000" d="M18.01171875 145.3720703125C18.01171875 230.4296875 52.6298828125 291.6953125 130.431640625 324.529296875C168.6845703125 340.4853515624999 207.8525390625 354.150390625 247.728515625 365.4521484375C312.919921875 384.2480468749999 356.3408203125 416.1298828125 332.0732421875 501.544921875C296.384765625 526.05078125 269.0234375 576.2529296875 255.4609375 619.3173828125C227.7421875 708.1826171875 212.515625 805.375 255.4609375 896.73828125C291.982421875 974.18359375 361.337890625 1000.236328125 441.5185546875 999.998046875C588.5556640625 999.998046875 675.3984375 882.1064453125 654.69921875 736.97265625C646.2529296875 677.4912109375 648.2753906250001 620.626953125 609.017578125 571.7333984375C600.771484375 558.421875 591.2919921875001 545.9150390625 580.7041015625 534.37890625C415.583984375 396.0263671875 395.00390625 346.775390625 422.6025390625 145.3720703125L18.01171875 145.3720703125zM954.484375 362.1220703125C941.7001953125 387.767578125 924.5517578125002 410.9951171875 903.806640625 430.763671875C902.2597656250002 432.4296875 900.5947265625001 433.9755859375 899.0478515625 435.4033203125C868.904296875 462.7607421875 832.572265625 482.3964843749999 793.1708984375 492.6240234375C790.5537109375 493.2187500000001 787.8173828125 493.9326171875001 785.0810546875 494.408203125C767.2080078125 498.4873046875 748.9287109375 500.5224609375 730.595703125 500.4755859375C707.5869140625 500.5234375 684.6845703125 497.3603515625 662.5498046875 491.0771484375001L656.1259765625 489.1738281250001C615.2773437500001 476.3525390625001 578.41015625 453.2490234375001 549.0595703125001 422.0791015625001L549.0595703125001 422.0791015625001C507.7802734375001 378.4111328125 483.6953125000001 321.2822265625 481.2509765625001 261.2421875C481.2509765625001 257.6738281250001 481.2509765625001 254.1044921875001 481.2509765625001 250.4169921875V248.39453125C481.2509765625001 240.423828125 481.2509765625001 232.572265625 482.5595703125 224.6015625C482.5595703125 221.8652343749999 482.5595703125 219.1289062499999 483.5117187500001 216.51171875C495.9794921875001 123.6474609375 559.3623046875 45.5673828125 647.6796875 14.2753906249999C671.9248046875 5.6201171874999 697.3720703125 0.8037109375 723.1025390625001 0C725.9570312500001 0 728.8125000000001 0 731.6679687500001 0C734.5224609375001 0 737.3779296875 0 740.232421875 0C773.4423828125 0.9912109375 806.1044921875 8.7216796875 836.2353515625001 22.7216796875C961.6513671875 80.2304687500001 1016.7011718750002 228.51953125 959.1923828125002 353.9355468750001C957.912109375 356.7285156250001 956.5800781250002 359.4970703125001 955.1972656250002 362.2402343750001L954.484375 362.1220703125zM772.4716796875 210.8017578125C748.6787109374999 181.4179687499999 796.263671875 107.8994140625 727.0283203125 111.23046875C671.2353515625 113.966796875 707.3994140625 177.373046875 692.0537109375 209.13671875C659.3388671875 229.1220703125 591.5302734375 187.0097656249999 592.36328125 251.25C593.0771484375 310.7314453125 656.484375 272.7822265625 691.578125 291.578125C707.6376953125001 324.5302734375001 670.521484375 387.69921875 729.2890625 388.8896484375001C794.1240234375 390.1982421875001 752.3681640625001 322.5087890625 770.92578125 290.5078125C802.5693359375 270.759765625 870.4970703125 313.2294921875001 869.6640625 248.990234375C868.9501953125 189.6259765625 806.1376953125 225.91015625 772.4716796875 210.8017578125L772.4716796875 210.8017578125z" />
13
+ <glyph glyph-name="arrow"
14
+ unicode="&#xE002;"
15
+ horiz-adv-x="1000" d="M77.2744140625 189.03125H522.251953125H922.9482421874998C991.5166015624998 189.03125 1025.8007812499998 283.4316406249999 977.2324218749998 338.7685546875L607.2480468749999 760.3115234375C547.9638671874999 827.8544921875 451.5429687499999 827.8544921875 392.2597656249999 760.3115234375L251.5507812500001 599.9921875L22.2744140625 338.7685546875C-25.578125 283.431640625 8.7060546875 189.03125 77.2744140625 189.03125z" />
16
+ </font>
17
+ </defs>
18
+ </svg>
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <defs>
5
+ <font id="Nova-Font" horiz-adv-x="1000">
6
+ <font-face font-family="Nova-Font"
7
+ units-per-em="1000" ascent="1000"
8
+ descent="0" />
9
+ <missing-glyph horiz-adv-x="0" />
10
+ <glyph glyph-name="add-user"
11
+ unicode="&#xE001;"
12
+ horiz-adv-x="1000" d="M18.01171875 145.3720703125C18.01171875 230.4296875 52.6298828125 291.6953125 130.431640625 324.529296875C168.6845703125 340.4853515624999 207.8525390625 354.150390625 247.728515625 365.4521484375C312.919921875 384.2480468749999 356.3408203125 416.1298828125 332.0732421875 501.544921875C296.384765625 526.05078125 269.0234375 576.2529296875 255.4609375 619.3173828125C227.7421875 708.1826171875 212.515625 805.375 255.4609375 896.73828125C291.982421875 974.18359375 361.337890625 1000.236328125 441.5185546875 999.998046875C588.5556640625 999.998046875 675.3984375 882.1064453125 654.69921875 736.97265625C646.2529296875 677.4912109375 648.2753906250001 620.626953125 609.017578125 571.7333984375C600.771484375 558.421875 591.2919921875001 545.9150390625 580.7041015625 534.37890625C415.583984375 396.0263671875 395.00390625 346.775390625 422.6025390625 145.3720703125L18.01171875 145.3720703125zM954.484375 362.1220703125C941.7001953125 387.767578125 924.5517578125002 410.9951171875 903.806640625 430.763671875C902.2597656250002 432.4296875 900.5947265625001 433.9755859375 899.0478515625 435.4033203125C868.904296875 462.7607421875 832.572265625 482.3964843749999 793.1708984375 492.6240234375C790.5537109375 493.2187500000001 787.8173828125 493.9326171875001 785.0810546875 494.408203125C767.2080078125 498.4873046875 748.9287109375 500.5224609375 730.595703125 500.4755859375C707.5869140625 500.5234375 684.6845703125 497.3603515625 662.5498046875 491.0771484375001L656.1259765625 489.1738281250001C615.2773437500001 476.3525390625001 578.41015625 453.2490234375001 549.0595703125001 422.0791015625001L549.0595703125001 422.0791015625001C507.7802734375001 378.4111328125 483.6953125000001 321.2822265625 481.2509765625001 261.2421875C481.2509765625001 257.6738281250001 481.2509765625001 254.1044921875001 481.2509765625001 250.4169921875V248.39453125C481.2509765625001 240.423828125 481.2509765625001 232.572265625 482.5595703125 224.6015625C482.5595703125 221.8652343749999 482.5595703125 219.1289062499999 483.5117187500001 216.51171875C495.9794921875001 123.6474609375 559.3623046875 45.5673828125 647.6796875 14.2753906249999C671.9248046875 5.6201171874999 697.3720703125 0.8037109375 723.1025390625001 0C725.9570312500001 0 728.8125000000001 0 731.6679687500001 0C734.5224609375001 0 737.3779296875 0 740.232421875 0C773.4423828125 0.9912109375 806.1044921875 8.7216796875 836.2353515625001 22.7216796875C961.6513671875 80.2304687500001 1016.7011718750002 228.51953125 959.1923828125002 353.9355468750001C957.912109375 356.7285156250001 956.5800781250002 359.4970703125001 955.1972656250002 362.2402343750001L954.484375 362.1220703125zM772.4716796875 210.8017578125C748.6787109374999 181.4179687499999 796.263671875 107.8994140625 727.0283203125 111.23046875C671.2353515625 113.966796875 707.3994140625 177.373046875 692.0537109375 209.13671875C659.3388671875 229.1220703125 591.5302734375 187.0097656249999 592.36328125 251.25C593.0771484375 310.7314453125 656.484375 272.7822265625 691.578125 291.578125C707.6376953125001 324.5302734375001 670.521484375 387.69921875 729.2890625 388.8896484375001C794.1240234375 390.1982421875001 752.3681640625001 322.5087890625 770.92578125 290.5078125C802.5693359375 270.759765625 870.4970703125 313.2294921875001 869.6640625 248.990234375C868.9501953125 189.6259765625 806.1376953125 225.91015625 772.4716796875 210.8017578125L772.4716796875 210.8017578125z" />
13
+ <glyph glyph-name="arrow"
14
+ unicode="&#xE002;"
15
+ horiz-adv-x="1000" d="M77.2744140625 189.03125H522.251953125H922.9482421874998C991.5166015624998 189.03125 1025.8007812499998 283.4316406249999 977.2324218749998 338.7685546875L607.2480468749999 760.3115234375C547.9638671874999 827.8544921875 451.5429687499999 827.8544921875 392.2597656249999 760.3115234375L251.5507812500001 599.9921875L22.2744140625 338.7685546875C-25.578125 283.431640625 8.7060546875 189.03125 77.2744140625 189.03125z" />
16
+ </font>
17
+ </defs>
18
+ </svg>
@@ -0,0 +1,31 @@
1
+
2
+
3
+ @font-face {
4
+ font-family: 'Epiled';
5
+ src:
6
+ url('../assets/fonts/Epiled/Epiled.woff2') format('woff2'),
7
+ url('../assets/fonts/Epiled/Epiled.woff') format('woff'),
8
+ url('../assets/fonts/Epiled/Epiled.ttf') format('truetype');
9
+ font-weight: normal;
10
+ font-style: normal;
11
+ font-display: swap;
12
+ }
13
+
14
+ [class^="icon-"], [class*=" icon-"] {
15
+ font-family: 'Epiled' !important;
16
+ speak: none;
17
+ font-style: normal;
18
+ font-weight: normal;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ line-height: 1;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ }
25
+
26
+ .icon-add-user::before {
27
+ content: "\e001";
28
+ }
29
+ .icon-arrow::before {
30
+ content: "\e002";
31
+ }
@@ -0,0 +1,31 @@
1
+
2
+
3
+ @font-face {
4
+ font-family: 'Nova-Font';
5
+ src:
6
+ url('../assets/fonts/Nova-Font/Nova-Font.woff2') format('woff2'),
7
+ url('../assets/fonts/Nova-Font/Nova-Font.woff') format('woff'),
8
+ url('../assets/fonts/Nova-Font/Nova-Font.ttf') format('truetype');
9
+ font-weight: normal;
10
+ font-style: normal;
11
+ font-display: swap;
12
+ }
13
+
14
+ [class^="icon-"], [class*=" icon-"] {
15
+ font-family: 'Nova-Font' !important;
16
+ speak: none;
17
+ font-style: normal;
18
+ font-weight: normal;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ line-height: 1;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ }
25
+
26
+ .icon-add-user::before {
27
+ content: "\e001";
28
+ }
29
+ .icon-arrow::before {
30
+ content: "\e002";
31
+ }
@@ -0,0 +1,73 @@
1
+
2
+ <html lang="pt-br">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Preview — Epiled</title>
6
+ <link rel="stylesheet" href="./css/epiled.css" />
7
+ <style>
8
+ body {
9
+ font-family: sans-serif;
10
+ background: #fafafa;
11
+ padding: 32px;
12
+ }
13
+ h1 {
14
+ font-size: 20px;
15
+ margin-bottom: 24px;
16
+ }
17
+ input {
18
+ padding: 8px;
19
+ font-size: 14px;
20
+ width: 300px;
21
+ margin-bottom: 24px;
22
+ }
23
+ .grid {
24
+ display: grid;
25
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
26
+ gap: 16px;
27
+ }
28
+ .icon-box {
29
+ text-align: center;
30
+ background: #fff;
31
+ border-radius: 8px;
32
+ padding: 12px;
33
+ border: 1px solid #eee;
34
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
35
+ }
36
+ .icon-box i {
37
+ font-size: 32px;
38
+ color: #333;
39
+ }
40
+ .icon-name {
41
+ font-size: 12px;
42
+ color: #666;
43
+ margin-top: 6px;
44
+ word-break: break-all;
45
+ }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <h1>Pré-visualização: Epiled</h1>
50
+ <input
51
+ type="text"
52
+ id="search"
53
+ placeholder="Buscar ícone..."
54
+ onkeyup="filterIcons()"
55
+ />
56
+ <div class="grid" id="icons">
57
+ <div class="icon-box" data-name="icon-add-user">
58
+ <i class="icon icon-add-user"></i>
59
+ <div class="icon-name">icon-add-user</div>
60
+ </div>
61
+ <div class="icon-box" data-name="icon-arrow">
62
+ <i class="icon icon-arrow"></i>
63
+ <div class="icon-name">icon-arrow</div>
64
+ </div>
65
+ </div>
66
+ <script>
67
+ function filterIcons() { const term =
68
+ document.getElementById('search').value.toLowerCase();
69
+ document.querySelectorAll('.icon-box').forEach(box => { box.style.display
70
+ = box.dataset.name.toLowerCase().includes(term) ? '' : 'none'; }); }
71
+ </script>
72
+ </body>
73
+ </html>
package/index.js ADDED
@@ -0,0 +1 @@
1
+ export { buildIcons } from "./src/build-icons.js";
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@epiled/icon-font-builder",
3
+ "version": "1.0.0",
4
+ "description": "Generate icon fonts from SVG files",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "icon-font-builder": "./bin/cli.js"
9
+ },
10
+ "keywords": [
11
+ "icons",
12
+ "icon-font",
13
+ "svg",
14
+ "font",
15
+ "webfont"
16
+ ],
17
+ "author": "Felipe De Andrade (https://github.com/epiled)",
18
+ "license": "MIT",
19
+ "homepage": "https://github.com/epiled/icon-font-builder",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/epiled/icon-font-builder.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/epiled/icon-font-builder/issues"
26
+ },
27
+ "engines": {
28
+ "node": ">=20.12.2"
29
+ },
30
+ "dependencies": {
31
+ "handlebars": "^4.7.8",
32
+ "svg2ttf": "^6.0.3",
33
+ "svgicons2svgfont": "^15.0.1",
34
+ "ttf2woff": "^3.0.0",
35
+ "ttf2woff2": "^8.0.1"
36
+ },
37
+ "devDependencies": {},
38
+ "scripts": {
39
+ "test": "echo \"Error: no test specified\" && exit 1"
40
+ }
41
+ }
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
5
+ <g>
6
+ <path d="M18.444,875.139c0-87.099,35.449-149.835,115.118-183.457c39.171-16.339,79.279-30.332,120.112-41.905
7
+ c66.756-19.247,111.219-51.894,86.369-139.359c-36.545-25.094-64.563-76.501-78.451-120.599
8
+ c-28.384-90.998-43.976-190.523,0-284.079C298.99,26.436,370.01-0.242,452.115,0.002c150.566,0,239.493,120.721,218.297,269.338
9
+ c-8.649,60.909-6.578,119.138-46.778,169.205c-8.444,13.631-18.151,26.438-28.993,38.251
10
+ C425.558,618.469,404.484,668.902,432.745,875.139L18.444,875.139z"/>
11
+ <path d="M977.392,653.187c-13.091-26.261-30.651-50.046-51.894-70.289c-1.584-1.706-3.289-3.289-4.873-4.751
12
+ c-30.867-28.014-68.071-48.121-108.418-58.594c-2.68-0.609-5.482-1.34-8.284-1.827c-18.302-4.177-37.02-6.261-55.793-6.213
13
+ c-23.561-0.049-47.013,3.19-69.679,9.624l-6.578,1.949c-41.829,13.129-79.581,36.787-109.636,68.705l0,0
14
+ c-42.27,44.716-66.933,103.216-69.436,164.697c0,3.654,0,7.309,0,11.085v2.071c0,8.162,0,16.202,1.34,24.364
15
+ c0,2.802,0,5.604,0.975,8.284c12.767,95.093,77.671,175.047,168.108,207.09c24.827,8.863,50.885,13.795,77.233,14.618
16
+ c2.923,0,5.847,0,8.771,0c2.923,0,5.847,0,8.77,0c34.007-1.015,67.453-8.931,98.307-23.267
17
+ c128.426-58.889,184.797-210.737,125.908-339.163c-1.311-2.86-2.675-5.695-4.091-8.504L977.392,653.187z M791.011,808.139
18
+ c-24.364,30.089,24.363,105.372-46.534,101.961c-57.132-2.802-20.1-67.73-35.814-100.256
19
+ c-33.5-20.465-102.936,22.658-102.083-43.124c0.731-60.909,65.66-22.049,101.596-41.296c16.445-33.743-21.562-98.428,38.616-99.647
20
+ c66.391-1.34,23.633,67.974,42.636,100.743c32.403,20.222,101.961-23.267,101.108,42.514
21
+ C889.805,829.823,825.485,792.668,791.011,808.139L791.011,808.139z"/>
22
+ </g>
23
+ </svg>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
5
+ <g>
6
+ <path d="M79.129,830.432h455.657h410.313c70.214,0,105.321-96.666,55.587-153.331l-378.864-431.66
7
+ c-60.707-69.164-159.442-69.164-220.148,0L257.588,409.608L22.809,677.101C-26.192,733.766,8.915,830.432,79.129,830.432z"/>
8
+ </g>
9
+ </svg>
@@ -0,0 +1,34 @@
1
+ import { createConfig } from "./config/create-config.js";
2
+
3
+ import { scanIcons } from "./core/scan-icons.js";
4
+ import { normalizeIcons } from "./core/normalize-naming.js";
5
+ import { generateUnicodeMap } from "./core/generate-unicode-map.js";
6
+ import { buildSvgFont } from "./core/build-svg-font.js";
7
+ import { convertFonts } from "./core/convert-fonts.js";
8
+ import { generateSass } from "./core/generate-sass.js";
9
+ import { generateCss } from "./core/generate-css.js";
10
+ import { generatePreview } from "./core/generate-preview.js";
11
+
12
+ export async function buildIcons(userConfig = {}) {
13
+ const config = createConfig(userConfig);
14
+
15
+ const icons = scanIcons(config.inputDir);
16
+
17
+ const iconsNormalized = normalizeIcons(icons, config);
18
+
19
+ const glyphs = generateUnicodeMap(iconsNormalized);
20
+
21
+ await buildSvgFont(glyphs, config);
22
+
23
+ await convertFonts(config);
24
+
25
+ if (process.argv.slice(2).includes("--sass")) {
26
+ await generateSass(glyphs, config);
27
+ } else {
28
+ await generateCss(glyphs, config);
29
+ }
30
+
31
+ await generatePreview(glyphs, config);
32
+
33
+ return glyphs;
34
+ }
@@ -0,0 +1,27 @@
1
+ // Function to create custom configuration
2
+
3
+ export function createConfig(userConfig = {}) {
4
+ const iconsName = userConfig.iconsName || "Epiled";
5
+
6
+ const fontName = userConfig.font?.fontName || iconsName;
7
+
8
+ const config = {
9
+ inputDir: userConfig.inputDir || "src/assets/svg/icons-ui",
10
+ outputDir: userConfig.outputDir || `dist/assets/fonts/${fontName}`,
11
+ iconsName,
12
+
13
+ font: {
14
+ fontName: userConfig.font?.folderName || fontName,
15
+ folderName: userConfig.font?.folderName || fontName,
16
+ fontFileName: userConfig.font?.fontFileName || fontName,
17
+ fontPath: userConfig.font?.fontPath || "../assets/fonts",
18
+ },
19
+
20
+ css: {
21
+ cssClass: userConfig.css?.cssClass || "icon-",
22
+ cssFileName: userConfig.css?.cssFileName || fontName.toLowerCase(),
23
+ },
24
+ };
25
+
26
+ return config;
27
+ }
@@ -0,0 +1,20 @@
1
+ // Default configuration
2
+
3
+ const fontName = "Epiled";
4
+
5
+ const defaultConfig = {
6
+ inputDir: "src/assets/svg/icons-ui",
7
+ outputDir: `dist/assets/fonts/${fontName}`,
8
+ font: {
9
+ fontName: fontName,
10
+ folderName: fontName,
11
+ fontFileName: fontName,
12
+ fontPath: "../assets/fonts",
13
+ },
14
+ css: {
15
+ cssClass: "icon-",
16
+ cssFileName: fontName.toLowerCase(),
17
+ },
18
+ };
19
+
20
+ export { defaultConfig };
@@ -0,0 +1,44 @@
1
+ // Step: 3
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ async function buildSvgFont(icons, config) {
7
+ const { SVGIcons2SVGFontStream } = await import("svgicons2svgfont");
8
+
9
+ const { font, outputDir } = config;
10
+ const { fontName } = font;
11
+
12
+ const fontPath = path.join(outputDir, `${fontName}.svg`);
13
+
14
+ fs.mkdirSync(outputDir, { recursive: true });
15
+
16
+ return new Promise((resolve, reject) => {
17
+ const fontStream = new SVGIcons2SVGFontStream({
18
+ fontName,
19
+ normalize: true,
20
+ fontHeight: 1000,
21
+ });
22
+
23
+ const writeStream = fs.createWriteStream(fontPath);
24
+
25
+ fontStream.pipe(writeStream);
26
+
27
+ writeStream.on("finish", resolve);
28
+ writeStream.on("error", reject);
29
+
30
+ icons.forEach((icon) => {
31
+ const glyph = fs.createReadStream(icon.path);
32
+
33
+ glyph.metadata = {
34
+ unicode: [icon.unicode],
35
+ name: icon.name,
36
+ };
37
+ fontStream.write(glyph);
38
+ });
39
+
40
+ fontStream.end();
41
+ });
42
+ }
43
+
44
+ export { buildSvgFont };
@@ -0,0 +1,33 @@
1
+ // Step: 4
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ import svg2ttf from "svg2ttf";
7
+ import ttf2woff from "ttf2woff";
8
+ import ttf2woff2 from "ttf2woff2";
9
+
10
+ async function convertFonts(config) {
11
+ const { font, outputDir } = config;
12
+ const { fontName } = font;
13
+
14
+ const svgPath = path.join(outputDir, `${fontName}.svg`);
15
+ const ttfPath = path.join(outputDir, `${fontName}.ttf`);
16
+ const woffPath = path.join(outputDir, `${fontName}.woff`);
17
+ const woff2Path = path.join(outputDir, `${fontName}.woff2`);
18
+
19
+ const svg = fs.readFileSync(svgPath, "utf8");
20
+
21
+ // SVG → TTF
22
+ const ttf = svg2ttf(svg, {});
23
+ fs.writeFileSync(ttfPath, Buffer.from(ttf.buffer));
24
+
25
+ // TTF → WOFF
26
+ const woff = ttf2woff(ttf.buffer);
27
+ fs.writeFileSync(woffPath, Buffer.from(woff.buffer));
28
+
29
+ // TTF → WOFF2
30
+ const woff2 = ttf2woff2(ttf.buffer);
31
+ fs.writeFileSync(woff2Path, Buffer.from(woff2));
32
+ }
33
+ export { convertFonts };
@@ -0,0 +1,60 @@
1
+ // Step: 5
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import url from "url";
6
+ import handlebars from "handlebars";
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const iconsTemplatePath = path.resolve(__dirname, "../templates/icons.css.hbs");
12
+
13
+ const baseIconTemplatePath = path.resolve(
14
+ __dirname,
15
+ "../templates/base-icons.hbs",
16
+ );
17
+
18
+ if (!fs.existsSync(baseIconTemplatePath))
19
+ throw new Error(`Base icons template not found: ${baseIconTemplatePath}`);
20
+
21
+ handlebars.registerPartial(
22
+ "base-icons",
23
+ fs.readFileSync(baseIconTemplatePath, "utf8"),
24
+ );
25
+
26
+ function generateCss(glyphs = [], config) {
27
+ const { font, css } = config;
28
+ const { fontName, folderName, fontFileName, fontPath } = font;
29
+ const { cssClass } = css;
30
+
31
+ if (!fs.existsSync(iconsTemplatePath)) {
32
+ throw new Error(`Template CSS not found: ${iconsTemplatePath}`);
33
+ }
34
+
35
+ if (!Array.isArray(glyphs)) {
36
+ throw new Error(
37
+ "⚠️ generateCss called without valid glyphs array. This task should never run in isolation — it must be triggered by iconsBuild.",
38
+ );
39
+ }
40
+
41
+ const outputPath = path.join("dist/css", `${fontFileName}.css`);
42
+
43
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
44
+
45
+ const cssRaw = fs.readFileSync(iconsTemplatePath, "utf8");
46
+ const cssCompiled = handlebars.compile(cssRaw);
47
+
48
+ const cssParse = cssCompiled({
49
+ fontName,
50
+ folderName,
51
+ fontFileName,
52
+ fontPath,
53
+ cssClass,
54
+ glyphs,
55
+ });
56
+
57
+ fs.writeFileSync(outputPath, cssParse);
58
+ }
59
+
60
+ export { generateCss };
@@ -0,0 +1,46 @@
1
+ // Step: 6
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import url from "url";
6
+ import handlebars from "handlebars";
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const previewTemplatePath = path.resolve(
12
+ __dirname,
13
+ "../templates/icons-preview.hbs",
14
+ );
15
+
16
+ function generatePreview(glyphs = [], config) {
17
+ const { font, css } = config;
18
+ const { fontName } = font;
19
+ const { cssClass, cssFileName } = css;
20
+
21
+ if (!fs.existsSync(previewTemplatePath)) {
22
+ throw new Error(`Template preview not found: ${previewTemplatePath}`);
23
+ }
24
+
25
+ if (!Array.isArray(glyphs)) {
26
+ throw new Error(
27
+ "⚠️ generatePreview called without valid glyphs array. This task should never run in isolation — it must be triggered by iconsBuild.",
28
+ );
29
+ }
30
+
31
+ const previewRaw = fs.readFileSync(previewTemplatePath, "utf8");
32
+ const previewComplied = handlebars.compile(previewRaw);
33
+
34
+ const previewParse = previewComplied({
35
+ fontName,
36
+ cssClass,
37
+ cssFileName,
38
+ glyphs,
39
+ });
40
+
41
+ const outputPath = path.join("dist", `icons-preview.html`);
42
+
43
+ fs.writeFileSync(outputPath, previewParse);
44
+ }
45
+
46
+ export { generatePreview };
@@ -0,0 +1,63 @@
1
+ // Step: 5
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import url from "url";
6
+ import handlebars from "handlebars";
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const iconsTemplatePath = path.resolve(
12
+ __dirname,
13
+ "../templates/icons.scss.hbs",
14
+ );
15
+
16
+ const baseIconTemplatePath = path.resolve(
17
+ __dirname,
18
+ "../templates/base-icons.hbs",
19
+ );
20
+
21
+ if (!fs.existsSync(baseIconTemplatePath))
22
+ throw new Error(`Base icons template not found: ${baseIconTemplatePath}`);
23
+
24
+ handlebars.registerPartial(
25
+ "base-icons",
26
+ fs.readFileSync(baseIconTemplatePath, "utf8"),
27
+ );
28
+
29
+ function generateSass(glyphs = [], config) {
30
+ const { font, css } = config;
31
+ const { fontName, folderName, fontFileName, fontPath } = font;
32
+ const { cssClass } = css;
33
+
34
+ if (!fs.existsSync(iconsTemplatePath)) {
35
+ throw new Error(`Template SASS not found: ${iconsTemplatePath}`);
36
+ }
37
+
38
+ if (!Array.isArray(glyphs)) {
39
+ throw new Error(
40
+ "⚠️ generateSass called without valid glyphs array. This task should never run in isolation — it must be triggered by iconsBuild.",
41
+ );
42
+ }
43
+
44
+ const outputPath = path.join("dist/sass", `_${fontFileName}.scss`);
45
+
46
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
47
+
48
+ const sassRaw = fs.readFileSync(iconsTemplatePath, "utf8");
49
+ const sassCompiled = handlebars.compile(sassRaw);
50
+
51
+ const sassParse = sassCompiled({
52
+ fontName,
53
+ folderName,
54
+ fontFileName,
55
+ fontPath,
56
+ cssClass,
57
+ glyphs,
58
+ });
59
+
60
+ fs.writeFileSync(outputPath, sassParse);
61
+ }
62
+
63
+ export { generateSass };
@@ -0,0 +1,20 @@
1
+ // Step: 2
2
+
3
+ function generateUnicodeMap(icons) {
4
+ const baseCode = 0xe001;
5
+
6
+ return icons
7
+ .sort((a, b) => a.name.localeCompare(b.name))
8
+ .map((icon, index) => {
9
+ const code = baseCode + index;
10
+
11
+ return {
12
+ ...icon,
13
+ unicode: String.fromCharCode(code),
14
+ codepoint: code,
15
+ unicodeHex: code.toString(16),
16
+ };
17
+ });
18
+ }
19
+
20
+ export { generateUnicodeMap };
@@ -0,0 +1,15 @@
1
+ // Step: Normalize
2
+
3
+ function normalizeIcons(icons = [], config) {
4
+ const prefix = config.css.cssClass || "";
5
+
6
+ return icons.map((icon) => ({
7
+ ...icon,
8
+ name:
9
+ prefix && icon.name.startsWith(prefix)
10
+ ? icon.name.slice(prefix.length)
11
+ : icon.name,
12
+ }));
13
+ }
14
+
15
+ export { normalizeIcons };
@@ -0,0 +1,28 @@
1
+ // Step: 1
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ function scanIcons(dir) {
7
+ if (!fs.existsSync(dir)) {
8
+ throw new Error(`Icons directory not found: ${dir}`);
9
+ }
10
+
11
+ const files = fs.readdirSync(dir);
12
+
13
+ if (files.length <= 0) {
14
+ throw new Error(`Not found files inside directory: ${dir}`);
15
+ }
16
+
17
+ const icons = files
18
+ .filter((file) => file.endsWith(".svg"))
19
+ .sort()
20
+ .map((file) => ({
21
+ name: path.basename(file, ".svg"),
22
+ path: path.join(dir, file).replace(/\\/g, "/"),
23
+ }));
24
+
25
+ return icons;
26
+ }
27
+
28
+ export { scanIcons };
@@ -0,0 +1,21 @@
1
+ {{!--
2
+ Base @font-face template used by the icon font pipeline.
3
+ This template defines the font-family and source paths.
4
+
5
+ Variables:
6
+ - fontName → Font family name
7
+ - fontPath → Base path for font assets
8
+ - folderName → Font output directory
9
+ - fontFileName → Font file Name
10
+ --}}
11
+
12
+ @font-face {
13
+ font-family: '{{fontName}}';
14
+ src:
15
+ url('{{fontPath}}/{{folderName}}/{{fontFileName}}.woff2') format('woff2'),
16
+ url('{{fontPath}}/{{folderName}}/{{fontFileName}}.woff') format('woff'),
17
+ url('{{fontPath}}/{{folderName}}/{{fontFileName}}.ttf') format('truetype');
18
+ font-weight: normal;
19
+ font-style: normal;
20
+ font-display: swap;
21
+ }
@@ -0,0 +1,83 @@
1
+ {{!--
2
+ Icon font preview HTML template.
3
+
4
+ This template renders a preview page for all generated icons.
5
+
6
+ Variables:
7
+ - fontName → Font family name
8
+ - glyphs → Array of icon glyphs with 'name' property
9
+ - name → Icon CSS class name (applied without prefix)
10
+ - cssClass → Prefix icon CSS class name
11
+ - cssFileName → CSS file name
12
+ --}}
13
+
14
+ <html lang="pt-br">
15
+ <head>
16
+ <meta charset="UTF-8" />
17
+ <title>Preview — {{fontName}}</title>
18
+ <link rel="stylesheet" href="./css/{{cssFileName}}.css" />
19
+ <style>
20
+ body {
21
+ font-family: sans-serif;
22
+ background: #fafafa;
23
+ padding: 32px;
24
+ }
25
+ h1 {
26
+ font-size: 20px;
27
+ margin-bottom: 24px;
28
+ }
29
+ input {
30
+ padding: 8px;
31
+ font-size: 14px;
32
+ width: 300px;
33
+ margin-bottom: 24px;
34
+ }
35
+ .grid {
36
+ display: grid;
37
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
38
+ gap: 16px;
39
+ }
40
+ .icon-box {
41
+ text-align: center;
42
+ background: #fff;
43
+ border-radius: 8px;
44
+ padding: 12px;
45
+ border: 1px solid #eee;
46
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
47
+ }
48
+ .icon-box i {
49
+ font-size: 32px;
50
+ color: #333;
51
+ }
52
+ .icon-name {
53
+ font-size: 12px;
54
+ color: #666;
55
+ margin-top: 6px;
56
+ word-break: break-all;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ <h1>Pré-visualização: {{fontName}}</h1>
62
+ <input
63
+ type="text"
64
+ id="search"
65
+ placeholder="Buscar ícone..."
66
+ onkeyup="filterIcons()"
67
+ />
68
+ <div class="grid" id="icons">
69
+ {{#each glyphs}}
70
+ <div class="icon-box" data-name="{{@root.cssClass}}{{name}}">
71
+ <i class="icon {{@root.cssClass}}{{name}}"></i>
72
+ <div class="icon-name">{{@root.cssClass}}{{name}}</div>
73
+ </div>
74
+ {{/each}}
75
+ </div>
76
+ <script>
77
+ function filterIcons() { const term =
78
+ document.getElementById('search').value.toLowerCase();
79
+ document.querySelectorAll('.icon-box').forEach(box => { box.style.display
80
+ = box.dataset.name.toLowerCase().includes(term) ? '' : 'none'; }); }
81
+ </script>
82
+ </body>
83
+ </html>
@@ -0,0 +1,30 @@
1
+ {{!--
2
+ Customize template to generate icon font CSS (Handlebars)
3
+ -------------------------------------------
4
+ {{fontName}} → Font name (ex: studio-icons)
5
+ {{fontPath}} → Relative path to font files (ex: ../fonts/)
6
+ {{cssClass}} → Prefix used for classes (ex: icon)
7
+ {{../cssClass}} → Goes up one level in context (inside each)
8
+ {{name}} → Name of the SVG file
9
+ {{unicodeHex}} → Automatically generated Unicode code (\e900, \e901, etc.)
10
+ --}}
11
+
12
+ {{> base-icons}}
13
+
14
+ [class^="{{cssClass}}"], [class*=" {{cssClass}}"] {
15
+ font-family: '{{fontName}}' !important;
16
+ speak: none;
17
+ font-style: normal;
18
+ font-weight: normal;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ line-height: 1;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ }
25
+
26
+ {{#each glyphs}}
27
+ .{{../cssClass}}{{name}}::before {
28
+ content: "\\{{unicodeHex}}";
29
+ }
30
+ {{/each}}
@@ -0,0 +1,31 @@
1
+ {{!--
2
+ Customize template to generate icon font CSS (Handlebars)
3
+ -------------------------------------------
4
+ {{fontName}} → Font name (ex: studio-icons)
5
+ {{fontPath}} → Relative path to font files (ex: ../fonts/)
6
+ {{cssClass}} → Prefix used for classes (ex: icon)
7
+ {{../cssClass}} → Goes up one level in context (inside each)
8
+ {{name}} → Name of the SVG file
9
+ {{unicodeHex}} → Automatically generated Unicode code (\e900, \e901, etc.)
10
+ --}}
11
+
12
+ {{> base-icons}}
13
+
14
+ %{{cssClass}} {
15
+ font-family: '{{fontName}}' !important;
16
+ speak: none;
17
+ font-style: normal;
18
+ font-weight: normal;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ line-height: 1;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale;
24
+ }
25
+
26
+ {{#each glyphs}}
27
+ .{{../cssClass}}{{name}}::before {
28
+ @extend %{{../cssClass}};
29
+ content: "\\{{unicodeHex}}";
30
+ }
31
+ {{/each}}