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 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
+ ```
@@ -0,0 +1,3 @@
1
+ export { default as produceIcons } from "./generator.js";
2
+ export { default as favgenVitePlugin } from "./vite-plugin.js";
3
+ export type { FavgenVitePluginOptions } from "./vite-plugin.js";
package/lib/index.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export { default as produceIcons } from "./generator";
2
+ export { default as favgenVitePlugin } from "./vite-plugin";
3
+ export type { FavgenVitePluginOptions } from "./vite-plugin";
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,7 @@
1
+ import type { Plugin } from "vite";
2
+ export declare type FavgenVitePluginOptions = {
3
+ source: string;
4
+ colors?: number;
5
+ assetsPath?: string;
6
+ };
7
+ export default function favgenVitePlugin(options: FavgenVitePluginOptions): Plugin;
@@ -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.0",
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
  }