From 200ec91d85e9d97c6d18d670190a74e6b6c2fee4 Mon Sep 17 00:00:00 2001 From: firefoxic Date: Fri, 5 Apr 2024 19:15:11 +0300 Subject: [PATCH] Add generating webmanifest --- README.md | 3 +- bin/cli.js | 2 +- lib/createRasterFavicons.js | 64 +++++++++++++++++++++++++++++-------- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5e3972b..a377909 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ conjure [options] - `images` — Optimize SVGs and convert raster images to modern formats (Webp and Avif by default). - `icons` — Optimize SVG icon files. -- `favicons` — Optimize SVG favicon and generate from it raster favicons, including ICO format and all necessary PNG and Webp. +- `favicons` — Optimize SVG favicon and generate from it raster favicons, including ICO format and all necessary PNG and Webp, and also generate a webmanifest. - `all` — Run all the above commands. > **Notice**: Individual commands handle the contents of the specified directory. But the general `all` command expects the path to the directory containing the `images`, `icons` and `favicons` directories (see [examples](#examples) below). @@ -71,6 +71,7 @@ conjure [options] - `-180.png` in size `180×180` for old iPhones - `-192.png` and `-192.webp` in size `192×192` - `-512.png` and `-512.webp` in size `512×512` + - `.webmanifest` with the `name` and `description` fields from your `package.json` and the `icons` field for the `192` and `512` files ```shell conjure favicons -i assets/favicons diff --git a/bin/cli.js b/bin/cli.js index 4ecfcd2..efd33a6 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -16,7 +16,7 @@ Commands Optimize SVG icon files. favicons - Optimize SVG favicon and generate from it raster favicons, including ICO format and all necessary PNG and Webp. + Optimize SVG favicon and generate from it raster favicons, including ICO format and all necessary PNG and Webp, and also generate a webmanifest. all Run all the above commands. diff --git a/lib/createRasterFavicons.js b/lib/createRasterFavicons.js index 061c94e..96264a1 100644 --- a/lib/createRasterFavicons.js +++ b/lib/createRasterFavicons.js @@ -1,14 +1,27 @@ -import { access, mkdir } from "node:fs/promises" +import { access, mkdir, readFile, writeFile } from "node:fs/promises" import { basename, extname, dirname, join, resolve } from "node:path" import sharp from "sharp" export async function createRasterFavicons({ vectorPaths, inputDirectory, outputDirectory }) { + let packageInfo = {} + + try { + const packageJson = await readFile(resolve(process.cwd(), `package.json`), `utf8`) + packageInfo = JSON.parse(packageJson) + } catch (error) { + console.error(`Error reading package.json:`, error) + } + for (const filePath of vectorPaths) { const fileName = basename(filePath, extname(filePath)) const baseName = basename(fileName, extname(fileName)) const subfolder = dirname(filePath.substring(inputDirectory.length)) const destSubfolder = join(outputDirectory, subfolder) + const rootRelativePath = destSubfolder.split(`/`).slice(1).join(`/`) + const sizes = [180, 192, 512] + const formats = [`png`, `webp`] + let icons = [] try { await access(destSubfolder) @@ -16,21 +29,44 @@ export async function createRasterFavicons({ vectorPaths, inputDirectory, output await mkdir(destSubfolder, { recursive: true }) } - for (const format of [`png`, `webp`]) { - for (const size of [512, 192, 180]) { - if (!(format === `webp` && size === 180)) { - try { - const image = sharp(filePath) - .resize(size) - .toFormat(format, { lossless: true }) - - const outputPath = resolve(destSubfolder, `${baseName}-${size}.${format}`) - await image.toFile(outputPath) - } catch (error) { - console.error(`Error processing ${filePath}:`, error) - } + for (const format of formats) { + for (const size of sizes) { + if (format === `webp` && size === 180) continue + + try { + const image = sharp(filePath) + .resize(size) + .toFormat(format, { lossless: true }) + + const outputPath = resolve(destSubfolder, `${baseName}-${size}.${format}`) + await image.toFile(outputPath) + } catch (error) { + console.error(`Error processing ${filePath}:`, error) } + + if (size === 180) continue + + icons.push( + { + src: `/${rootRelativePath}${baseName}-${size}.${format}`, + sizes: `${size}x${size}`, + type: `image/${format}`, + }, + ) } } + + const webmanifest = { + ...(packageInfo.name && { name: packageInfo.name }), + ...(packageInfo.description && { description: packageInfo.description }), + icons, + } + + const webmanifestPath = resolve(destSubfolder, `${baseName}.webmanifest`) + try { + await writeFile(webmanifestPath, JSON.stringify(webmanifest, null, `\t`)) + } catch (error) { + console.error(`Error writing ${baseName}.webmanifest file:`, error) + } } }