/* art-optimizer-scripts Contains two Node scripts to prepare optimized images and to rewrite your HTML into an "optimized" version that uses + webp srcsets and lazy-loading. Usage (in project folder that contains art.html and your original image folder): 1. Put your original images in a folder named `PHOTOS` next to these scripts. 2. Install dependencies: npm install sharp 3. Run: node make-images.js # generates PHOTOS/-320.webp, -640.webp, ... 4. Run: node make-html.js # reads art.html and writes art-optimized.html Notes: - These scripts are conservative text transforms. Always keep backups. - After generation, upload the generated webp files + art-optimized.html to Neocities. */ // ---------- make-images.js ---------- // Generates resized webp versions for each image in the PHOTOS folder. const fs = require('fs'); const path = require('path'); const sharp = require('sharp'); const inputDir = path.join(__dirname, 'PHOTOS'); // change if your folder name differs const widths = [320, 640, 1024, 1600]; const webpQuality = 80; if (!fs.existsSync(inputDir)) { console.error('Folder "PHOTOS" not found. ضع صورك هناك ثم شغّل السكربت.'); process.exit(1); } const files = fs.readdirSync(inputDir); async function processFile(file) { const ext = path.extname(file).toLowerCase(); const name = path.basename(file, ext); // skip already-generated webp variants if (/-\d+\.webp$/.test(file)) return; if (!['.jpg', '.jpeg', '.png', '.gif', '.webp'].includes(ext)) return; const inputPath = path.join(inputDir, file); for (const w of widths) { const outWebp = path.join(inputDir, `${name}-${w}.webp`); try { await sharp(inputPath) .resize({ width: w }) .webp({ quality: webpQuality }) .toFile(outWebp); console.log(`Created ${path.basename(outWebp)}`); } catch (err) { console.error('Error processing', file, err); } } } (async () => { for (const f of files) { await processFile(f); } console.log('All done.'); })(); // ---------- make-html.js ---------- // Rewrites
...
blocks into blocks // that reference the generated webp files inside the PHOTOS folder. const fs2 = require('fs'); const path2 = require('path'); const inputHtmlFiles = ['art.html', 'photos.html'].map(f => path2.join(__dirname, f)); const outputSuffix = '-optimized.html'; const imgFolder = 'PHOTOS'; // same folder where make-images.js put webp files const existingInputs = inputHtmlFiles.filter(f => fs2.existsSync(f)); if (existingInputs.length === 0) { console.error('No art.html or photos.html found in this folder.'); process.exit(1); } // This regex matches simple
... ... ...
const imgRegex = /
\s*]*?)src=["']([^"']+)["']([^>]*?)alt=["']([^"']*)["']([^>]*)>\s*<\/figure>/gi; function optimizeHtml(html) { return html.replace(imgRegex, (m, beforeSrc, src, betweenAltAndRest, altText, afterAlt) => { // strip query string and anchors const srcUnescaped = src.split('?')[0].split('#')[0]; const base = srcUnescaped.split('/').pop(); const ext = path2.extname(base); const name = base.replace(ext, ''); // if we couldn't determine a sensible base, keep original if (!name) return m; const widths = [320, 640, 1024, 1600]; const webpSrcset = widths.map(w => `${imgFolder}/${encodeURIComponent(name)}-${w}.webp ${w}w`).join(', '); const sizes = '(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px'; const dataFull = src; // original URL as full-res fallback const picture = `
${altText}
`; return picture; }); } for (const inputFile of existingInputs) { const htmlIn = fs2.readFileSync(inputFile, 'utf8'); const optimized = optimizeHtml(htmlIn); const out = inputFile.replace(/\.html$/, outputSuffix); fs2.writeFileSync(out, optimized, 'utf8'); console.log('Created ' + path2.basename(out)); }