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>
52 lines
2.2 KiB
JavaScript
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');
|
|
}
|