watchparty/client/build.mjs
Nicolas 75a9fef18a
Some checks failed
Publier l'image Docker / test (pull_request) Failing after 4s
Publier l'image Docker / publish (pull_request) Has been skipped
Make the app installable as a PWA
Manifest + hand-authored icons (any + maskable) + iOS meta tags, so it
can be added to a phone's home screen and opened without browser
chrome. No service worker - explored as a possible route to automatic
Picture-in-Picture on backgrounding, but confirmed (by actually calling
requestPictureInPicture() from a real visibilitychange listener) that
install state doesn't change that API's fresh-gesture requirement, so
this is a standalone feature rather than a fix for that limitation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 14:07:28 +02:00

52 lines
2.2 KiB
JavaScript

// Client build: TypeScript (+ per-component .html/.css) -> client/dist,
// via esbuild. Kept as a small hand-written script rather than a config
// file for a heavier tool (Vite, webpack) - this app's whole client is a
// couple thousand lines across a dozen files, esbuild's own bundling
// (including its built-in "collect CSS imported from JS into a sibling
// .css file" behavior) is all it needs, consistent with this project's
// general bias against dependencies it doesn't need.
//
// Usage: node build.mjs [--watch]
// npm run build -> one-off build (what server/Dockerfile's build stage runs)
// npm run watch -> rebuilds on save, for local dev (see README's
// Running locally section)
import * as esbuild from 'esbuild';
import { copyFileSync, cpSync, mkdirSync } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const here = path.dirname(fileURLToPath(import.meta.url));
const outdir = path.join(here, 'dist');
mkdirSync(outdir, { recursive: true });
copyFileSync(path.join(here, 'src', 'index.html'), path.join(outdir, 'index.html'));
// PWA manifest + icons (manifest.webmanifest, icons/*) - see
// docs/architecture.md's "Progressive Web App" section. Copied wholesale
// rather than named file-by-file like index.html above, so adding another
// icon later needs no build.mjs change.
cpSync(path.join(here, 'src', 'public'), outdir, { recursive: true });
const buildOptions = {
entryPoints: [path.join(here, 'src', 'app.ts')],
outfile: path.join(outdir, 'app.js'),
bundle: true,
format: 'esm',
target: 'es2020',
sourcemap: true,
// Each component imports its own template as a raw string (`import html
// from './Foo.html'`) - see global.d.ts. CSS imports need no loader
// entry: esbuild bundles any CSS reached from a JS/TS entry point into a
// sibling app.css automatically.
loader: { '.html': 'text' }
};
const watch = process.argv.includes('--watch');
if (watch) {
const ctx = await esbuild.context(buildOptions);
await ctx.watch();
console.log('[watchparty-client] watching for changes...');
} else {
await esbuild.build(buildOptions);
console.log('[watchparty-client] built to client/dist');
}