wmt-polyicon 0.1.5 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.0] — 2026-04-08
6
+
7
+ ### Added
8
+ - **`fontName` config option** — controls font-family name and output filenames (default: `"fontello"`). Generate drop-in replacements for existing Fontello fonts
9
+ - **`hashOutput` config option** — appends a content hash to the output folder for cache busting (default: `true`). Same icons = same hash; change an SVG = new hash. Old hashed folders are auto-deleted on regenerate
10
+ - **Commented `.polyiconrc`** — `polyicon init` now creates a config file with inline comments explaining every field
11
+ - **Improved CLI output** — `polyicon init` shows next steps; `polyicon generate` shows full output paths and usage example
12
+
13
+ ### Changed
14
+ - Default output paths changed from `./src/assets/` to `./public/assets/` (more common in modern web projects)
15
+ - Default `fontName` changed from `"polyicon"` to `"fontello"` — output now matches traditional Fontello structure
16
+ - Default `outputStyles` changed from `polyicon.css` to `fontello.css`
17
+ - Preview file renamed from `{fontName}-preview.html` to `demo.html`
18
+ - Config parser now supports `//` comments in `.polyiconrc`
19
+
20
+ ## [0.1.6] — 2026-04-02
21
+
22
+ ### Fixed
23
+ - Configurable `classPrefix` — the `.polyiconrc` `classPrefix` field now correctly propagates to CSS selectors, HTML preview, and config.json output
24
+
5
25
  ## [0.1.5] — 2026-04-02
6
26
 
7
27
  ### Fixed
package/README.md CHANGED
@@ -1,121 +1,327 @@
1
- # Polyicon
1
+ # wmt-polyicon
2
2
 
3
- SVG to icon font generator with HTML preview Fontello-style output for React and web projects.
3
+ SVG to icon font generator drop in SVGs, get a production-ready Fontello-style icon font with one command.
4
4
 
5
- Converts a folder of SVGs into `woff2`, `woff`, `ttf`, `eot`, `svg` font files plus a CSS file, a TypeScript/JS types file, and an interactive HTML preview page.
5
+ Converts a folder of SVGs into `woff2`, `woff`, `ttf`, `eot`, `svg` font files plus CSS, JS constants, and an interactive preview page.
6
+
7
+ ---
6
8
 
7
9
  ## Install
8
10
 
9
11
  ```bash
10
- # project dev dependency
11
- npm i -D polyicon
12
+ # global (recommended)
13
+ npm i -g wmt-polyicon
12
14
 
13
- # or global
14
- npm i -g polyicon
15
+ # or project dev dependency
16
+ npm i -D wmt-polyicon
15
17
 
16
18
  # or one-off
17
- npx polyicon init
19
+ npx wmt-polyicon init
18
20
  ```
19
21
 
20
22
  ## Quick Start
21
23
 
22
24
  ```bash
23
- polyicon init # creates .polyiconrc with defaults
24
- polyicon generate # builds fonts, CSS, types, and preview
25
+ polyicon init # creates .polyiconrc with defaults
26
+ # edit .polyiconrc set "svg" to your SVG folder
27
+ polyicon generate # builds fonts, CSS, types, and preview
25
28
  ```
26
29
 
27
- Place your SVGs in the folder specified by `svg` in `.polyiconrc`, then run `generate`. Output folders are created automatically.
30
+ That's it 3 steps:
31
+
32
+ 1. `polyicon init`
33
+ 2. Change `"svg"` path in `.polyiconrc`
34
+ 3. `polyicon generate`
35
+
36
+ ---
28
37
 
29
38
  ## Config (`.polyiconrc`)
30
39
 
31
- ```json
40
+ Running `polyicon init` creates a fully commented config file:
41
+
42
+ ```jsonc
32
43
  {
33
- "svg": "./src/assets/svg",
34
- "outputFonts": "./src/assets/fontello/font",
35
- "outputStyles": "./src/assets/fontello/css/polyicon.css",
36
- "outputTypes": "./src/types/IconTypes.js",
37
- "polyiconConfig": "./src/assets/fontello/config.json",
44
+ // Path to your SVG icons folder (change this to your SVG folder)
45
+ "svg": "./public/assets/svg",
46
+
47
+ // Output paths
48
+ "outputFonts": "./public/assets/fontello/font",
49
+ "outputStyles": "./public/assets/fontello/css/fontello.css",
50
+ "outputTypes": "./public/assets/fontello/IconTypes.js",
51
+ "polyiconConfig": "./public/assets/fontello/config.json",
52
+
53
+ // Font URL prefix used inside the generated CSS (relative to CSS file)
38
54
  "importFontsPath": "../font/",
39
- "formats": ["eot", "svg", "ttf", "woff", "woff2"]
55
+ // Font file formats to generate
56
+ "formats": ["eot", "svg", "ttf", "woff", "woff2"],
57
+ // Font family name used in CSS and font filenames
58
+ "fontName": "fontello",
59
+ // CSS class prefix (e.g. "icon" → .icon-home, "pi" → .pi-home)
60
+ "classPrefix": "icon",
61
+ // Append content hash to output folder for cache busting (e.g. fontello-a3f7b2c1/)
62
+ "hashOutput": true
40
63
  }
41
64
  ```
42
65
 
43
- | Field | Required | Description |
66
+ ### Config Reference
67
+
68
+ | Field | Default | Description |
44
69
  |---|---|---|
45
- | `svg` | | Folder containing your `.svg` files |
46
- | `outputFonts` | | Where font files are written |
47
- | `outputStyles` | | CSS file output path |
48
- | `outputTypes` | | JS or TS types file output path |
49
- | `polyiconConfig` | | Fontello-compatible `config.json` output path |
50
- | `importFontsPath` | | Font URL prefix in the generated CSS (e.g. `../font/`) |
51
- | `formats` | | Font formats to generate (default: all five) |
70
+ | `svg` | `./public/assets/svg` | **Required.** Folder containing your `.svg` icon files |
71
+ | `outputFonts` | `./public/assets/fontello/font` | Where font files (`fontello.woff2`, etc.) are written |
72
+ | `outputStyles` | `./public/assets/fontello/css/fontello.css` | CSS file output path |
73
+ | `outputTypes` | `./public/assets/fontello/IconTypes.js` | JS constants file output path |
74
+ | `polyiconConfig` | `./public/assets/fontello/config.json` | Fontello-compatible config output path |
75
+ | `importFontsPath` | `../font/` | Font URL prefix in generated CSS use relative path or CDN URL |
76
+ | `formats` | `["eot","svg","ttf","woff","woff2"]` | Font formats to generate |
77
+ | `fontName` | `fontello` | Font family name in CSS and font filenames |
78
+ | `classPrefix` | `icon` | CSS class prefix for icons |
79
+ | `hashOutput` | `true` | Append content hash to output folder for cache busting |
80
+
81
+ ---
82
+
83
+ ## Features
84
+
85
+ ### Font Name (`fontName`)
86
+
87
+ Controls the font-family name in CSS and the output filenames.
88
+
89
+ ```jsonc
90
+ "fontName": "fontello" // → fontello.woff2, font-family: "fontello"
91
+ "fontName": "myicons" // → myicons.woff2, font-family: "myicons"
92
+ ```
93
+
94
+ This means you can generate a **drop-in replacement** for an existing Fontello font — same filenames, same `font-family`, just upload to CDN.
95
+
96
+ ### Class Prefix (`classPrefix`)
97
+
98
+ Controls the CSS class prefix for all icons. SVG filename becomes the icon name.
99
+
100
+ ```jsonc
101
+ "classPrefix": "icon" // → .icon-home, .icon-settings, .icon-user
102
+ "classPrefix": "pi" // → .pi-home, .pi-settings, .pi-user
103
+ ```
104
+
105
+ ### Cache Busting (`hashOutput`)
52
106
 
53
- SVG filenames become CSS class names. Spaces and special characters are auto-sanitised to hyphens. Use clean, single-colour SVGs — icon fonts are monochrome.
107
+ When `true`, the output folder gets a content hash suffix:
108
+
109
+ ```
110
+ fontello-5f3f3c64/
111
+ ├── css/fontello.css
112
+ ├── font/fontello.woff2 ...
113
+ ├── IconTypes.js
114
+ ├── config.json
115
+ └── demo.html
116
+ ```
117
+
118
+ - **Same icons** → same hash → CDN cache works
119
+ - **Add/remove/change an SVG** → new hash → CDN cache is busted automatically
120
+ - Old hashed folders are auto-deleted on regenerate
121
+
122
+ Set `"hashOutput": false` for a plain `fontello/` folder without hash.
123
+
124
+ ### CDN Support (`importFontsPath`)
125
+
126
+ Controls the font URL prefix inside the generated CSS.
127
+
128
+ ```jsonc
129
+ // Relative (default) — for local hosting
130
+ "importFontsPath": "../font/"
131
+ // → url('../font/fontello.woff2')
132
+
133
+ // CDN — for remote hosting
134
+ "importFontsPath": "https://cdn.example.com/font/"
135
+ // → url('https://cdn.example.com/font/fontello.woff2')
136
+ ```
137
+
138
+ ### Stroke-to-Fill Conversion
139
+
140
+ Line/stroke icons (e.g. from Figma, Heroicons) are automatically converted to filled outlines. Icon fonts only support fills — this conversion happens transparently so your stroke-based SVGs work out of the box.
141
+
142
+ ### SVG Sanitisation
143
+
144
+ - Spaces and special characters in filenames are auto-sanitised to hyphens
145
+ - `<defs>`, `clip-path`, `filter`, `mask` elements are stripped
146
+ - White background `<rect>` elements are removed
147
+ - Non-path elements (`<circle>`, `<ellipse>`, `<rect>`, `<polygon>`, `<polyline>`, `<line>`) are fully supported
148
+
149
+ ---
54
150
 
55
151
  ## Output
56
152
 
57
153
  ```
58
- src/assets/fontello/
59
- polyicon-preview.html ← interactive icon browser
60
- css/
61
- polyicon.css
62
- font/
63
- polyicon.eot
64
- polyicon.svg
65
- polyicon.ttf
66
- polyicon.woff
67
- polyicon.woff2
68
- config.json
69
- src/types/
70
- IconTypes.js
154
+ fontello-5f3f3c64/
155
+ ├── css/
156
+ │ └── fontello.css ← @font-face + all .icon-* classes
157
+ ├── font/
158
+ │ ├── fontello.woff2 ← primary web font
159
+ │ ├── fontello.woff
160
+ │ ├── fontello.ttf
161
+ │ ├── fontello.eot
162
+ │ └── fontello.svg
163
+ ├── IconTypes.js ← JS constants for icon names
164
+ ├── config.json ← Fontello-compatible glyph metadata
165
+ └── demo.html ← interactive icon browser (search, click to copy)
71
166
  ```
72
167
 
73
- Open `polyicon-preview.html` in a browser to browse all generated icons, search by name, and click any card to copy its class name to the clipboard.
168
+ Open `demo.html` in a browser to browse all icons, search by name, and click any card to copy its class name.
74
169
 
75
- ## Usage in React
170
+ ---
76
171
 
77
- Import the generated CSS once (e.g. in your root component or entry file):
172
+ ## Usage
173
+
174
+ ### HTML
175
+
176
+ ```html
177
+ <!-- Load CSS once -->
178
+ <link rel="stylesheet" href="fontello/css/fontello.css" />
179
+
180
+ <!-- Use icons -->
181
+ <i class="icon-home"></i>
182
+ <i class="icon-settings"></i>
183
+ <i class="icon-arrow-right"></i>
184
+ ```
185
+
186
+ ### React / Next.js
187
+
188
+ Import the CSS once in your root layout or entry file:
78
189
 
79
190
  ```jsx
80
- import "./assets/fontello/css/polyicon.css";
191
+ // Option 1: <link> tag (for public/ assets or CDN)
192
+ <link rel="stylesheet" href="/assets/fontello/css/fontello.css" />
193
+
194
+ // Option 2: CSS import (for src/ assets)
195
+ import "./assets/fontello/css/fontello.css";
81
196
  ```
82
197
 
83
- Use icons by class name:
198
+ Use icons anywhere:
84
199
 
85
200
  ```jsx
86
- <i className="polyicon polyicon-home" />
201
+ // Direct class name
202
+ <i className="icon-home" />
203
+
204
+ // With Tailwind — control size and color
205
+ <i className="icon-home text-4xl text-blue-500" />
206
+
207
+ // Dynamic from variable or CMS
208
+ <i className={`icon-${iconName} text-2xl`} />
209
+ ```
210
+
211
+ ### Using IconTypes.js
212
+
213
+ The generated `IconTypes.js` provides constants for every icon name:
214
+
215
+ ```js
216
+ const IconTypes = {
217
+ HOME: 'home',
218
+ SETTINGS: 'settings',
219
+ ARROW_RIGHT: 'arrow-right',
220
+ // ...
221
+ };
222
+ export default IconTypes;
87
223
  ```
88
224
 
89
- Or with the generated types for autocomplete:
225
+ Use for consistent, typo-free icon references:
90
226
 
91
227
  ```jsx
92
- import IconTypes from "./types/IconTypes";
228
+ import IconTypes from "./fontello/IconTypes";
93
229
 
94
- <i className={`polyicon polyicon-${IconTypes.HOME}`} />
230
+ <i className={`icon-${IconTypes.HOME}`} />
231
+ <i className={`icon-${IconTypes.SETTINGS}`} />
95
232
  ```
96
233
 
234
+ ### Sizing and Coloring
235
+
236
+ Icons are a **font**, so standard text CSS properties control their appearance:
237
+
238
+ ```css
239
+ /* Size — use font-size */
240
+ .icon-home { font-size: 24px; }
241
+ .icon-home { font-size: 2rem; }
242
+
243
+ /* Color — use color */
244
+ .icon-home { color: #335fff; }
245
+
246
+ /* Tailwind shorthand */
247
+ <i class="icon-home text-4xl text-blue-500" />
248
+ <i class="icon-home text-6xl text-red-600 hover:text-red-800" />
249
+ ```
250
+
251
+ ---
252
+
97
253
  ## Programmatic API
98
254
 
99
255
  ```js
100
- const { buildIcons } = require("polyicon");
101
-
102
- await buildIcons({
103
- svg: "./src/assets/svg",
104
- outputFonts: "./dist/fonts",
105
- outputStyles: "./dist/polyicon.css",
106
- outputTypes: "./src/types/IconTypes.js",
107
- importFontsPath: "./fonts/",
256
+ const { buildIcons } = require("wmt-polyicon");
257
+
258
+ const result = await buildIcons({
259
+ svg: "./icons",
260
+ outputFonts: "./dist/fontello/font",
261
+ outputStyles: "./dist/fontello/css/fontello.css",
262
+ outputTypes: "./dist/fontello/IconTypes.js",
263
+ importFontsPath: "../font/",
264
+ fontName: "fontello",
265
+ classPrefix: "icon",
266
+ hashOutput: true,
108
267
  formats: ["woff2", "woff", "ttf"]
109
268
  });
269
+
270
+ console.log(result.outputDir);
271
+ // → ./dist/fontello-a3f7b2c1
110
272
  ```
111
273
 
112
- ## CLI
274
+ ---
275
+
276
+ ## CLI Reference
113
277
 
114
278
  ```
115
- polyicon init Create .polyiconrc
116
- polyicon generate Generate fonts, CSS, types, and preview
279
+ polyicon <command> [options]
280
+
281
+ Commands:
282
+ init Create .polyiconrc with defaults and comments
283
+ generate Generate fonts, CSS, types, and preview
117
284
 
118
285
  Options:
119
- -c, --config Path to config (default: .polyiconrc)
120
- -f, --force Overwrite config on init
286
+ -c, --config Path to config file (default: .polyiconrc)
287
+ -f, --force Overwrite existing config on init
288
+ ```
289
+
290
+ ### Examples
291
+
292
+ ```bash
293
+ # Standard workflow
294
+ polyicon init
295
+ polyicon generate
296
+
297
+ # Custom config path
298
+ polyicon init --config icons.json
299
+ polyicon generate --config icons.json
300
+
301
+ # Overwrite existing config
302
+ polyicon init --force
303
+ ```
304
+
305
+ ---
306
+
307
+ ## SVG Guidelines
308
+
309
+ For best results, your SVG icons should be:
310
+
311
+ - **Single colour** — icon fonts are monochrome (colour is applied via CSS)
312
+ - **Square artboard** — e.g. 24×24, 32×32, or any consistent size
313
+ - **Clean paths** — remove masks, filters, clip-paths (auto-stripped but cleaner input = better output)
314
+ - **Filled or stroked** — both work (strokes are auto-converted to fills)
315
+
316
+ SVG filenames become CSS class names:
121
317
  ```
318
+ mobile-app.svg → .icon-mobile-app
319
+ Web-Development.svg → .icon-Web-Development
320
+ AI ML Model.svg → .icon-AI-ML-Model (spaces → hyphens)
321
+ ```
322
+
323
+ ---
324
+
325
+ ## License
326
+
327
+ MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wmt-polyicon",
3
- "version": "0.1.5",
4
- "description": "SVG to icon font generator with HTML preview Fontello-style output for React and web projects.",
3
+ "version": "0.2.0",
4
+ "description": "SVG to icon font generator drop in SVGs, get a production-ready Fontello-style icon font with one command.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
7
7
  "main": "src/index.js",
@@ -13,11 +13,16 @@
13
13
  "font",
14
14
  "iconfont",
15
15
  "fontello",
16
+ "svg-to-font",
17
+ "icon-font",
18
+ "webfont",
16
19
  "react",
20
+ "nextjs",
17
21
  "cli",
18
22
  "woff",
19
23
  "woff2",
20
- "ttf"
24
+ "ttf",
25
+ "cache-busting"
21
26
  ],
22
27
  "files": [
23
28
  "bin",
@@ -18,8 +18,17 @@ async function generateCommand({ configPath }) {
18
18
  `SVG path not found: ${conf.svg}. Update \`svg\` in ${configPath}.`
19
19
  );
20
20
  }
21
- await buildIcons(conf);
22
- console.log(`Done. Generated ${conf.outputFonts} and preview HTML.`);
21
+ const result = await buildIcons(conf);
22
+ const fontName = conf.fontName || "fontello";
23
+ const outDir = result.outputDir;
24
+ console.log(`\n✔ Generated successfully!\n`);
25
+ console.log(` Output: ${outDir}`);
26
+ console.log(` Fonts: ${outDir}/font/${fontName}.woff2 (+woff, ttf, eot, svg)`);
27
+ console.log(` CSS: ${outDir}/css/${fontName}.css`);
28
+ console.log(` Types: ${outDir}/IconTypes.js`);
29
+ console.log(` Preview: ${outDir}/demo.html\n`);
30
+ console.log(`Usage in HTML/JSX:`);
31
+ console.log(` <i class="icon-YourIconName"></i>\n`);
23
32
  }
24
33
 
25
34
  module.exports = {
@@ -13,7 +13,33 @@ async function initCommand({ configPath, force }) {
13
13
  }
14
14
 
15
15
  await ensureDir(path.dirname(targetPath));
16
- fs.writeFileSync(targetPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
16
+
17
+ const configWithComments = `{
18
+ // ── REQUIRED ──────────────────────────────────────────────────
19
+ // Path to your SVG icons folder (change this to your SVG folder)
20
+ "svg": "${DEFAULT_CONFIG.svg}",
21
+
22
+ // ── OUTPUT PATHS ──────────────────────────────────────────────
23
+ "outputFonts": "${DEFAULT_CONFIG.outputFonts}",
24
+ "outputStyles": "${DEFAULT_CONFIG.outputStyles}",
25
+ "outputTypes": "${DEFAULT_CONFIG.outputTypes}",
26
+ "polyiconConfig": "${DEFAULT_CONFIG.polyiconConfig}",
27
+
28
+ // ── FONT SETTINGS ────────────────────────────────────────────
29
+ // Font URL prefix used inside the generated CSS (relative to CSS file)
30
+ "importFontsPath": "${DEFAULT_CONFIG.importFontsPath}",
31
+ // Font file formats to generate
32
+ "formats": ${JSON.stringify(DEFAULT_CONFIG.formats)},
33
+ // Font family name used in CSS and font filenames
34
+ "fontName": "${DEFAULT_CONFIG.fontName}",
35
+ // CSS class prefix (e.g. "icon" → .icon-home, "pi" → .pi-home)
36
+ "classPrefix": "${DEFAULT_CONFIG.classPrefix}",
37
+ // Append content hash to output folder for cache busting (e.g. fontello-a3f7b2c1/)
38
+ "hashOutput": ${DEFAULT_CONFIG.hashOutput}
39
+ }
40
+ `;
41
+
42
+ fs.writeFileSync(targetPath, configWithComments);
17
43
 
18
44
  }
19
45
 
package/src/cli/index.js CHANGED
@@ -34,10 +34,10 @@ async function run(argv) {
34
34
  try {
35
35
  if (command === "init") {
36
36
  await initCommand({ configPath, force });
37
- console.log(`Created ${configPath}`);
38
- console.log(
39
- "Place SVGs in ./src/assets/svg and run `polyicon generate`."
40
- );
37
+ console.log(`\n✔ Created ${configPath}\n`);
38
+ console.log(`Next steps:`);
39
+ console.log(` 1. Update "svg" path in ${configPath} to your SVG folder`);
40
+ console.log(` 2. Run: polyicon generate\n`);
41
41
  return;
42
42
  }
43
43
 
@@ -1,12 +1,14 @@
1
1
  const DEFAULT_CONFIG = {
2
- svg: "./src/assets/svg",
3
- outputFonts: "./src/assets/fontello/font",
2
+ svg: "./public/assets/svg",
3
+ outputFonts: "./public/assets/fontello/font",
4
4
  importFontsPath: "../font/",
5
- outputTypes: "./src/types/IconTypes.js",
6
- outputStyles: "./src/assets/fontello/css/polyicon.css",
5
+ outputTypes: "./public/assets/fontello/IconTypes.js",
6
+ outputStyles: "./public/assets/fontello/css/fontello.css",
7
7
  formats: ["eot", "svg", "ttf", "woff", "woff2"],
8
- polyiconConfig: "./src/assets/fontello/config.json",
9
- classPrefix: "icon"
8
+ polyiconConfig: "./public/assets/fontello/config.json",
9
+ classPrefix: "icon",
10
+ fontName: "fontello",
11
+ hashOutput: true
10
12
  };
11
13
 
12
14
  module.exports = {
@@ -2,8 +2,10 @@ const fs = require("fs");
2
2
 
3
3
  function loadConfig(filePath) {
4
4
  const raw = fs.readFileSync(filePath, "utf8");
5
+ // Strip single-line comments (// ...) so users can annotate .polyiconrc
6
+ const stripped = raw.replace(/^\s*\/\/.*$/gm, "").replace(/,\s*([}\]])/g, "$1");
5
7
  try {
6
- return JSON.parse(raw);
8
+ return JSON.parse(stripped);
7
9
  } catch (err) {
8
10
  const message = err && err.message ? err.message : "Invalid JSON";
9
11
  throw new Error(`Invalid JSON in ${filePath}: ${message}`);
package/src/constants.js CHANGED
@@ -9,7 +9,7 @@ const WEBSITE = {
9
9
  links: []
10
10
  };
11
11
 
12
- const FONT_NAME = "polyicon";
12
+ const FONT_NAME = "fontello";
13
13
  const CLASS_PREFIX = "icon";
14
14
  const TMP_DIR = ".polyicon_tmp";
15
15
 
@@ -1,6 +1,7 @@
1
1
  const path = require("path");
2
+ const crypto = require("crypto");
2
3
  const fse = require("fs-extra");
3
- const { TMP_DIR, CLASS_PREFIX } = require("../constants");
4
+ const { TMP_DIR, CLASS_PREFIX, FONT_NAME } = require("../constants");
4
5
  const { resolveFromCwd } = require("../utils/paths");
5
6
  const { runSvgToFont } = require("./run_svgtofont");
6
7
  const { parseGlyphs } = require("./parse_svg");
@@ -24,6 +25,7 @@ async function buildIcons(conf) {
24
25
  const importFontsPath = conf.importFontsPath || "";
25
26
  const formats = conf.formats || ["eot", "svg", "ttf", "woff", "woff2"];
26
27
  const classPrefix = conf.classPrefix || CLASS_PREFIX;
28
+ const fontName = conf.fontName || FONT_NAME;
27
29
  const tmpPath = path.resolve(process.cwd(), TMP_DIR);
28
30
  const tmpSvgPath = path.resolve(process.cwd(), `${TMP_DIR}_svgs`);
29
31
 
@@ -94,21 +96,59 @@ async function buildIcons(conf) {
94
96
  await fse.writeFile(outputPath, outputSvg, "utf8");
95
97
  }
96
98
 
97
- await runSvgToFont({ svgPath: tmpSvgPath, tmpPath, classPrefix });
98
- const glyphs = await parseGlyphs(tmpPath);
99
+ await runSvgToFont({ svgPath: tmpSvgPath, tmpPath, classPrefix, fontName });
100
+ const glyphs = await parseGlyphs(tmpPath, fontName);
99
101
 
100
102
  await writeTypes({ outputTypesPath, glyphs });
101
- await writePolyiconConfig({ outputPath: polyiconConfigPath, glyphs, classPrefix });
103
+ await writePolyiconConfig({ outputPath: polyiconConfigPath, glyphs, classPrefix, fontName });
102
104
  await writeCss({
103
105
  tmpPath,
104
106
  outputStylesPath,
105
- importFontsPath
107
+ importFontsPath,
108
+ fontName
106
109
  });
107
- await writeFonts({ tmpPath, outputFontsPath, formats });
108
- await writeHtml({ tmpPath, outputFontsPath, glyphs, classPrefix });
110
+ await writeFonts({ tmpPath, outputFontsPath, formats, fontName });
111
+ await writeHtml({ tmpPath, outputFontsPath, glyphs, classPrefix, fontName });
112
+
113
+ // Compute content hash BEFORE cleaning up tmp files
114
+ let contentHash = null;
115
+ if (conf.hashOutput !== false) {
116
+ const hashInput = crypto.createHash("md5");
117
+ const sortedFiles = [...seen.keys()].sort();
118
+ for (const name of sortedFiles) {
119
+ hashInput.update(name);
120
+ const svgContent = await fse.readFile(path.resolve(tmpSvgPath, `${name}.svg`), "utf8");
121
+ hashInput.update(svgContent);
122
+ }
123
+ contentHash = hashInput.digest("hex").slice(0, 8);
124
+ }
109
125
 
110
126
  await fse.remove(tmpPath);
111
127
  await fse.remove(tmpSvgPath);
128
+
129
+ // Rename output folder with hash suffix for cache busting
130
+ let finalOutputDir = path.dirname(outputFontsPath);
131
+ if (contentHash) {
132
+ const baseDir = path.dirname(outputFontsPath);
133
+ const baseName = path.basename(baseDir);
134
+ const parentDir = path.dirname(baseDir);
135
+ const hashedDir = path.resolve(parentDir, `${baseName}-${contentHash}`);
136
+
137
+ // Remove previous hashed folders
138
+ const siblings = await fse.readdir(parentDir);
139
+ for (const sibling of siblings) {
140
+ if (sibling.startsWith(`${baseName}-`) && sibling !== path.basename(hashedDir)) {
141
+ await fse.remove(path.resolve(parentDir, sibling));
142
+ }
143
+ }
144
+
145
+ // Rename fontello/ → fontello-<hash>/
146
+ if (await fse.pathExists(hashedDir)) await fse.remove(hashedDir);
147
+ await fse.move(baseDir, hashedDir);
148
+ finalOutputDir = hashedDir;
149
+ }
150
+
151
+ return { outputDir: finalOutputDir };
112
152
  }
113
153
 
114
154
  module.exports = {
@@ -1,10 +1,8 @@
1
1
  const fse = require("fs-extra");
2
2
  const { XMLParser } = require("fast-xml-parser");
3
3
  const path = require("path");
4
- const { FONT_NAME } = require("../constants");
5
-
6
- async function parseGlyphs(tmpPath) {
7
- const svgPath = path.resolve(tmpPath, `${FONT_NAME}.svg`);
4
+ async function parseGlyphs(tmpPath, fontName) {
5
+ const svgPath = path.resolve(tmpPath, `${fontName}.svg`);
8
6
  const svg = await fse.readFile(svgPath, "utf8");
9
7
  const parser = new XMLParser({
10
8
  ignoreAttributes: false,
@@ -1,12 +1,12 @@
1
- const { WEBSITE, FONT_NAME } = require("../constants");
1
+ const { WEBSITE } = require("../constants");
2
2
 
3
- async function runSvgToFont({ svgPath, tmpPath, classPrefix }) {
3
+ async function runSvgToFont({ svgPath, tmpPath, classPrefix, fontName }) {
4
4
  const mod = await import("svgtofont");
5
5
  const svgtofont = mod.default;
6
6
  await svgtofont({
7
7
  src: svgPath,
8
8
  dist: tmpPath,
9
- fontName: FONT_NAME,
9
+ fontName,
10
10
  css: true,
11
11
  classNamePrefix: classPrefix,
12
12
  website: WEBSITE,
@@ -1,28 +1,26 @@
1
1
  const fse = require("fs-extra");
2
2
  const path = require("path");
3
3
  const { ensureDir } = require("../utils/fs");
4
- const { FONT_NAME } = require("../constants");
5
-
6
- async function writeCss({ tmpPath, outputStylesPath, importFontsPath }) {
4
+ async function writeCss({ tmpPath, outputStylesPath, importFontsPath, fontName }) {
7
5
  if (!outputStylesPath) return;
8
6
 
9
7
  await ensureDir(path.dirname(outputStylesPath));
10
8
 
11
9
  let css = await fse.readFile(
12
- path.resolve(tmpPath, `${FONT_NAME}.css`),
10
+ path.resolve(tmpPath, `${fontName}.css`),
13
11
  "utf8"
14
12
  );
15
13
 
16
- while (css.indexOf(`url('${FONT_NAME}`) > -1) {
14
+ while (css.indexOf(`url('${fontName}`) > -1) {
17
15
  css = css.replace(
18
- `url('${FONT_NAME}`,
19
- `url('${importFontsPath}${FONT_NAME}`
16
+ `url('${fontName}`,
17
+ `url('${importFontsPath}${fontName}`
20
18
  );
21
19
  }
22
- while (css.indexOf(`url("${FONT_NAME}`) > -1) {
20
+ while (css.indexOf(`url("${fontName}`) > -1) {
23
21
  css = css.replace(
24
- `url("${FONT_NAME}`,
25
- `url("${importFontsPath}${FONT_NAME}`
22
+ `url("${fontName}`,
23
+ `url("${importFontsPath}${fontName}`
26
24
  );
27
25
  }
28
26
 
@@ -1,15 +1,13 @@
1
1
  const fse = require("fs-extra");
2
2
  const path = require("path");
3
3
  const { ensureDir } = require("../utils/fs");
4
- const { FONT_NAME } = require("../constants");
5
-
6
- async function writeFonts({ tmpPath, outputFontsPath, formats }) {
4
+ async function writeFonts({ tmpPath, outputFontsPath, formats, fontName }) {
7
5
  await ensureDir(outputFontsPath);
8
6
 
9
7
  const copyIf = async (ext) => {
10
8
  await fse.copy(
11
- path.resolve(tmpPath, `${FONT_NAME}.${ext}`),
12
- path.resolve(outputFontsPath, `${FONT_NAME}.${ext}`)
9
+ path.resolve(tmpPath, `${fontName}.${ext}`),
10
+ path.resolve(outputFontsPath, `${fontName}.${ext}`)
13
11
  );
14
12
  };
15
13
 
@@ -1,14 +1,12 @@
1
1
  const fse = require("fs-extra");
2
2
  const path = require("path");
3
3
  const { ensureDir } = require("../utils/fs");
4
- const { FONT_NAME } = require("../constants");
5
-
6
- async function writeHtml({ tmpPath, outputFontsPath, glyphs, classPrefix }) {
4
+ async function writeHtml({ tmpPath, outputFontsPath, glyphs, classPrefix, fontName }) {
7
5
  const outputDir = path.dirname(outputFontsPath);
8
6
  const fontsSubDir = path.basename(outputFontsPath);
9
7
 
10
8
  let css = await fse.readFile(
11
- path.resolve(tmpPath, `${FONT_NAME}.css`),
9
+ path.resolve(tmpPath, `${fontName}.css`),
12
10
  "utf8"
13
11
  );
14
12
 
@@ -50,7 +48,7 @@ async function writeHtml({ tmpPath, outputFontsPath, glyphs, classPrefix }) {
50
48
  <head>
51
49
  <meta charset="UTF-8" />
52
50
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
53
- <title>${FONT_NAME} — Icon Preview</title>
51
+ <title>${fontName} — Icon Preview</title>
54
52
  <style>
55
53
  ${css}
56
54
 
@@ -230,7 +228,7 @@ ${css}
230
228
 
231
229
  <header class="header">
232
230
  <div class="header-left">
233
- <h1>${FONT_NAME}</h1>
231
+ <h1>${fontName}</h1>
234
232
  <p id="count">${glyphs.length} icon${glyphs.length !== 1 ? "s" : ""}</p>
235
233
  </div>
236
234
  <div class="search-wrap">
@@ -293,7 +291,7 @@ ${items}
293
291
 
294
292
  await ensureDir(outputDir);
295
293
  await fse.writeFile(
296
- path.resolve(outputDir, `${FONT_NAME}-preview.html`),
294
+ path.resolve(outputDir, "demo.html"),
297
295
  html,
298
296
  "utf8"
299
297
  );
@@ -1,11 +1,9 @@
1
1
  const { ensureFile, writeJson } = require("../utils/fs");
2
- const { FONT_NAME } = require("../constants");
3
-
4
- async function writePolyiconConfig({ outputPath, glyphs, classPrefix }) {
2
+ async function writePolyiconConfig({ outputPath, glyphs, classPrefix, fontName }) {
5
3
  if (!outputPath) return;
6
4
 
7
5
  const config = {
8
- name: FONT_NAME,
6
+ name: fontName,
9
7
  css_prefix_text: `${classPrefix}-`,
10
8
  css_use_suffix: false,
11
9
  hinting: true,