// Re-point every asset reference at the site's origin, and refuse to emit anything that // would 404. A silently-missing image is precisely how the page goes back to lying about // its own content — which is the failure this whole projection exists to end. import { writeFile, mkdir, copyFile } from 'node:fs/promises' import { existsSync } from 'node:fs' import { basename, join } from 'node:path' import { DIST, postcss, publicBase, publicDir } from './paths.mjs' export async function projectAssets(html, css, id, { apply = true } = {}) { const base = `${publicBase(id)}/assets` const wanted = new Map() const unresolved = [] const claim = (ref) => { const clean = ref.trim().replace(/^["']|["']$/g, '') if (/^(data:|https?:|#|mailto:)/i.test(clean)) return null // external / inline: not ours if (!clean.startsWith('/')) { unresolved.push({ ref: clean, why: 'referencia relativa, sin base' }); return null } const onDisk = join(DIST, clean) if (!existsSync(onDisk)) { unresolved.push({ ref: clean, why: `no está en dist/` }); return null } const pub = `${base}/${basename(clean)}` wanted.set(clean, { onDisk, pub }) return pub } html = html .replace(/(]+src=")([^"]+)(")/g, (m, a, ref, z) => { const p = claim(ref); return p ? a + p + z : m }) .replace(/(background-image:\s*url\()([^)]+)(\))/g, (m, a, ref, z) => { const p = claim(ref); return p ? a + p + z : m }) const root = postcss.parse(css) root.walkDecls(d => { if (!d.value.includes('url(')) return d.value = d.value.replace(/url\(([^)]+)\)/g, (m, ref) => { const p = claim(ref) return p ? `url("${p}")` : m }) }) css = root.toString() if (unresolved.length) { const lines = unresolved.map(u => ` ${u.ref} — ${u.why}`).join('\n') throw new Error(`${unresolved.length} referencia(s) sin resolver; no se emite un deck con assets rotos:\n${lines}`) } if (apply) { const dest = join(publicDir(id), 'assets') await mkdir(dest, { recursive: true }) for (const [, v] of wanted) await copyFile(v.onDisk, join(dest, basename(v.pub))) } return { html, css, copied: [...wanted.values()].map(v => v.pub) } }