- 매니페스트·아이콘(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>
38 lines
1.3 KiB
TypeScript
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,
|
|
},
|
|
});
|