-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfn_generate_favicons
More file actions
145 lines (127 loc) · 4.25 KB
/
Copy pathfn_generate_favicons
File metadata and controls
145 lines (127 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env node
import fs from "fs";
import path from "path";
import {readFile, writeFile} from "fs/promises";
import imagemin from "imagemin";
import imageminPngquant from "imagemin-pngquant";
import sharp from "sharp";
import {optimize} from "svgo";
import png2icons from "png2icons";
const { version } = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
async function main() {
const args = {};
const cliArgs = process.argv.slice(2);
for (let i = 0; i < cliArgs.length; i++) {
const arg = cliArgs[i];
switch (arg) {
case "--png":
case "-p":
args.pngSrc = cliArgs[++i];
break;
case "--svg":
case "-s":
args.svgSrc = cliArgs[++i];
break;
case "--dist":
case "-d":
args.dist = cliArgs[++i];
break;
case "--version":
case "-v":
console.log(version);
process.exit(0);
break;
case "--help":
case "-h":
console.log(`
Usage:
favgen [--png <path>] [--svg <path>] [--dist <output-dir>]
PNG source is required: either pass --png flag or place favicon_src.png in current directory.
Arguments:
--png, -p Path to PNG source. Defaults to "favicon_src.png".
--svg, -s Path to SVG source. Defaults to "favicon_src.svg".
If file not found, SVG processing is skipped.
--dist, -d Output directory. Creates temporary directory if not specified.
--version, -v Print version and exit.
--help, -h Show this help message and exit.
Generated files:
favicon.ico, favicon-16x16.png, favicon-32x32.png, favicon-48x48.png,
apple-touch-icon.png, icon-192.png, icon-512.png, [favicon.svg]
`);
process.exit(0);
}
}
const pngSrc = path.resolve(process.cwd(), args.pngSrc || "favicon_src.png");
if (!fs.existsSync(pngSrc)) {
console.error("PNG source file not found");
process.exit(1);
}
const svgSrc = args.svgSrc
? path.resolve(process.cwd(), args.svgSrc)
: path.resolve(process.cwd(), "favicon_src.svg");
if (args.svgSrc && !fs.existsSync(svgSrc)) {
console.error("SVG source file not found");
process.exit(1);
}
const distDir = args.dist || fs.mkdtempSync(path.join(process.cwd(), "favicons_"));
if (args.dist) {fs.mkdirSync(distDir, {recursive: true});}
const generatedFiles = [];
try {
const pngBuffer = await readFile(pngSrc);
const optimizedPng = await sharp(pngBuffer)
.resize(256, 256, {
fit: "contain",
background: { r: 0, g: 0, b: 0, alpha: 0 }
})
.png({ compressionLevel: 9, palette: true, colors: 128 })
.toBuffer();
const icoBuffer = png2icons.createICO(optimizedPng, 2, 256, true);
await writeFile(path.join(distDir, "favicon.ico"), icoBuffer);
generatedFiles.push("favicon.ico");
} catch (err) {
console.error("ICO generation failed:", err.message);
}
const pngSizes = [
{name: "favicon-16x16.png", size: 16},
{name: "favicon-32x32.png", size: 32},
{name: "favicon-48x48.png", size: 48},
{name: "apple-touch-icon.png", size: 180},
{name: "icon-192.png", size: 192},
{name: "icon-512.png", size: 512}
];
await Promise.all(
pngSizes.map(async ({name, size}) => {
try {
const outputPath = path.join(distDir, name);
await sharp(pngSrc)
.resize(size, size, {
fit: "contain",
background: {r: 0, g: 0, b: 0, alpha: 0}
})
.png({compressionLevel: 6})
.toFile(outputPath);
await imagemin([outputPath], {
destination: distDir,
plugins: [imageminPngquant({quality: [0.7, 0.85]})]
});
generatedFiles.push(name);
} catch (err) {
console.error(`Failed to process ${name}:`, err.message);
}
})
);
if (fs.existsSync(svgSrc)) {
try {
const svgContent = await readFile(svgSrc, "utf8");
const optimized = optimize(svgContent, { multipass: true });
await writeFile(path.join(distDir, "favicon.svg"), optimized.data);
generatedFiles.push("favicon.svg");
} catch (err) {
console.error("SVG optimization failed:", err.message);
}
}
console.log(`Generated ${generatedFiles.length} files → ${distDir}`);
}
main().catch((err) => {
console.error("Unexpected error:", err.message);
});