← back to Fentucci Naturals
scripts/image-audit.js
47 lines
#!/usr/bin/env node
// WallQuest image-collapse guard: per PATTERN, compare distinct image checksums vs colorway count.
// Flags any pattern where colorways share one image file. Also verifies every file real & >5KB.
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const ROOT = path.join(__dirname, '..');
const products = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', 'products.json')));
const byPattern = new Map();
let missing = [], small = [];
for (const p of products) {
const f = path.join(ROOT, p.image_local);
let hash = null;
if (!fs.existsSync(f)) missing.push(p.mfr_sku);
else if (fs.statSync(f).size <= 5120) small.push(p.mfr_sku);
else hash = crypto.createHash('md5').update(fs.readFileSync(f)).digest('hex');
const arr = byPattern.get(p.pattern_key) || [];
arr.push({ sku: p.mfr_sku, hash });
byPattern.set(p.pattern_key, arr);
}
const flagged = [];
for (const [key, arr] of byPattern) {
const withImg = arr.filter(a => a.hash);
const distinct = new Set(withImg.map(a => a.hash)).size;
if (withImg.length > 1 && distinct < withImg.length) {
// find which skus share
const seen = new Map();
const dups = [];
for (const a of withImg) { if (seen.has(a.hash)) dups.push(`${a.sku}=${seen.get(a.hash)}`); else seen.set(a.hash, a.sku); }
flagged.push({ pattern: key, colorways: withImg.length, distinct_images: distinct, shared: dups });
}
}
const report = {
total: products.length,
with_image: products.length - missing.length - small.length,
missing, small,
coverage_pct: +(100 * (products.length - missing.length - small.length) / products.length).toFixed(1),
shared_image_patterns: flagged,
};
fs.writeFileSync(path.join(ROOT, 'data', 'image-audit.json'), JSON.stringify(report, null, 2));
console.log(JSON.stringify({ ...report, missing: missing.length, small: small.length }, null, 1));
if (flagged.length) console.log('FLAGGED patterns:', flagged.map(f => f.pattern).join(', '));