mimeforge 0.1.1 → 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
@@ -3,6 +3,20 @@
3
3
  All notable changes are listed here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
4
4
  project uses [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.2.0] - 2026-10-08
7
+
8
+ - Fallback icon: `mimeforge default` and `mimeforge --all` write `default.svg` (generic body without a label), and
9
+ `--png` adds `<size>/default.png`; library: `renderDefaultIcon()`. For apps that need a file to point at when an
10
+ extension has no icon.
11
+ - PNG export no longer loads system fonts: `--all --png 16` takes about half a second instead of minutes.
12
+
13
+ ## [0.1.2] - 2026-10-08
14
+
15
+ Documentation only.
16
+
17
+ - README: separate install paths (npx, global, project dependency, from source), a "use it in a project" example,
18
+ library install and ESM notes
19
+
6
20
  ## [0.1.1] - 2026-10-08
7
21
 
8
22
  Maintenance release, the first one published through GitHub Actions (npm Trusted Publishing). No code changes.
package/README.md CHANGED
@@ -29,40 +29,95 @@ And, frankly, why not? :)
29
29
 
30
30
  ## Contents
31
31
 
32
- [Quick start](#quick-start) · [Everyday use](#everyday-use) · [Customize](#customize) ([colors](#change-colors) · [label](#change-the-label) · [font](#change-the-font) · [bodies](#change-the-bodies) · [config file](#use-a-config-file) · [PNG](#export-png)) · [Edge cases](#edge-cases) · [Library API](#library-api) · [CLI reference](#cli-reference) · [Categories](#categories) · [Development](#development)
32
+ [Quick start](#quick-start) · [Static hosts](#use-it-on-a-static-host-with-a-fallback-icon) · [Everyday use](#everyday-use) · [Customize](#customize) ([colors](#change-colors) · [label](#change-the-label) · [font](#change-the-font) · [bodies](#change-the-bodies) · [config file](#use-a-config-file) · [PNG](#export-png)) · [Edge cases](#edge-cases) · [Library API](#library-api) · [CLI reference](#cli-reference) · [Categories](#categories) · [Development](#development)
33
33
 
34
34
  ## Quick start
35
35
 
36
36
  Requires Node.js 20 or newer.
37
37
 
38
+ **Try it, nothing to install:**
39
+
38
40
  ```bash
39
- npx mimeforge pdf docx webm -o icons # no install needed
40
- npm i mimeforge # or add it to a project (CLI + library)
41
+ npx mimeforge pdf docx webm -o icons
41
42
  ```
42
43
 
43
- To work from source instead:
44
+ This writes `icons/pdf.svg`, `icons/docx.svg` and `icons/webm.svg`. Use them like any image:
45
+
46
+ ```html
47
+ <img src="icons/pdf.svg" width="32" height="32" alt="PDF">
48
+ ```
49
+
50
+ **Install:**
51
+
52
+ ```bash
53
+ npm i -g mimeforge # a global `mimeforge` command
54
+ npm i mimeforge # or as a dependency of your project (CLI via npx, plus the library API)
55
+ ```
56
+
57
+ **From source** (to hack on it):
44
58
 
45
59
  ```bash
46
60
  git clone https://github.com/reachdevel/mimeforge.git
47
61
  cd mimeforge
48
- npm install # also builds the project
62
+ npm install # also builds the project
49
63
  node dist/cli.js pdf docx webm -o icons
50
64
  ```
51
65
 
52
- This writes `icons/pdf.svg`, `icons/docx.svg` and `icons/webm.svg`. Use them like any image:
66
+ The examples below use the `mimeforge` command. Without a global install, put `npx` in front of it
67
+ (`npx mimeforge --all`); from a source checkout use `node dist/cli.js` or run `npm link` once.
53
68
 
54
- ```html
55
- <img src="icons/pdf.svg" width="32" height="32" alt="PDF">
69
+ ### Use it in a project
70
+
71
+ Generate the icons at build time, so your app only ships the SVGs it needs:
72
+
73
+ ```json
74
+ {
75
+ "scripts": {
76
+ "icons": "mimeforge --all -o public/icons"
77
+ },
78
+ "devDependencies": {
79
+ "mimeforge": "^0.1.1"
80
+ }
81
+ }
82
+ ```
83
+
84
+ Then `npm run icons` and reference `/icons/<ext>.svg`. `public/icons/manifest.json` maps every extension to its
85
+ category, which is handy for lookups. If you only show a few types, list them instead of `--all`. To render icons at
86
+ runtime instead (a server, a build plugin), use the [library API](#library-api).
87
+
88
+ ### Use it on a static host (with a fallback icon)
89
+
90
+ If your app serves a prebuilt set of files, say `/m/<size>/<ext>.png`, generate the whole set once:
91
+
92
+ ```bash
93
+ mimeforge --all --png 16,20,32,48 -o m
94
+ ```
95
+
96
+ ```
97
+ m/pdf.svg m/16/pdf.png m/20/pdf.png m/32/pdf.png m/48/pdf.png
98
+ m/default.svg m/16/default.png … the fallback, see below
99
+ m/manifest.json { "pdf": "pdf", "gdoc": "word", … } every known extension and its category
56
100
  ```
57
101
 
58
- The examples below use `mimeforge`. Install it globally with `npm i -g mimeforge`, use `npx mimeforge`, or (from a
59
- source checkout) run `npm link` once or replace `mimeforge` with `node dist/cli.js`.
102
+ Extensions MimeForge does not know (a `.hoppa` file) have no file of their own. `default.svg` and
103
+ `<size>/default.png` are the fallback for those: the generic page with a question mark and **no label** (an unknown
104
+ extension cannot be named). Point your app at it when an icon is missing:
105
+
106
+ ```js
107
+ const manifest = await (await fetch('/m/manifest.json')).json();
108
+ const src = ext in manifest ? `/m/32/${ext}.png` : '/m/32/default.png';
109
+ ```
110
+
111
+ or let the browser do it: `<img src="/m/32/hoppa.png" onerror="this.onerror=null;this.src='/m/32/default.png'">`.
112
+ To give one unknown extension its own icon instead, generate it explicitly (`mimeforge hoppa`, which draws the generic
113
+ body with the label `HOPPA`) or, from code, with a category of your choice (`renderIcon('hoppa', { category: 'archive' })`).
60
114
 
61
115
  ## Everyday use
62
116
 
63
117
  ```bash
64
118
  mimeforge pdf docx webm -o icons # a few icons
65
- mimeforge --all -o icons # every known extension, plus icons/manifest.json (ext → category)
119
+ mimeforge --all -o icons # every known extension, plus icons/default.svg and icons/manifest.json (ext → category)
120
+ mimeforge default -o icons # only the fallback icon: generic body, no label
66
121
  mimeforge --list # which extension gets which category
67
122
  mimeforge --test-run # visual check, see below
68
123
  ```
@@ -182,7 +237,7 @@ optional dependency `@resvg/resvg-js` (installed by default; if it is missing, M
182
237
 
183
238
  | Situation | What happens |
184
239
  |---|---|
185
- | Unknown extension (`mimeforge xyz`) | generic body (question mark) with the label `XYZ` |
240
+ | Unknown extension (`mimeforge xyz`) | generic body (question mark) with the label `XYZ`; for a label-less fallback file use `mimeforge default` (also written by `--all`) |
186
241
  | Long extension (`webmanifest`) | up to 6 letters are drawn at full size; longer text is scaled down proportionally to fit the label, and below about a third of the size letters are dropped from the end |
187
242
  | Light accent color | label text switches to a dark ink when white would have less than 3:1 contrast |
188
243
  | Character the font does not have | skipped |
@@ -199,10 +254,13 @@ how to change it.
199
254
 
200
255
  ## Library API
201
256
 
257
+ Install with `npm i mimeforge`. The package is ESM, so use `import` (from CommonJS: `const { renderIcon } = await import('mimeforge')`).
258
+
202
259
  ```ts
203
- import { renderIcon, loadFont, loadBodies, createPalette } from 'mimeforge';
260
+ import { renderIcon, renderDefaultIcon, loadFont, loadBodies, createPalette } from 'mimeforge';
204
261
 
205
262
  renderIcon('pdf').svg; // SVG string
263
+ renderDefaultIcon().svg; // fallback icon: generic body, no label
206
264
  renderIcon('docx', { accent: '#0a7', label: 'WORD' }).svg; // custom accent and label text
207
265
 
208
266
  renderIcon('xyz', {
@@ -214,15 +272,15 @@ renderIcon('xyz', {
214
272
  });
215
273
  ```
216
274
 
217
- `renderIcon` returns `{ svg, ext, category, accent }`. Other exports: `categoryFor`, `allExtensions`, `CATEGORIES`,
218
- `DEFAULT_PALETTE`, `validateBody`, `renderLabel`, `bitmapFont`, `outlineFont` (see `src/index.ts`).
219
- The package is ESM only.
275
+ `renderIcon` returns `{ svg, ext, category, accent }`. Other exports: `renderDefaultIcon`, `categoryFor`, `allExtensions`, `CATEGORIES`,
276
+ `DEFAULT_PALETTE`, `validateBody`, `renderLabel`, `bitmapFont`, `outlineFont` (see `src/index.ts`). Types are included.
220
277
 
221
278
  ## CLI reference
222
279
 
223
280
  ```
224
281
  mimeforge <ext...> [options] write <out>/<ext>.svg for the given extensions
225
- mimeforge --all [options] every known extension + manifest.json
282
+ mimeforge --all [options] every known extension + default.svg + manifest.json
283
+ mimeforge default [options] only the fallback icon (generic body, no label)
226
284
  mimeforge --test-run [options] HTML preview page (default dir: out/test-run)
227
285
  ```
228
286
 
package/dist/cli.js CHANGED
@@ -8,13 +8,14 @@ import { renderPng } from './png.js';
8
8
  import { loadFont } from './font.js';
9
9
  import { allExtensions, categoryFor } from './mime.js';
10
10
  import { createPalette } from './palette.js';
11
- import { renderIcon } from './render.js';
11
+ import { renderDefaultIcon, renderIcon } from './render.js';
12
12
  import { testRun } from './testrun.js';
13
13
  import { ROOT } from './paths.js';
14
14
  const HELP = `MimeForge – generate file-type icons (SVG) from file extensions
15
15
 
16
16
  Usage
17
17
  mimeforge <ext...> [options] write <out>/<ext>.svg for the given extensions
18
+ ("default" = the fallback icon without a label)
18
19
  mimeforge --all [options] every known extension (mime-db + extras) + manifest.json
19
20
  mimeforge --test-run [options] write an HTML page with all body types at 16/48/128 px
20
21
 
@@ -107,21 +108,22 @@ async function main() {
107
108
  const out = resolve(values.out ?? cfg.out ?? 'out');
108
109
  mkdirSync(out, { recursive: true });
109
110
  const pngSizes = (values.png ? values.png.split(',').map(Number) : cfg.png ?? []).filter((n) => Number.isFinite(n) && n > 0);
110
- const exts = values.all ? allExtensions() : positionals.map((e) => e.toLowerCase().replace(/^\./, ''));
111
+ const exts = values.all ? [...allExtensions(), 'default'] : positionals.map((e) => e.toLowerCase().replace(/^\./, ''));
111
112
  const manifest = {};
112
113
  let bytes = 0;
113
114
  for (const ext of exts) {
114
- const r = renderIcon(ext, opts);
115
+ const r = ext === 'default' ? renderDefaultIcon(opts) : renderIcon(ext, opts);
115
116
  writeFileSync(join(out, `${ext}.svg`), r.svg);
116
117
  for (const size of pngSizes) {
117
118
  mkdirSync(join(out, String(size)), { recursive: true });
118
119
  writeFileSync(join(out, String(size), `${ext}.png`), await renderPng(r.svg, size));
119
120
  }
120
- manifest[ext] = r.category;
121
+ if (ext !== 'default')
122
+ manifest[ext] = r.category;
121
123
  bytes += r.svg.length;
122
124
  }
123
125
  if (values.all)
124
126
  writeFileSync(join(out, 'manifest.json'), JSON.stringify(manifest, null, 1));
125
- console.log(`${exts.length} icon(s) → ${out} (${(bytes / 1024).toFixed(0)} KB total, ${(bytes / exts.length / 1024).toFixed(1)} KB avg)`);
127
+ console.log(`${exts.length} icon(s)${values.all ? ' (incl. default)' : ''} → ${out} (${(bytes / 1024).toFixed(0)} KB total, ${(bytes / exts.length / 1024).toFixed(1)} KB avg)`);
126
128
  }
127
129
  main().catch((e) => fail(e.message));
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { renderIcon, type RenderOptions, type RenderResult } from './render.js';
1
+ export { renderIcon, renderDefaultIcon, type RenderOptions, type RenderResult } from './render.js';
2
2
  export { CATEGORIES, classify, classifyMime, isCategory, type Category } from './categories.js';
3
3
  export { allExtensions, categoryFor, extToMime } from './mime.js';
4
4
  export { DEFAULT_PALETTE, createPalette, accentFor, type Palette } from './palette.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { renderIcon } from './render.js';
1
+ export { renderIcon, renderDefaultIcon } from './render.js';
2
2
  export { CATEGORIES, classify, classifyMime, isCategory } from './categories.js';
3
3
  export { allExtensions, categoryFor, extToMime } from './mime.js';
4
4
  export { DEFAULT_PALETTE, createPalette, accentFor } from './palette.js';
package/dist/png.js CHANGED
@@ -7,5 +7,6 @@ export async function renderPng(svg, size) {
7
7
  catch {
8
8
  throw new Error('PNG export needs the optional dependency @resvg/resvg-js (npm i @resvg/resvg-js).');
9
9
  }
10
- return new mod.Resvg(svg, { fitTo: { mode: 'width', value: size } }).render().asPng();
10
+ // Our icons contain no <text>, so skip loading system fonts (it dominates the render time).
11
+ return new mod.Resvg(svg, { fitTo: { mode: 'width', value: size }, font: { loadSystemFonts: false } }).render().asPng();
11
12
  }
package/dist/render.d.ts CHANGED
@@ -24,3 +24,8 @@ export interface RenderResult {
24
24
  accent: string;
25
25
  }
26
26
  export declare function renderIcon(extension: string, opts?: RenderOptions): RenderResult;
27
+ /**
28
+ * The fallback icon for extensions that have no icon of their own: the generic body without a label
29
+ * (an unknown extension cannot be named). Written as `default.svg` by `mimeforge --all`.
30
+ */
31
+ export declare function renderDefaultIcon(opts?: RenderOptions): RenderResult;
package/dist/render.js CHANGED
@@ -21,3 +21,10 @@ export function renderIcon(extension, opts = {}) {
21
21
  }
22
22
  return { svg: svg.replace(/>\s*\n\s*</g, '><').trim() + '\n', ext, category, accent };
23
23
  }
24
+ /**
25
+ * The fallback icon for extensions that have no icon of their own: the generic body without a label
26
+ * (an unknown extension cannot be named). Written as `default.svg` by `mimeforge --all`.
27
+ */
28
+ export function renderDefaultIcon(opts = {}) {
29
+ return renderIcon('default', { ...opts, category: 'generic', label: false });
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mimeforge",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Generate file-type icons as SVG from a file extension: 24 bundled bodies, extension labels, pluggable fonts, bodies and colors.",
5
5
  "author": {
6
6
  "name": "Levent Kurt",