diff --git a/README.md b/README.md index 35c5c3616..5fd71da55 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,36 @@ +# personal_site + +This repository is a React-based personal website for Gregory Campbell. + +Quick commands + +```bash +# install deps (use legacy peer deps if you hit TypeScript/react-scripts conflicts) +npm install --legacy-peer-deps + +# run dev server +npm start + +# build production +npm run build + +# optimize images (requires sharp) +# install sharp (one-time): +npm run optimize-images:install +# then run optimization: +npm run optimize-images +``` + +Image optimization + +This repo includes `scripts/optimize-images.js`, a small Node script that uses `sharp` to generate WebP resized variants for images found in `src/assets`. The script writes outputs to `public/images` by default. + +If you prefer a different pipeline, consider using `imagemin` or a CI-integrated image optimization step. + +Deployment / Amplify notes + +- The project uses `package-lock.json` (npm) — ensure CI is configured to use `npm install` and not `yarn`. +- If deploying via AWS Amplify and it errors with multiple lockfiles, ensure only one lockfile (package-lock.json) is present. [![Netlify Status](https://api.netlify.com/api/v1/badges/b9a5df97-64f4-4888-8d76-548e77e029ae/deploy-status)](https://app.netlify.com/projects/gregoryjcampbell/deploys) This is the source code for my [Personal Website](https://gregoryjcampbell.netlify.app/). diff --git a/package.json b/package.json index aa17ef965..5df65566c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "optimize-images": "node scripts/optimize-images.js", + "optimize-images:install": "npm install --save-dev sharp --legacy-peer-deps" }, "eslintConfig": { "extends": "react-app" diff --git a/scripts/optimize-images.js b/scripts/optimize-images.js new file mode 100644 index 000000000..344530fb6 --- /dev/null +++ b/scripts/optimize-images.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +async function main() { + let sharp; + try { + sharp = require('sharp'); + } catch (e) { + console.error('\nError: the package "sharp" is not installed.'); + console.error('Install it with: npm install --save-dev sharp'); + console.error('Or run: npm run optimize-images:install to install prerequisites.\n'); + process.exit(1); + } + + const assetsDir = path.join(__dirname, '..', 'src', 'assets'); + const outDir = path.join(__dirname, '..', 'public', 'images'); + if (!fs.existsSync(assetsDir)) { + console.error('No assets directory found at', assetsDir); + process.exit(1); + } + if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true }); + + const files = fs.readdirSync(assetsDir).filter((f) => /\.(jpe?g|png)$/i.test(f)); + if (!files.length) { + console.log('No PNG/JPG assets found to optimize.'); + return; + } + + console.log(`Optimizing ${files.length} images to ${outDir} ...`); + const sizes = [320, 640, 1024]; + + for (const file of files) { + const input = path.join(assetsDir, file); + const name = path.parse(file).name; + + for (const w of sizes) { + const outPath = path.join(outDir, `${name}-${w}.webp`); + try { + await sharp(input) + .resize({ width: w }) + .webp({ quality: 80 }) + .toFile(outPath); + console.log('wrote', outPath); + } catch (err) { + console.error('failed to process', input, err.message); + } + } + } + + console.log('\nImage optimization complete. Copy generated files into your build or update components to reference them.'); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +});