// Generate web/static-assets.js — the browser scripts, as a MODULE.
//
// ⛔ WHY THIS EXISTS. lib/static.js serves web/static/ by reading the FILESYSTEM relative to
// import.meta.url. That works on a server and cannot work inside a bundle: bare-pack packs the JS module
// GRAPH, and these files are imported by nothing, so they are simply absent from favela-android's
// worklet bundle. Measured on the handset 2026-08-18 — /static/compose.js and /static/actions.js both
// 404, so image attach, like, repost, follow, drafts and reporting had never worked on the phone at all.
// The markup arrived "for free" from favela-web; the behaviour silently did not.
//
// Making them a module puts them IN the graph, so they travel with every surface that imports favela-web
// without a second distribution mechanism, extra copy step, or --offload-assets files to keep beside the
// bundle on Android.
//
// The output is COMMITTED, not built on demand: favela-web's CI is `npm install` → vendor siblings →
// `pgh publish .`, with no codegen step, and adding one would mean the published tree could differ from
// the reviewed one. test/static-assets.test.mjs fails if this file drifts from web/static/, so a stale
// copy is a red test rather than a silent 404 on a phone.
//
// Run: npm run gen:static
import { readdirSync, statSync, readFileSync, writeFileSync } from 'node:fs'
import { join, relative, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
const STATIC_DIR = join(ROOT, 'web', 'static')
const OUT = join(ROOT, 'web', 'static-assets.js')
// Same allowlist lib/static.js serves. Anything else could not be fetched anyway, so bundling it would
// be dead weight in every app that embeds favela-web.
const ALLOWED = new Set(['.js', '.css', '.json', '.map'])
export function collect (dir = STATIC_DIR) {
const out = []
const walk = (d) => {
for (const name of readdirSync(d).sort()) {
const abs = join(d, name)
if (statSync(abs).isDirectory()) { walk(abs); continue }
const dot = name.lastIndexOf('.')
if (dot < 0 || !ALLOWED.has(name.slice(dot))) continue
// POSIX separators: this key is compared against a URL path, not a filesystem path.
out.push([relative(dir, abs).split('\\').join('/'), readFileSync(abs, 'utf8')])
}
}
walk(dir)
return out
}
function render (entries) {
const body = entries.map(([k, v]) => ` ${JSON.stringify(k)}: ${JSON.stringify(v)}`).join(',\n')
return `// GENERATED by scripts/gen-static-assets.mjs — DO NOT EDIT.
//
// web/static/ as a module, so the browser scripts survive bundling. lib/static.js reads the filesystem
// first (unchanged for servers) and falls back to this map, which is the only copy that exists inside
// favela-android's worklet bundle. Regenerate with \`npm run gen:static\`; test/static-assets.test.mjs
// fails if this drifts from web/static/.
export const STATIC_ASSETS = {
${body}
}
`
}
const entries = collect()
writeFileSync(OUT, render(entries))
const bytes = entries.reduce((n, [, v]) => n + Buffer.byteLength(v), 0)
console.log(`web/static-assets.js — ${entries.length} files, ${bytes} bytes of source`)