From 3a1dd1008f514d17e7d4a3fc941b2ca3f4d00ff1 Mon Sep 17 00:00:00 2001 From: Wilfred007 Date: Mon, 29 Jun 2026 17:24:05 +0100 Subject: [PATCH] feat(perf): optimize image loading with WebP conversion and next/image sizing --- assets/avatar.webp | Bin 0 -> 940 bytes assets/example.webp | Bin 0 -> 6210 bytes components/atoms/author-info/index.tsx | 2 +- components/organisms/campaign/index.tsx | 13 +- next.config.js | 7 + package-lock.json | 612 +++++++++++++++++++++++- package.json | 4 +- scripts/convert-to-webp.js | 48 ++ 8 files changed, 657 insertions(+), 29 deletions(-) create mode 100644 assets/avatar.webp create mode 100644 assets/example.webp create mode 100644 scripts/convert-to-webp.js diff --git a/assets/avatar.webp b/assets/avatar.webp new file mode 100644 index 0000000000000000000000000000000000000000..36917229e9e0f5877781129233d79a2e7fda89ff GIT binary patch literal 940 zcmV;d15^A`Nk&Gb0{{S5MM6+kP&il$0000G0000l001ul06|PpNM8W}00Dqi+iu27 zWJMk<4d^9PQLrHs2FZV}O_nLwv}nmy|Lv2D&5?^nHh8{4+3b^xZP)cEVU1Wqj*5iq zao5F z0Zp^44RN1x{25N!YKCHb1{oPf#nqOf;d;A99MZLhyLDW-dl-WIY;SbNqs==%L47nl z&6}^~PWf3QyvWjRp9%vYuG2cS5#DB5)ZBg8z6jwy09H^qARYn$01yrUodGH^05AYP zO(2X!BBCJ|90xEc1jM!gVUs{PBX*O#+_`11`;5|q`KsSJXpnjF`fPX;$0oF_C z0s5=y8T*a!EBl%0AkhVIx0(GmF^}enaetY;#vDwq?#gvJa42*Z4!z7UPbr#ZjzS;I zn0wg6#DD<){MSwurQECfmf1kWdg9~mI#7Hp-yfvAU8!cu)AcvQQRUD4C*ANL+1;dv zc(?-6oaj^ah$l=D8?O~r)<@=(vDPqd+;q@1 zcS?;=1H9z!*=OFAkL{4;P&m~k&c9@2ve!I+ICuxc8RZafQzGl%S-UIiZ&6mX5>U%y-O zn^80LqxUDgyuKD36)S;Re1N>HEa{NQyei=2a$I3DcsvoyMg0eQ3_9QwOV_U<(`n4G zJw_H^F)e<|-m{d559P2&vcd^+cj+${msqGFh%Rg=(rE`;>%ko%))udM+w`vEAGR)^ zCd>28iF8ct0L$WVl>O7xDFbXH$ivHwm?Pb_OQ=pWk_cBqx#W%B{5vi`)$5LqKjgti z{*IG8Lq(1rCK@}c!(vU0z6r7t=J6E}bmLdA;n>UKuyK&f=tAdxH|X7ws9#=pgQF4Q Ow5gq;aS+bEKmY(mPQ-x# literal 0 HcmV?d00001 diff --git a/assets/example.webp b/assets/example.webp new file mode 100644 index 0000000000000000000000000000000000000000..930ec31eddc89599287926604bf5bb23a010697a GIT binary patch literal 6210 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 - avatar + avatar
{dateTime}
diff --git a/components/organisms/campaign/index.tsx b/components/organisms/campaign/index.tsx index 3a87451..d04ec6b 100644 --- a/components/organisms/campaign/index.tsx +++ b/components/organisms/campaign/index.tsx @@ -1,9 +1,9 @@ import React from 'react' import styles from './style.module.css' import Image from 'next/image' -import picture from '../../../assets/example.png' +import picture from '../../../assets/example.webp' import { AuthorInfo } from '../../atoms' -import avatar from '../../../assets/avatar.png' +import avatar from '../../../assets/avatar.webp' export function Campaign() { return ( @@ -11,7 +11,14 @@ export function Campaign() {
Starfund #821

Planetary open-source photography

- project image + project image

Hello! My name is Sam and I’m working with a team of three other passionate individuals to take photos of the planets in our solar system diff --git a/next.config.js b/next.config.js index 5f3f217..da05b02 100644 --- a/next.config.js +++ b/next.config.js @@ -13,6 +13,13 @@ const nextConfig = { locales: ['en', 'es'], defaultLocale: 'en', }, + images: { + // Serve WebP (and AVIF where supported) instead of the original format. + formats: ['image/avif', 'image/webp'], + // Breakpoints used when generating responsive srcsets. + deviceSizes: [640, 750, 828, 1080, 1200, 1920], + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + }, }; setupDevPlatform(); diff --git a/package-lock.json b/package-lock.json index 83ab38d..6300965 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "eslint": "^8.14.0", "eslint-config-next": "^13.3.1", "postcss": "^8.5.14", + "sharp": "^0.35.2", "tailwindcss": "^3.4.19", "typescript": "5.1.3" }, @@ -149,6 +150,17 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -280,6 +292,533 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", + "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.1" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", + "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.1" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", + "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", + "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", + "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", + "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", + "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", + "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", + "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", + "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", + "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", + "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", + "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", + "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.1" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", + "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.1" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", + "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.1" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", + "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.1" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", + "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.1" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", + "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.1" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", + "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", + "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.1" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", + "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", + "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", + "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", + "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", + "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -4048,18 +4587,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5370,13 +5897,11 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -5396,6 +5921,51 @@ "sha.js": "bin.js" } }, + "node_modules/sharp": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", + "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.2", + "@img/sharp-darwin-x64": "0.35.2", + "@img/sharp-freebsd-wasm32": "0.35.2", + "@img/sharp-libvips-darwin-arm64": "1.3.1", + "@img/sharp-libvips-darwin-x64": "1.3.1", + "@img/sharp-libvips-linux-arm": "1.3.1", + "@img/sharp-libvips-linux-arm64": "1.3.1", + "@img/sharp-libvips-linux-ppc64": "1.3.1", + "@img/sharp-libvips-linux-riscv64": "1.3.1", + "@img/sharp-libvips-linux-s390x": "1.3.1", + "@img/sharp-libvips-linux-x64": "1.3.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", + "@img/sharp-libvips-linuxmusl-x64": "1.3.1", + "@img/sharp-linux-arm": "0.35.2", + "@img/sharp-linux-arm64": "0.35.2", + "@img/sharp-linux-ppc64": "0.35.2", + "@img/sharp-linux-riscv64": "0.35.2", + "@img/sharp-linux-s390x": "0.35.2", + "@img/sharp-linux-x64": "0.35.2", + "@img/sharp-linuxmusl-arm64": "0.35.2", + "@img/sharp-linuxmusl-x64": "0.35.2", + "@img/sharp-webcontainers-wasm32": "0.35.2", + "@img/sharp-win32-arm64": "0.35.2", + "@img/sharp-win32-ia32": "0.35.2", + "@img/sharp-win32-x64": "0.35.2" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6204,12 +6774,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 0831a83..8c10d07 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "build:contracts": "./target/bin/soroban contract build", "bindings:crowdfund": "./target/bin/soroban contract bindings typescript --wasm ./target/wasm32-unknown-unknown/release/soroban_crowdfund_contract.wasm --id $(cat ./.soroban-example-dapp/crowdfund_id) --output-dir ./.soroban-example-dapp/crowdfund-contract --network $(cat ./.soroban-example-dapp/network) --overwrite", "bindings:abundance": "./target/bin/soroban contract bindings typescript --wasm ./target/wasm32-unknown-unknown/release/abundance_token.wasm --id $(cat ./.soroban-example-dapp/abundance_token_id) --output-dir ./.soroban-example-dapp/abundance-token --network $(cat ./.soroban-example-dapp/network) --overwrite", - "bindings": "npm run bindings:crowdfund && npm run bindings:abundance" + "bindings": "npm run bindings:crowdfund && npm run bindings:abundance", + "convert:assets": "node scripts/convert-to-webp.js" }, "dependencies": { "@hookform/resolvers": "^5.4.0", @@ -41,6 +42,7 @@ "eslint": "^8.14.0", "eslint-config-next": "^13.3.1", "postcss": "^8.5.14", + "sharp": "^0.35.2", "tailwindcss": "^3.4.19", "typescript": "5.1.3" }, diff --git a/scripts/convert-to-webp.js b/scripts/convert-to-webp.js new file mode 100644 index 0000000..037681c --- /dev/null +++ b/scripts/convert-to-webp.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node +/** + * Converts PNG/JPG assets in the assets/ directory to WebP. + * Run with: node scripts/convert-to-webp.js + */ + +const sharp = require('sharp') +const path = require('path') +const fs = require('fs') + +const ASSETS_DIR = path.join(__dirname, '..', 'assets') +const SUPPORTED_EXTS = ['.png', '.jpg', '.jpeg'] + +async function convertToWebP(filePath) { + const parsed = path.parse(filePath) + const outPath = path.join(parsed.dir, parsed.name + '.webp') + + await sharp(filePath).webp({ quality: 85 }).toFile(outPath) + + const srcSize = fs.statSync(filePath).size + const dstSize = fs.statSync(outPath).size + const savings = (((srcSize - dstSize) / srcSize) * 100).toFixed(1) + + console.log( + ` ${parsed.base} → ${parsed.name}.webp (${(srcSize / 1024).toFixed(1)} KB → ${(dstSize / 1024).toFixed(1)} KB, ${savings}% smaller)` + ) +} + +async function main() { + const files = fs + .readdirSync(ASSETS_DIR) + .filter(f => SUPPORTED_EXTS.includes(path.extname(f).toLowerCase())) + .map(f => path.join(ASSETS_DIR, f)) + + if (files.length === 0) { + console.log('No PNG/JPG files found in assets/') + return + } + + console.log(`Converting ${files.length} file(s) to WebP…`) + await Promise.all(files.map(convertToWebP)) + console.log('Done.') +} + +main().catch(err => { + console.error(err) + process.exit(1) +})