favgen 0.2.0 → 0.2.2
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/README.md +16 -0
- package/lib/index.d.mts +3 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/lib/index.mjs +10 -0
- package/lib/vite-plugin.d.ts +7 -0
- package/lib/vite-plugin.js +155 -0
- package/package.json +21 -2
package/README.md
CHANGED
|
@@ -31,3 +31,19 @@ const outputDirPath = "__favicons__"
|
|
|
31
31
|
const paletteSize = 64 // default value
|
|
32
32
|
produceIcons(inputFilePath, outputDirPath, paletteSize)
|
|
33
33
|
```
|
|
34
|
+
|
|
35
|
+
Vite plugin usage:
|
|
36
|
+
```js
|
|
37
|
+
import { defineConfig } from "vite"
|
|
38
|
+
import { favgenVitePlugin } from "favgen"
|
|
39
|
+
|
|
40
|
+
export default defineConfig({
|
|
41
|
+
plugins: [
|
|
42
|
+
favgenVitePlugin({
|
|
43
|
+
source: "src/assets/logo.svg",
|
|
44
|
+
colors: 64,
|
|
45
|
+
assetsPath: "favicons",
|
|
46
|
+
}),
|
|
47
|
+
],
|
|
48
|
+
})
|
|
49
|
+
```
|
package/lib/index.d.mts
ADDED
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.produceIcons = void 0;
|
|
6
|
+
exports.favgenVitePlugin = exports.produceIcons = void 0;
|
|
7
7
|
// eslint-disable-next-line import/prefer-default-export
|
|
8
8
|
var generator_1 = require("./generator");
|
|
9
9
|
Object.defineProperty(exports, "produceIcons", { enumerable: true, get: function () { return __importDefault(generator_1).default; } });
|
|
10
|
+
var vite_plugin_1 = require("./vite-plugin");
|
|
11
|
+
Object.defineProperty(exports, "favgenVitePlugin", { enumerable: true, get: function () { return __importDefault(vite_plugin_1).default; } });
|
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.favgenVitePlugin = exports.produceIcons = void 0;
|
|
7
|
+
var generator_js_1 = require("./generator.js");
|
|
8
|
+
Object.defineProperty(exports, "produceIcons", { enumerable: true, get: function () { return __importDefault(generator_js_1).default; } });
|
|
9
|
+
var vite_plugin_js_1 = require("./vite-plugin.js");
|
|
10
|
+
Object.defineProperty(exports, "favgenVitePlugin", { enumerable: true, get: function () { return __importDefault(vite_plugin_js_1).default; } });
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
7
|
+
const os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const generator_1 = __importDefault(require("./generator"));
|
|
10
|
+
function normalizeAssetsPath(rawPath) {
|
|
11
|
+
if (!rawPath) {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
return rawPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
15
|
+
}
|
|
16
|
+
function getAssetUrl(base, assetsPath, filename) {
|
|
17
|
+
const relativePath = assetsPath
|
|
18
|
+
? path_1.default.posix.join(assetsPath, filename)
|
|
19
|
+
: filename;
|
|
20
|
+
const normalizedBase = base.trim();
|
|
21
|
+
if (normalizedBase === "." || normalizedBase === "./") {
|
|
22
|
+
return relativePath;
|
|
23
|
+
}
|
|
24
|
+
if (/^https?:\/\//.test(normalizedBase)) {
|
|
25
|
+
return new URL(relativePath, normalizedBase).toString();
|
|
26
|
+
}
|
|
27
|
+
const baseWithSlash = normalizedBase.endsWith("/")
|
|
28
|
+
? normalizedBase
|
|
29
|
+
: `${normalizedBase}/`;
|
|
30
|
+
return `${baseWithSlash}${relativePath}`;
|
|
31
|
+
}
|
|
32
|
+
function isValidPaletteSize(size) {
|
|
33
|
+
return Number.isInteger(size) && size >= 2 && size <= 256;
|
|
34
|
+
}
|
|
35
|
+
function rewriteManifest(manifestBuffer, base, assetsPath) {
|
|
36
|
+
const manifest = JSON.parse(manifestBuffer.toString("utf8"));
|
|
37
|
+
if (Array.isArray(manifest.icons)) {
|
|
38
|
+
manifest.icons = manifest.icons.map((icon) => {
|
|
39
|
+
const sourcePath = icon.src;
|
|
40
|
+
const iconFilename = typeof sourcePath === "string"
|
|
41
|
+
? path_1.default.posix.basename(sourcePath)
|
|
42
|
+
: "";
|
|
43
|
+
return {
|
|
44
|
+
...icon,
|
|
45
|
+
src: getAssetUrl(base, assetsPath, iconFilename),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return Buffer.from(JSON.stringify(manifest, null, 2));
|
|
50
|
+
}
|
|
51
|
+
function getHtmlTags(base, assetsPath, hasSvgIcon) {
|
|
52
|
+
const tags = [
|
|
53
|
+
{
|
|
54
|
+
tag: "link",
|
|
55
|
+
attrs: {
|
|
56
|
+
rel: "icon",
|
|
57
|
+
href: getAssetUrl(base, assetsPath, "favicon.ico"),
|
|
58
|
+
sizes: "any",
|
|
59
|
+
},
|
|
60
|
+
injectTo: "head",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
tag: "link",
|
|
64
|
+
attrs: {
|
|
65
|
+
rel: "apple-touch-icon",
|
|
66
|
+
href: getAssetUrl(base, assetsPath, "apple-touch-icon.png"),
|
|
67
|
+
},
|
|
68
|
+
injectTo: "head",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
tag: "link",
|
|
72
|
+
attrs: {
|
|
73
|
+
rel: "manifest",
|
|
74
|
+
href: getAssetUrl(base, assetsPath, "manifest.webmanifest"),
|
|
75
|
+
},
|
|
76
|
+
injectTo: "head",
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
if (hasSvgIcon) {
|
|
80
|
+
tags.splice(1, 0, {
|
|
81
|
+
tag: "link",
|
|
82
|
+
attrs: {
|
|
83
|
+
rel: "icon",
|
|
84
|
+
href: getAssetUrl(base, assetsPath, "icon.svg"),
|
|
85
|
+
type: "image/svg+xml",
|
|
86
|
+
},
|
|
87
|
+
injectTo: "head",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return tags;
|
|
91
|
+
}
|
|
92
|
+
function favgenVitePlugin(options) {
|
|
93
|
+
const assetsPath = normalizeAssetsPath(options.assetsPath);
|
|
94
|
+
const paletteSize = options.colors ?? 64;
|
|
95
|
+
const sourcePath = options.source;
|
|
96
|
+
let config = null;
|
|
97
|
+
let tempDirPath = null;
|
|
98
|
+
let generatedAssets = [];
|
|
99
|
+
let hasSvgIcon = false;
|
|
100
|
+
return {
|
|
101
|
+
name: "favgen-vite-plugin",
|
|
102
|
+
apply: "build",
|
|
103
|
+
configResolved(resolvedConfig) {
|
|
104
|
+
config = resolvedConfig;
|
|
105
|
+
},
|
|
106
|
+
async buildStart() {
|
|
107
|
+
if (!config) {
|
|
108
|
+
throw new Error("favgen-vite-plugin: Vite config is not resolved.");
|
|
109
|
+
}
|
|
110
|
+
if (!sourcePath || sourcePath.trim().length === 0) {
|
|
111
|
+
throw new Error("favgen-vite-plugin: `source` option is required.");
|
|
112
|
+
}
|
|
113
|
+
if (!isValidPaletteSize(paletteSize)) {
|
|
114
|
+
throw new Error("favgen-vite-plugin: `colors` must be an integer between 2 and 256.");
|
|
115
|
+
}
|
|
116
|
+
const resolvedSourcePath = path_1.default.isAbsolute(sourcePath)
|
|
117
|
+
? sourcePath
|
|
118
|
+
: path_1.default.resolve(config.root, sourcePath);
|
|
119
|
+
tempDirPath = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "favgen-vite-"));
|
|
120
|
+
await (0, generator_1.default)(resolvedSourcePath, tempDirPath, paletteSize);
|
|
121
|
+
const fileNames = (await promises_1.default.readdir(tempDirPath)).sort();
|
|
122
|
+
hasSvgIcon = fileNames.includes("icon.svg");
|
|
123
|
+
const assets = await Promise.all(fileNames.map(async (filename) => ({
|
|
124
|
+
filename,
|
|
125
|
+
source: await promises_1.default.readFile(path_1.default.join(tempDirPath, filename)),
|
|
126
|
+
})));
|
|
127
|
+
generatedAssets = assets.map((asset) => (asset.filename === "manifest.webmanifest"
|
|
128
|
+
? {
|
|
129
|
+
...asset,
|
|
130
|
+
source: rewriteManifest(asset.source, config?.base ?? "/", assetsPath),
|
|
131
|
+
}
|
|
132
|
+
: asset));
|
|
133
|
+
},
|
|
134
|
+
generateBundle() {
|
|
135
|
+
generatedAssets.forEach((asset) => {
|
|
136
|
+
this.emitFile({
|
|
137
|
+
type: "asset",
|
|
138
|
+
fileName: assetsPath
|
|
139
|
+
? path_1.default.posix.join(assetsPath, asset.filename)
|
|
140
|
+
: asset.filename,
|
|
141
|
+
source: asset.source,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
transformIndexHtml() {
|
|
146
|
+
return getHtmlTags(config?.base ?? "/", assetsPath, hasSvgIcon);
|
|
147
|
+
},
|
|
148
|
+
async closeBundle() {
|
|
149
|
+
if (tempDirPath) {
|
|
150
|
+
await promises_1.default.rm(tempDirPath, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
exports.default = favgenVitePlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "favgen",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "CLI tool to generate a set of favicons from a single input file.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"favicon"
|
|
@@ -10,18 +10,36 @@
|
|
|
10
10
|
"homepage": "https://github.com/favgen/favgen",
|
|
11
11
|
"repository": "https://github.com/favgen/favgen.git",
|
|
12
12
|
"main": "lib/index.js",
|
|
13
|
+
"module": "lib/index.mjs",
|
|
13
14
|
"types": "lib/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./lib/index.mjs",
|
|
18
|
+
"require": "./lib/index.js",
|
|
19
|
+
"types": "./lib/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
14
22
|
"bin": "bin/index.js",
|
|
15
23
|
"files": [
|
|
16
24
|
"lib/",
|
|
17
25
|
"bin/"
|
|
18
26
|
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependenciesMeta": {
|
|
31
|
+
"vite": {
|
|
32
|
+
"optional": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
19
35
|
"engines": {
|
|
20
36
|
"node": ">= 16"
|
|
21
37
|
},
|
|
22
38
|
"scripts": {
|
|
23
39
|
"build": "tsc --project tsconfig.json",
|
|
24
40
|
"build:dev": "tsc --watch --project tsconfig.json",
|
|
41
|
+
"test": "npm run build && vitest run",
|
|
42
|
+
"test:watch": "npm run build && vitest",
|
|
25
43
|
"prepublishOnly": "npm run build",
|
|
26
44
|
"huskify": "husky install && husky add .husky/pre-commit 'npx lint-staged' && git add .husky/pre-commit",
|
|
27
45
|
"lint": "eslint --ext .js,.cjs,.mjs,.ts,.cts,.mts --fix --ignore-path .gitignore --cache",
|
|
@@ -49,6 +67,7 @@
|
|
|
49
67
|
"husky": "^8.0.1",
|
|
50
68
|
"lint-staged": "^13.0.3",
|
|
51
69
|
"prettier": "^2.7.1",
|
|
52
|
-
"typescript": "^4.7.4"
|
|
70
|
+
"typescript": "^4.7.4",
|
|
71
|
+
"vitest": "^3.2.4"
|
|
53
72
|
}
|
|
54
73
|
}
|