-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbuild.js
More file actions
102 lines (91 loc) · 4.28 KB
/
Copy pathbuild.js
File metadata and controls
102 lines (91 loc) · 4.28 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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env bun
/* global Bun */
// Builds the static site into ./dist for GitHub Pages. Like the Quotes and
// Opening Hours apps this is a plain static bundle — no server. The timer has no
// dataset: the title and target arrive entirely in the launch URL's query string
// (see the manifest at .well-known/signage-app.json), and the clock ticks live in
// the browser. Steps:
// 1. vendor fonts from @fontsource (sync-fonts.js)
// 2. assemble dist/ (index.html + static assets + .well-known, copied not mutated)
// 3. compile Tailwind v4 CSS (minified)
// 4. bundle TypeScript → browser JS (minified, ./timer inlined)
// 5. stamp a content hash into asset URLs (?v=) for cache-busting
// 6. write CNAME for the custom domain
// dist/ is gitignored; CI uploads it as the Pages artifact.
import { rm, mkdir, cp, readFile, writeFile } from 'node:fs/promises'
import { bundleJs, injectGate, processCss } from '@screenly-labs/signage-kit/build'
import { run as syncFonts } from './sync-fonts.js'
const DIST = 'dist'
const DOMAIN = 'timer.srly.io'
// The degraded-mode support floor, the CSS down-leveling recipe (cascade-layers
// flatten + Lightning CSS), the JS bundler, and the inline degraded-mode gate all
// come from @screenly-labs/signage-kit. This file only orchestrates the
// app-specific steps.
// 1. Vendor the Bun-managed webfonts into ./assets before copying.
await syncFonts()
// 2. Fresh dist/, then copy the web root (everything served at /static/...), the
// page shell, and the signage manifest served at the well-known path. Sources
// are never minified in place.
await rm(DIST, { recursive: true, force: true })
await mkdir(`${DIST}/static`, { recursive: true })
// Create the output subdirs up front so Tailwind/esbuild never race an absent dir.
await mkdir(`${DIST}/static/styles`, { recursive: true })
await mkdir(`${DIST}/static/js`, { recursive: true })
await cp('assets/static/fonts', `${DIST}/static/fonts`, { recursive: true })
await cp('assets/static/images', `${DIST}/static/images`, { recursive: true })
// Copy the page shell with the shared degraded-mode gate injected before the
// stylesheet so it runs before first paint.
await writeFile(`${DIST}/index.html`, injectGate(await readFile('index.html', 'utf8')))
// Signage app manifest served at /.well-known/signage-app.json (see the
// app-store's docs/app-manifest.md). GitHub Pages returns it as application/json
// with Access-Control-Allow-Origin: * so the store and players can fetch it.
await cp('.well-known', `${DIST}/.well-known`, { recursive: true })
// 3. Tailwind -> the kit's CSS pipeline (flatten @layer, down-level to the floor).
const cssOut = `${DIST}/static/styles/main.css`
const tailwind = Bun.spawn(
[
'node_modules/.bin/tailwindcss',
'--input',
'assets/static/styles/tailwind.css',
'--output',
cssOut
],
{ stdout: 'inherit', stderr: 'inherit' }
)
if ((await tailwind.exited) !== 0) {
console.error('✗ Tailwind build failed')
process.exit(1)
}
try {
await writeFile(cssOut, await processCss(await readFile(cssOut, 'utf8'), { flattenLayers: true, filename: cssOut }))
} catch (error) {
console.error(`✗ CSS build failed (${cssOut})`)
console.error(error)
process.exit(1)
}
console.log(`✓ CSS: ${cssOut}`)
// 4. Client TS -> the kit's bundler (self-contained IIFE at the floor's syntax level).
try {
await bundleJs('assets/static/js/main.ts', `${DIST}/static/js/main.js`)
} catch (error) {
console.error('✗ JS build failed')
console.error(error)
process.exit(1)
}
console.log(`✓ JS: ${DIST}/static/js/main.js`)
// 5. Cache-busting: hash the built JS + CSS so the token changes exactly when
// shipped code changes, then stamp it into the page's asset URLs.
const fingerprint = await Promise.all([
readFile(`${DIST}/static/js/main.js`),
readFile(`${DIST}/static/styles/main.css`)
])
const hasher = new Bun.CryptoHasher('sha256')
for (const buf of fingerprint) hasher.update(buf)
const version = hasher.digest('hex').slice(0, 10)
const html = await readFile(`${DIST}/index.html`, 'utf8')
await writeFile(`${DIST}/index.html`, html.replaceAll('__ASSET_VERSION__', version))
console.log(`✓ Stamped asset version ${version}`)
// 6. Custom domain for GitHub Pages.
await writeFile(`${DIST}/CNAME`, `${DOMAIN}\n`)
console.log(`✓ CNAME: ${DOMAIN}`)
console.log('Build complete → dist/')