#!/usr/bin/env bun import { readdirSync, writeFileSync } from 'fs' import { join } from 'path' import { ROOT, readJson, writeJson } from './lib/intel-utils' const assetRoot = process.env.ASSET_ROOT || join(ROOT, '..', '..', 'jv-assets') const contentRoot = join(ROOT, 'data', 'content') const productsRoot = join(contentRoot, 'products') const outRoot = join(ROOT, 'content_population_exports') function walk(dir: string): { name: string; path: string }[] { try { return readdirSync(dir, { withFileTypes: true }).flatMap(entry => { const path = join(dir, entry.name) if (entry.isDirectory()) return walk(path) if (entry.isFile()) return [{ name: entry.name, path }] return [] }) } catch (_) { return [] } } function csv(rows: any[], headers: string[]) { const esc = (v: any) => { v = v ?? '' v = String(v) return /[",\n\r]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v } return [headers.join(','), ...rows.map(r => headers.map(h => esc(r[h])).join(','))].join('\n') + '\n' } const files = walk(assetRoot) const available = new Map(files.map(f => [f.name, f.path])) const productFiles = readdirSync(productsRoot).filter(f => f.endsWith('.json')) const required: any[] = [] const missingReferences: any[] = [] for (const file of productFiles) { const product = readJson(join(productsRoot, file), null) for (const field of ['pdpResults', 'pdpResultsMobile', 'pdpHowWeCompare']) { const filename = product?.metafields?.[field] if (filename) required.push({ Kind: 'product', Owner: product.sku, Field: field, Filename: filename, Exists: available.has(filename) ? 'yes' : 'no', Path: available.get(filename) || '' }) else missingReferences.push({ Kind: 'product', Owner: product.sku, Field: field, Filename: '', Exists: 'no', Path: 'No filename assigned' }) } } const meta = readJson(join(contentRoot, 'metaobjects.json'), { definitions: {} }) for (const [definition, rows] of Object.entries(meta.definitions || {}) as any) { for (const row of rows) { for (const field of ['icon', 'image', 'background_image']) { const filename = row[field] if (filename) required.push({ Kind: 'metaobject', Owner: `${definition}.${row.handle || row.name || ''}`, Field: field, Filename: filename, Exists: available.has(filename) ? 'yes' : 'no', Path: available.get(filename) || '' }) } } } const allRows = [...required, ...missingReferences] writeFileSync(join(outRoot, 'asset_manifest_validation.csv'), csv(allRows, ['Kind', 'Owner', 'Field', 'Filename', 'Exists', 'Path']), 'utf8') writeJson(join(outRoot, 'asset_manifest_validation.json'), { generatedAt: new Date().toISOString(), assetRoot, availableCount: files.length, requiredCount: required.length, missingUploadCount: required.filter(r => r.Exists === 'no').length, missingReferenceCount: missingReferences.length, ready: required.every(r => r.Exists === 'yes') && missingReferences.length === 0, required, missingReferences }) console.log(`Available assets: ${files.length}`) console.log(`Required filenames: ${required.length}`) console.log(`Missing uploads: ${required.filter(r => r.Exists === 'no').length}`) console.log(`Missing filename refs: ${missingReferences.length}`)