1
0

chore: add generate pwa icons script (#2130)

This commit is contained in:
Joaquín Sánchez 2023-05-29 16:52:27 +02:00 committed by GitHub
parent 0767df3f78
commit d601a117c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 449 additions and 9 deletions

View File

@ -6,6 +6,8 @@
*.txt
Dockerfile
public/
public-dev/
public-staging/
https-dev-config/localhost.crt
https-dev-config/localhost.key
Dockerfile

View File

@ -15,6 +15,16 @@ export const pwa: VitePWANuxtOptions = {
injectManifest: {
globPatterns: ['**/*.{js,json,css,html,txt,svg,png,ico,webp,woff,woff2,ttf,eot,otf,wasm}'],
globIgnores: ['emojis/**', 'shiki/**', 'manifest**.webmanifest'],
manifestTransforms: [(entries) => {
const manifest = entries.map((entry) => {
if (entry.url.length > 1 && entry.url[0] !== '/')
entry.url = `/${entry.url}`
return entry
})
return { manifest, warnings: [] }
}],
},
devOptions: {
enabled: process.env.VITE_DEV_PWA === 'true',

View File

@ -103,6 +103,16 @@ Elk will generate 2 web manifests per locale, one for light theme and one for da
You can check web manifest generation on [modules/pwa/i18n.ts](https://github.com/elk-zone/elk/blob/main/modules/pwa/i18n.ts) module.
### PWA Icons
Elk's favicon and PWA icons are generated from [Elk's SVG Logo](https://github.com/elk-zone/elk/blob/main/public/logo.svg) via [custom script](https://github.com/elk-zone/elk/blob/main/scripts/generate-pwa-icons.ts), using [sharp](https://github.com/lovell/sharp/) and [sharp-io](https://github.com/ssnangua/sharp-ico) libraries:
- favicon.ico: transparent 64x64 32-bits icon
- pwa-64x64.png: transparent 64x64 8-bits icon (optimized from 32-bitss color)
- pwa-192x192.png: transparent 192x192 8-bits icon (optimized from 32-bits color)
- pwa-512x512.png: transparent 512x512 8-bits icon (optimized from 32-bit color)
- maskable-icon.png: white background 512x512 8-bits icon (optimized from 32-bits color)
- apple-touch-icon.png: white background 180x180 8-bits icon (optimized from 32-bits color)
### PWA UI Components
Elk will provide a set of UI components to allow you to customize the PWA installation prompt on browsers with [beforeinstallprompt](https://web.dev/customize-install/) support.

View File

@ -76,8 +76,13 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
orientation: 'natural',
display: 'standalone',
display_override: ['window-controls-overlay'],
categories: ['social', 'social networking'],
categories: ['social', 'social networking', 'news'],
icons: [
{
src: 'pwa-64x64.png',
sizes: '64x64',
type: 'image/png',
},
{
src: 'pwa-192x192.png',
sizes: '192x192',
@ -114,6 +119,8 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
},
}
// TODO: add related_applications, only when env === 'release'
const locales: RequiredWebManifestEntry[] = await Promise.all(
pwaLocales
.filter(l => l.code !== 'en-US')

View File

@ -25,6 +25,7 @@
"update:team:avatars": "tsx scripts/avatars.ts",
"cleanup-translations": "tsx scripts/cleanup-translations.ts",
"prepare-translation-status": "tsx scripts/prepare-translation-status.ts",
"generate-pwa-icons": "tsx scripts/generate-pwa-icons.ts",
"postinstall": "ignore-dependency-scripts \"stale-dep -u && simple-git-hooks && nuxi prepare && nr prepare-translation-status\"",
"release": "stale-dep && bumpp && tsx scripts/release.ts"
},
@ -94,7 +95,7 @@
"ufo": "^1.1.2",
"ultrahtml": "^1.2.0",
"unimport": "^3.0.7",
"vite-plugin-pwa": "^0.15.0",
"vite-plugin-pwa": "^0.15.1",
"vue-advanced-cropper": "^2.8.8",
"vue-virtual-scroller": "2.0.0-beta.8",
"workbox-build": "^6.5.4",
@ -120,6 +121,8 @@
"lint-staged": "^13.2.2",
"nuxt": "3.5.2",
"prettier": "^2.8.8",
"sharp": "^0.32.1",
"sharp-ico": "^0.1.5",
"simple-git-hooks": "^2.8.1",
"tsx": "^3.12.7",
"typescript": "^5.0.4",

View File

@ -205,8 +205,8 @@ importers:
specifier: ^3.0.7
version: 3.0.7(rollup@2.79.1)
vite-plugin-pwa:
specifier: ^0.15.0
version: 0.15.0(vite@4.3.9)(workbox-build@6.5.4)(workbox-window@6.5.4)
specifier: ^0.15.1
version: 0.15.1(vite@4.3.9)(workbox-build@6.5.4)(workbox-window@6.5.4)
vue-advanced-cropper:
specifier: ^2.8.8
version: 2.8.8(vue@3.3.4)
@ -277,6 +277,12 @@ importers:
prettier:
specifier: ^2.8.8
version: 2.8.8
sharp:
specifier: ^0.32.1
version: 0.32.1
sharp-ico:
specifier: ^0.1.5
version: 0.1.5
simple-git-hooks:
specifier: ^2.8.1
version: 2.8.1
@ -1599,6 +1605,10 @@ packages:
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
/@canvas/image-data@1.0.0:
resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==}
dev: true
/@cloudflare/kv-asset-handler@0.3.0:
resolution: {integrity: sha512-9CB/MKf/wdvbfkUdfrj+OkEwZ5b7rws0eogJ4293h+7b6KX5toPwym+VQKmILafNB9YiehqY0DlNrDcDhdWHSQ==}
dependencies:
@ -5908,6 +5918,10 @@ packages:
optionalDependencies:
fsevents: 2.3.2
/chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: true
/chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
@ -6023,10 +6037,25 @@ packages:
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
/color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
dev: true
/color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
hasBin: true
/color@4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
dev: true
/colord@2.9.3:
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
@ -6343,18 +6372,47 @@ packages:
dependencies:
ms: 2.1.2
/decode-bmp@0.2.1:
resolution: {integrity: sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==}
engines: {node: '>=8.6.0'}
dependencies:
'@canvas/image-data': 1.0.0
to-data-view: 1.1.0
dev: true
/decode-ico@0.4.1:
resolution: {integrity: sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==}
engines: {node: '>=8.6'}
dependencies:
'@canvas/image-data': 1.0.0
decode-bmp: 0.2.1
to-data-view: 1.1.0
dev: true
/decode-named-character-reference@1.0.2:
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
dependencies:
character-entities: 2.0.2
dev: true
/decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
dependencies:
mimic-response: 3.1.0
dev: true
/deep-eql@4.1.3:
resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
engines: {node: '>=6'}
dependencies:
type-detect: 4.0.8
/deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
dev: true
/deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@ -7418,6 +7476,11 @@ packages:
signal-exit: 3.0.7
strip-final-newline: 3.0.0
/expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
dev: true
/extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
dev: true
@ -7811,6 +7874,10 @@ packages:
dependencies:
git-up: 7.0.0
/github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
dev: true
/github-reserved-names@2.0.4:
resolution: {integrity: sha512-T2azXbRJTJGQc28G6x89LpzQmuVjzl0hzJXPRD2t9yMh7URYUW8Opqr5ptHvjAVDJ+hwhBtoYmVx3VyFawRoFg==}
dev: false
@ -8194,6 +8261,10 @@ packages:
ms: 2.1.3
dev: false
/ico-endec@0.1.6:
resolution: {integrity: sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==}
dev: true
/iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
@ -8377,6 +8448,10 @@ packages:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: true
/is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
dev: true
/is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
@ -9724,6 +9799,11 @@ packages:
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
engines: {node: '>=12'}
/mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
dev: true
/min-indent@1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
@ -9839,6 +9919,10 @@ packages:
resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==}
dev: false
/mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
dev: true
/mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
@ -9917,6 +10001,10 @@ packages:
engines: {node: ^14 || ^16 || >=18}
hasBin: true
/napi-build-utils@1.0.2:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
dev: true
/natural-compare-lite@1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
dev: true
@ -10019,6 +10107,17 @@ packages:
lower-case: 2.0.2
tslib: 2.5.2
/node-abi@3.40.0:
resolution: {integrity: sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==}
engines: {node: '>=10'}
dependencies:
semver: 7.5.1
dev: true
/node-addon-api@6.1.0:
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
dev: true
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@ -11301,6 +11400,25 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/prebuild-install@7.1.1:
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
engines: {node: '>=10'}
hasBin: true
dependencies:
detect-libc: 2.0.1
expand-template: 2.0.3
github-from-package: 0.0.0
minimist: 1.2.8
mkdirp-classic: 0.5.3
napi-build-utils: 1.0.2
node-abi: 3.40.0
pump: 3.0.0
rc: 1.2.8
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
dev: true
/prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@ -11521,6 +11639,13 @@ packages:
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
dev: false
/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
dev: true
/punycode@2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
@ -11554,6 +11679,16 @@ packages:
destr: 1.2.2
flat: 5.0.2
/rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
dependencies:
deep-extend: 0.6.0
ini: 1.3.8
minimist: 1.2.8
strip-json-comments: 2.0.1
dev: true
/react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
@ -12153,6 +12288,29 @@ packages:
/setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
/sharp-ico@0.1.5:
resolution: {integrity: sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q==}
dependencies:
decode-ico: 0.4.1
ico-endec: 0.1.6
sharp: 0.32.1
dev: true
/sharp@0.32.1:
resolution: {integrity: sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==}
engines: {node: '>=14.15.0'}
requiresBuild: true
dependencies:
color: 4.2.3
detect-libc: 2.0.1
node-addon-api: 6.1.0
prebuild-install: 7.1.1
semver: 7.5.1
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
dev: true
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -12213,6 +12371,18 @@ packages:
- supports-color
dev: false
/simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
dev: true
/simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
dependencies:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
dev: true
/simple-git-hooks@2.8.1:
resolution: {integrity: sha512-DYpcVR1AGtSfFUNzlBdHrQGPsOhuuEJ/FkmPOOlFysP60AHd3nsEpkGq/QEOdtUyT1Qhk7w9oLmFoMG+75BDog==}
hasBin: true
@ -12229,6 +12399,12 @@ packages:
- supports-color
dev: false
/simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
dependencies:
is-arrayish: 0.3.2
dev: true
/sirv@2.0.3:
resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==}
engines: {node: '>= 10'}
@ -12574,6 +12750,11 @@ packages:
min-indent: 1.0.1
dev: true
/strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
dev: true
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@ -12671,6 +12852,15 @@ packages:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
/tar-fs@2.1.1:
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
dependencies:
chownr: 1.1.4
mkdirp-classic: 0.5.3
pump: 3.0.0
tar-stream: 2.2.0
dev: true
/tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
@ -12819,6 +13009,10 @@ packages:
dependencies:
os-tmpdir: 1.0.2
/to-data-view@1.1.0:
resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==}
dev: true
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@ -12910,6 +13104,12 @@ packages:
- supports-color
dev: false
/tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
safe-buffer: 5.2.1
dev: true
/type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -13698,8 +13898,8 @@ packages:
- supports-color
dev: false
/vite-plugin-pwa@0.15.0(vite@4.3.9)(workbox-build@6.5.4)(workbox-window@6.5.4):
resolution: {integrity: sha512-gpmx3BeubsRIXRBkjPToOTJbo8fknNmZFQs24i0TPZyaNVa0n27YHDo0Y72amnO70WvHKGE3e1fn8SYUP7e8SA==}
/vite-plugin-pwa@0.15.1(vite@4.3.9)(workbox-build@6.5.4)(workbox-window@6.5.4):
resolution: {integrity: sha512-lJVzEYda/Y9AfwxFzX0rV+QCQ2+WdBoEGtR1RBZKWxvrJ4NWEH1VZaHOMyzvRiYhWQsi7aFhewsp1CDvN/R1Og==}
peerDependencies:
vite: ^3.1.0 || ^4.0.0
workbox-build: ^6.5.4

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 993 B

View File

@ -18,7 +18,7 @@
maskUnits="userSpaceOnUse"
style="mask-type:alpha">
<path
fill="#D9D9D9"
fill="white"
d="M244 123c0 64.617-38.383 112-103 112-64.617 0-103-30.883-103-95.5C38 111.194-8.729 36.236 8 16 29.46-9.959 88.689 6 125 6c64.617 0 119 52.383 119 117Z"
id="path19" />
</mask>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
public-dev/pwa-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 965 B

View File

@ -18,7 +18,7 @@
maskUnits="userSpaceOnUse"
style="mask-type:alpha">
<path
fill="#D9D9D9"
fill="white"
d="M244 123c0 64.617-38.383 112-103 112-64.617 0-103-30.883-103-95.5C38 111.194-8.729 36.236 8 16 29.46-9.959 88.689 6 125 6c64.617 0 119 52.383 119 117Z"
id="path19" />
</mask>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -18,7 +18,7 @@
maskUnits="userSpaceOnUse"
style="mask-type:alpha">
<path
fill="#D9D9D9"
fill="white"
d="M244 123c0 64.617-38.383 112-103 112-64.617 0-103-30.883-103-95.5C38 111.194-8.729 36.236 8 16 29.46-9.959 88.689 6 125 6c64.617 0 119 52.383 119 117Z"
id="path19" />
</mask>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
public/pwa-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

View File

@ -0,0 +1,208 @@
import { rm, writeFile } from 'node:fs/promises'
import { resolve } from 'pathe'
import type { PngOptions, ResizeOptions } from 'sharp'
import sharp from 'sharp'
import ico from 'sharp-ico'
interface Icon {
sizes: number[]
padding: number
resizeOptions?: ResizeOptions
}
type IconType = 'transparent' | 'maskable' | 'apple'
/**
* PWA Icons definition:
* - transparent: [{ sizes: [192, 512], padding: 0.05, resizeOptions: { fit: 'contain', background: 'transparent' } }]
* - maskable: [{ sizes: [512], padding: 0.3 }, resizeOptions: { fit: 'contain', background: 'white' } }]
* - apple: [{ sizes: [180], padding: 0.3 }, resizeOptions: { fit: 'contain', background: 'white' } }]
*/
interface Icons extends Record<IconType, Icon> {
/**
* @default: { compressionLevel: 9, quality: 60 }`
*/
png?: PngOptions
/**
* @default `pwa-<size>x<size>.png`, `maskable-icon-<size>x<size>.png`, `apple-touch-icon-<size>x<size>.png`
*/
iconName?: (type: IconType, size: number) => string
/**
* Generate `favicon.ico` from transparent icons (from `pwa-<size>x<size>.png` ones)
*/
ico?: {
/**
* @default `favicon-<size>x<size>.ico`
*/
icoName?: (size: number) => string
sizes: number[]
}
}
interface ResolvedIcons extends Required<Omit<Icons, 'ico'>> {
ico?: {
/**
* @default `favicon-<size>x<size>.ico`
*/
icoName?: (size: number) => string
sizes: number[]
}
}
const defaultIcons: Icons = {
transparent: {
sizes: [192, 512],
padding: 0.05,
resizeOptions: {
fit: 'contain',
background: 'transparent',
},
},
maskable: {
sizes: [512],
padding: 0.3,
resizeOptions: {
fit: 'contain',
background: 'white',
},
},
apple: {
sizes: [180],
padding: 0.3,
resizeOptions: {
fit: 'contain',
background: 'white',
},
},
}
const root = process.cwd()
const publicFolders = ['public', 'public-dev', 'public-staging'].map(folder => resolve(root, folder))
async function optimizePng(filePath: string, png: PngOptions) {
await sharp(filePath).png(png).toFile(`${filePath.replace(/-temp\.png$/, '.png')}`)
await rm(filePath)
}
async function generateTransparentIcons(icons: ResolvedIcons, svgLogo: string, folder: string) {
const { sizes, padding, resizeOptions } = icons.transparent
await Promise.all(sizes.map(async (size) => {
const filePath = resolve(folder, icons.iconName('transparent', size))
await sharp({
create: {
width: size,
height: size,
channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 },
},
}).composite([{
input: await sharp(svgLogo)
.resize(
Math.round(size * (1 - padding)),
Math.round(size * (1 - padding)),
resizeOptions,
).toBuffer(),
}]).toFile(filePath)
await optimizePng(filePath, icons.png)
}))
}
async function generateMaskableIcons(type: IconType, icons: ResolvedIcons, svgLogo: string, folder: string) {
const { sizes, padding, resizeOptions } = icons[type]
await Promise.all(sizes.map(async (size) => {
const filePath = resolve(folder, icons.iconName(type, size))
await sharp({
create: {
width: size,
height: size,
channels: 4,
background: resizeOptions?.background ?? 'white',
},
}).composite([{
input: await sharp(svgLogo)
.resize(
Math.round(size * (1 - padding)),
Math.round(size * (1 - padding)),
resizeOptions,
).toBuffer(),
}]).toFile(filePath)
await optimizePng(filePath, icons.png)
}))
}
async function generatePWAIconForEnv(folder: string, icons: ResolvedIcons) {
const svgLogo = resolve(folder, 'logo.svg')
await Promise.all([
generateTransparentIcons(icons, svgLogo, folder),
generateMaskableIcons('maskable', icons, svgLogo, folder),
generateMaskableIcons('apple', icons, svgLogo, folder),
])
if (icons.ico) {
const {
icoName = size => `favicon-${size}x${size}.ico`,
} = icons.ico
await Promise.all(icons.ico.sizes.map(async (size) => {
const png = await sharp(
resolve(folder, icons.iconName('transparent', size).replace(/-temp\.png$/, '.png')),
).toFormat('png').toBuffer()
await writeFile(resolve(folder, icoName(size)), ico.encode([png]))
}))
}
}
async function generatePWAIcons(folders: string[], icons: Icons) {
const {
png = { compressionLevel: 9, quality: 60 },
iconName = (type, size) => {
switch (type) {
case 'transparent':
return `pwa-${size}x${size}.png`
case 'maskable':
return `maskable-icon-${size}x${size}.png`
case 'apple':
return `apple-touch-icon-${size}x${size}.png`
}
},
transparent = { ...defaultIcons.transparent },
maskable = { ...defaultIcons.maskable },
apple = { ...defaultIcons.apple },
ico,
} = icons
if (!transparent.resizeOptions)
transparent.resizeOptions = { ...defaultIcons.transparent.resizeOptions }
if (!maskable.resizeOptions)
maskable.resizeOptions = { ...defaultIcons.maskable.resizeOptions }
if (!apple.resizeOptions)
apple.resizeOptions = { ...defaultIcons.apple.resizeOptions }
await Promise.all(folders.map(folder => generatePWAIconForEnv(folder, {
png,
iconName,
transparent,
maskable,
apple,
ico,
})))
}
console.log('Generating Elk PWA Icons...')
generatePWAIcons(publicFolders, <Icons>{
transparent: { ...defaultIcons.transparent, sizes: [64, 192, 512] },
ico: { sizes: [64], icoName: _ => 'favicon.ico' },
iconName: (type, size) => {
switch (type) {
case 'transparent':
return `pwa-${size}x${size}-temp.png`
case 'maskable':
return 'maskable-icon-temp.png'
case 'apple':
return 'apple-touch-icon-temp.png'
}
},
}).then(() => console.log('Elk PWA Icons generated')).catch(console.error)