-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Expand file tree
/
Copy pathgenerate-hero-artwork.ts
More file actions
61 lines (56 loc) · 2.64 KB
/
Copy pathgenerate-hero-artwork.ts
File metadata and controls
61 lines (56 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* Pre-encodes the homepage painting with the existing Next.js quality=90 settings.
* Run `bun run scripts/generate-hero-artwork.ts` after changing the master.
* Content hashes allow immutable caching without request-time image optimization.
*/
import { createHash } from 'node:crypto'
import { mkdir, readdir, unlink, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { createLogger } from '@sim/logger'
import sharp from 'sharp'
const logger = createLogger('HeroArtwork')
const ROOT = path.resolve(import.meta.dirname, '..')
const PUBLIC_DIR = path.join(ROOT, 'apps/sim/public/landing')
const OUTPUT_DIR = path.join(PUBLIC_DIR, 'hero-artwork')
const MANIFEST = path.join(
ROOT,
'apps/sim/app/(landing)/components/hero/components/hero-platform-stage/hero-artwork.generated.ts'
)
/** Retain the Next.js candidates needed by the desktop-only stage, including retina displays. */
const WIDTHS = [1080, 1200, 1920, 2048, 3840] as const
const QUALITY = 90
const sources: Record<'avif' | 'webp', string[]> = { avif: [], webp: [] }
const generatedFiles = new Set<string>()
let fallback = ''
sharp.concurrency(2)
await mkdir(OUTPUT_DIR, { recursive: true })
for (const format of ['avif', 'webp'] as const) {
for (const width of WIDTHS) {
const image = sharp(path.join(PUBLIC_DIR, 'hero-painted-4k.webp'))
.rotate()
.resize(width, undefined, { withoutEnlargement: true })
const buffer = await (format === 'avif'
? image.avif({ quality: Math.round(QUALITY * (50 / 80)), effort: 3 })
: image.webp({ quality: QUALITY })
).toBuffer()
const hash = createHash('sha256').update(buffer).digest('hex').slice(0, 12)
const filename = `painting-${width}-${hash}.${format}`
await writeFile(path.join(OUTPUT_DIR, filename), buffer)
generatedFiles.add(filename)
const url = `/landing/hero-artwork/${filename}`
sources[format].push(`${url} ${width}w`)
if (format === 'webp') fallback = url
logger.info('Encoded hero artwork', { width, format, bytes: buffer.length })
}
}
await writeFile(
MANIFEST,
`/** Generated by scripts/generate-hero-artwork.ts. */\nexport const HERO_ARTWORK = ${JSON.stringify({ avifSrcSet: sources.avif.join(', '), webpSrcSet: sources.webp.join(', '), src: fallback }, null, 2)} as const\n`
)
const format = Bun.spawn(['bunx', 'biome', 'format', '--write', MANIFEST], { cwd: ROOT })
if ((await format.exited) !== 0) throw new Error('Could not format the hero artwork manifest')
for (const filename of await readdir(OUTPUT_DIR)) {
if (/^painting-\d+-[a-f0-9]{12}\.(avif|webp)$/.test(filename) && !generatedFiles.has(filename)) {
await unlink(path.join(OUTPUT_DIR, filename))
}
}