Files
joke-app/apps/web/vite.config.ts
EJClaw 2ed1a7ed33 M9: PWA — 홈 화면에 추가, 오프라인 안내
- 매니페스트·아이콘(192/512/maskable/apple-touch, scripts/pwa-icons.ts로 다시 만들기)
- 손으로 쓴 서비스 워커(apps/web/sw.js): 빌드 때 Vite 플러그인이 빌드 ID를 넣음.
  페이지는 네트워크만(오프라인이면 offline.html), /assets/*만 캐시 먼저(200개까지),
  /api·/ws·/healthz·/internal은 손대지 않음
- 오프라인 안내 페이지, 방 밖에서도 "인터넷 연결이 없어요" 띠
- 홈 아래 "홈 화면에 추가" 카드(설치 버튼, iOS는 공유 메뉴 안내, 닫으면 기억)
- 테스트: apps/web/src/sw.test.ts, e2e/pwa.e2e.ts(E2E_PORT), a11y.e2e.ts도 E2E_PORT 읽기
- 문서: 07 §11, 11 배포(no-cache 대상), README 확인 목록

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:12:23 +09:00

38 lines
1.3 KiB
TypeScript

import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { defineConfig, type Plugin } from 'vite';
import react from '@vitejs/plugin-react';
/** Emits dist/sw.js with a build id, so a deploy that changes files installs a new worker (docs/07 §11). */
function serviceWorker(): Plugin {
return {
name: 'bg-service-worker',
apply: 'build',
generateBundle(_, bundle) {
const src = readFileSync(new URL('./sw.js', import.meta.url), 'utf8');
const offline = readFileSync(new URL('./public/offline.html', import.meta.url), 'utf8');
const files = Object.keys(bundle).sort().join('\n');
const build = createHash('sha256').update(src).update(offline).update(files).digest('hex').slice(0, 12);
if (!src.includes("'__BUILD__'")) throw new Error('sw.js: VERSION placeholder missing');
this.emitFile({ type: 'asset', fileName: 'sw.js', source: src.replace("'__BUILD__'", `'${build}'`) });
},
};
}
export default defineConfig({
plugins: [react(), serviceWorker()],
server: {
port: 5173,
proxy: {
'/api': 'http://localhost:3000',
'/healthz': 'http://localhost:3000',
'/ws': { target: 'ws://localhost:3000', ws: true },
},
},
build: {
target: 'es2022',
cssCodeSplit: true,
sourcemap: false,
},
});