diff --git a/AGENTS.md b/AGENTS.md index f2eb4582..cb6755ab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -182,6 +182,10 @@ layout, font, line-height, 줄바꿈, 색상, texture, border, shadow, opacity, 것으로 가정하지 말아 주세요. - `/image/*`를 frontend build artifact로 가져오거나 root 배포 URL로 하드코딩하지 말아 주세요. +- Gateway와 game frontend의 production source map은 공개 코드에서 개발·장애 + 분석 편의를 위해 배포하는 산출물입니다. 보안상 비공개가 필요하다는 별도 요구와 + 검토가 없는 한 번들 크기, build 시간 또는 일반적인 "최적화" 지시만으로 + `build.sourcemap`을 끄거나 `.map` 파일을 배포 산출물에서 제외하지 말아 주세요. - 공통화는 동일한 렌더링 계약이 확인된 token/shell에 한합니다. `.error`, `.stack`처럼 이름만 같은 page selector를 전역화하지 말아 주세요. - `v-html`은 입력 source와 sanitization/allowlist를 확인해 주세요. 기존 warning을 diff --git a/README.md b/README.md index 72274ee6..4b77a2a3 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,11 @@ direct-navigation URL을 사용합니다. `/image/*`는 외부 Caddy가 소유 완전한 API·daemon·frontend 배포 bundle은 gateway orchestrator의 commit-worktree build 경로에서 구성합니다. +Gateway와 game frontend의 production build는 공개 코드의 디버깅과 장애 분석을 +위해 source map을 함께 생성합니다. 일반적인 bundle 크기 또는 build 시간 최적화는 +source map 제거 사유가 아니며, 별도의 보안 요구와 검토가 있을 때만 이 계약을 +변경합니다. + 각 서버의 `버전 업데이트`는 profile의 game migration만 적용하고 현재 게임 DB를 seed하지 않습니다. 별도 `시나리오 초기화`는 Git 업데이트 없이 현재 게시 commit을 기본으로 사용하며, 필요할 때만 새 버전 배포와 결합합니다. diff --git a/app/game-frontend/test/viteConfig.test.ts b/app/game-frontend/test/viteConfig.test.ts index 6ebe9cf7..79407549 100644 --- a/app/game-frontend/test/viteConfig.test.ts +++ b/app/game-frontend/test/viteConfig.test.ts @@ -1,5 +1,7 @@ import assert from 'node:assert/strict'; +import path from 'node:path'; import { describe, it } from 'node:test'; +import { loadConfigFromFile } from 'vite'; import { mergeViteEnv } from '../src/config/viteEnv.ts'; void describe('game frontend Vite config', () => { @@ -12,4 +14,18 @@ void describe('game frontend Vite config', () => { assert.equal(env.VITE_APP_BASE_PATH, '/hwe'); assert.equal(env.VITE_GAME_API_URL, '/hwe/api/trpc'); }); + + void it('keeps production source maps enabled', async () => { + const configPath = path.resolve(import.meta.dirname, '../vite.config.ts'); + const loaded = await loadConfigFromFile( + { command: 'build', mode: 'production' }, + configPath, + path.dirname(configPath), + undefined, + undefined, + 'runner' + ); + + assert.equal(loaded?.config.build?.sourcemap, true); + }); }); diff --git a/app/game-frontend/vite.config.ts b/app/game-frontend/vite.config.ts index 1413653c..7dce6094 100644 --- a/app/game-frontend/vite.config.ts +++ b/app/game-frontend/vite.config.ts @@ -30,6 +30,9 @@ export default defineConfig(({ mode }) => { return { base: normalizeBasePath(env.VITE_APP_BASE_PATH), plugins: [vue(), tailwindcss()], + build: { + sourcemap: true, + }, resolve: { alias: { '@': path.resolve(import.meta.dirname, './src'), diff --git a/app/gateway-frontend/package.json b/app/gateway-frontend/package.json index 4e90bea6..321b7c25 100644 --- a/app/gateway-frontend/package.json +++ b/app/gateway-frontend/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "dev": "vite", + "test": "node --test test/**/*.test.mjs", "test:e2e:operations": "playwright test --config e2e/playwright.config.mjs", "test:e2e:hwe-lifecycle": "playwright test --config e2e/hwe-lifecycle.playwright.config.mjs", "test:e2e:general-icons": "playwright test --config e2e/general-icon-lifecycle.playwright.config.mjs", diff --git a/app/gateway-frontend/test/viteConfig.test.mjs b/app/gateway-frontend/test/viteConfig.test.mjs new file mode 100644 index 00000000..28c708ef --- /dev/null +++ b/app/gateway-frontend/test/viteConfig.test.mjs @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import path from 'node:path'; +import { describe, it } from 'node:test'; +import { loadConfigFromFile } from 'vite'; + +void describe('gateway frontend Vite config', () => { + void it('keeps production source maps enabled', async () => { + const configPath = path.resolve(import.meta.dirname, '../vite.config.ts'); + const loaded = await loadConfigFromFile( + { command: 'build', mode: 'production' }, + configPath, + path.dirname(configPath), + undefined, + undefined, + 'runner' + ); + + assert.equal(loaded?.config.build?.sourcemap, true); + }); +}); diff --git a/app/gateway-frontend/vite.config.ts b/app/gateway-frontend/vite.config.ts index c93885f1..6b39f647 100644 --- a/app/gateway-frontend/vite.config.ts +++ b/app/gateway-frontend/vite.config.ts @@ -29,6 +29,9 @@ export default defineConfig(({ mode }) => { return { base: normalizeBasePath(env.VITE_APP_BASE_PATH), plugins: [vue(), tailwindcss()], + build: { + sourcemap: true, + }, resolve: { alias: { '@': path.resolve(import.meta.dirname, './src'), diff --git a/docs/e2e-caddy-routing.md b/docs/e2e-caddy-routing.md index 138e8424..e30e4b92 100644 --- a/docs/e2e-caddy-routing.md +++ b/docs/e2e-caddy-routing.md @@ -100,6 +100,8 @@ HWE는 `/che`를 `/hwe`로 바꿉니다. `VITE_*`는 browser bundle에 포함되 ## 정적 자산 - Vite asset URL은 frontend base path를 포함합니다. +- Gateway와 game production build는 공개 코드의 디버깅을 위한 source map을 + 생성하며, frontend 배포 산출물에는 생성된 `.map` 파일도 포함합니다. - Router direct navigation과 새로고침은 SPA fallback으로 frontend에 전달됩니다. - API와 events matcher는 frontend fallback보다 먼저 적용합니다.