@gaonjs/cli 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.js +11 -0
- package/dist/dev/console.d.ts +1 -1
- package/dist/dev/console.js +1 -0
- package/dist/dev/frontend-build.d.ts +14 -0
- package/dist/dev/frontend-build.js +56 -0
- package/dist/dev/index.d.ts +2 -0
- package/dist/dev/index.js +2 -0
- package/dist/templates/project/agents/data.md.tpl +5 -0
- package/dist/templates/project/package.json.tpl +1 -0
- package/package.json +5 -5
- package/dist/templates/auth/app.ts.tpl +0 -29
- package/dist/templates/auth/server.ts.tpl +0 -14
package/dist/commands/dev.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface DevCommandOptions {
|
|
|
36
36
|
readonly noVueTsc?: boolean;
|
|
37
37
|
/** Docker 자동 기동 끄기(개별 debug · 사용자가 이미 띄운 경우). */
|
|
38
38
|
readonly noDocker?: boolean;
|
|
39
|
+
/** 프론트 watch 빌드(vite build --watch) 끄기(개별 debug). */
|
|
40
|
+
readonly noVite?: boolean;
|
|
39
41
|
/** 통합 콘솔 타임스탬프 표시. */
|
|
40
42
|
readonly timestamp?: boolean;
|
|
41
43
|
/** 시그널(테스트 주입 · 기본 process). */
|
package/dist/commands/dev.js
CHANGED
|
@@ -30,6 +30,7 @@ import { createDevConsole } from '../dev/console.js';
|
|
|
30
30
|
import { ensureInfra, composeDown } from '../dev/docker.js';
|
|
31
31
|
import { startTscWatchers, killChild } from '../dev/tsc.js';
|
|
32
32
|
import { startRestartWatcher } from '../dev/watcher.js';
|
|
33
|
+
import { startFrontendBuild } from '../dev/frontend-build.js';
|
|
33
34
|
/**
|
|
34
35
|
* gaon 셀프 경로를 찾는다. 부모가 gaon 으로 실행됐다면 argv[1] 이
|
|
35
36
|
* gaonjs/dist/cli.js (또는 개발 시 packages/gaonjs/src/cli.ts). 자식
|
|
@@ -148,6 +149,15 @@ export async function runDevCommand(opts = {}) {
|
|
|
148
149
|
}
|
|
149
150
|
// ── 2) .gaon 재생성 워처 ─────────────────────────────────────────
|
|
150
151
|
const regen = await startGaonRegen({ cwd, console: consoleOut });
|
|
152
|
+
// ── 2') 프론트 번들 watch 빌드(결정 67) ─────────────────────────
|
|
153
|
+
// serve 가 dist/<앱>/index.html 을 문서 셸로 읽으므로 dev 에서 dist 를
|
|
154
|
+
// 최신으로 유지한다. 프론트 없는 프로젝트면 no-op.
|
|
155
|
+
const frontend = opts.noVite
|
|
156
|
+
? undefined
|
|
157
|
+
: startFrontendBuild({
|
|
158
|
+
cwd,
|
|
159
|
+
onLog: (line, level) => consoleOut.log('vite', line, level),
|
|
160
|
+
});
|
|
151
161
|
// ── 3) tsc / vue-tsc 워치 ────────────────────────────────────────
|
|
152
162
|
const tsc = opts.noTsc && opts.noVueTsc
|
|
153
163
|
? undefined
|
|
@@ -243,6 +253,7 @@ export async function runDevCommand(opts = {}) {
|
|
|
243
253
|
if (tsc) {
|
|
244
254
|
await tsc.stop();
|
|
245
255
|
}
|
|
256
|
+
frontend?.close();
|
|
246
257
|
regen.close();
|
|
247
258
|
if (stopDocker && composeFile) {
|
|
248
259
|
consoleOut.log('docker', '--stop-docker · compose down 실행');
|
package/dist/dev/console.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 정책상 chalk 는 쓰지 않는다 — 필요한 코드만 직접 쓴다.
|
|
14
14
|
*/
|
|
15
15
|
/** 콘솔이 구분하는 로그 소스. */
|
|
16
|
-
export type DevSource = 'dev' | 'docker' | 'serve' | 'watcher' | 'tsc' | 'vue-tsc';
|
|
16
|
+
export type DevSource = 'dev' | 'docker' | 'serve' | 'watcher' | 'tsc' | 'vue-tsc' | 'vite';
|
|
17
17
|
/** 로그 레벨 — human 은 색상 강조, json 은 필드로 실린다. */
|
|
18
18
|
export type DevLevel = 'info' | 'warn' | 'error';
|
|
19
19
|
export interface DevConsoleOptions {
|
package/dist/dev/console.js
CHANGED
|
@@ -26,6 +26,7 @@ const SOURCE_COLOR = {
|
|
|
26
26
|
watcher: '\x1b[36m', // cyan — 파일 감시
|
|
27
27
|
tsc: '\x1b[33m', // yellow — 타입 검사
|
|
28
28
|
'vue-tsc': '\x1b[95m', // bright magenta — vue 전용
|
|
29
|
+
vite: '\x1b[92m', // bright green — 프론트 빌드
|
|
29
30
|
};
|
|
30
31
|
/** stdout.isTTY 여부 자동 감지(테스트에서는 stream.isTTY 를 봄). */
|
|
31
32
|
function detectTty(stream) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface FrontendBuildHandle {
|
|
2
|
+
/** watch 빌드 프로세스를 종료한다. */
|
|
3
|
+
close(): void;
|
|
4
|
+
}
|
|
5
|
+
export interface StartFrontendBuildOptions {
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
/** 통합 콘솔 로그 훅. */
|
|
8
|
+
readonly onLog: (line: string, level: 'info' | 'warn' | 'error') => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* vite build --watch 를 띄운다. 프론트가 없거나 vite 가 없으면 아무 것도 하지
|
|
12
|
+
* 않고 no-op 핸들을 돌려준다(dev 는 계속 진행).
|
|
13
|
+
*/
|
|
14
|
+
export declare function startFrontendBuild(opts: StartFrontendBuildOptions): FrontendBuildHandle;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @gaonjs/cli · dev/frontend-build — 프론트 번들 watch 빌드 (결정 67 · M9-C)
|
|
3
|
+
*
|
|
4
|
+
* `gaon dev` 는 서버(serve)를 재시작 방식으로 띄운다(§dev.ts). 프론트 번들은
|
|
5
|
+
* serve 가 dist/<앱>/index.html 을 매 요청 읽어 문서 셸에 이어 붙이므로
|
|
6
|
+
* (packages/web frontend.ts), dev 에서 그 dist 를 최신으로 유지하려면 vite 를
|
|
7
|
+
* watch 모드로 함께 돌린다. 진짜 HMR(모듈 교체)은 v1 어댑터 범위 밖이라(§6.4)
|
|
8
|
+
* "저장 → 재빌드 → 새로고침" 루프를 제공한다.
|
|
9
|
+
*
|
|
10
|
+
* fail-open: apps/web/index.html·vite 가 없으면 조용히 건너뛴다(프론트 없는
|
|
11
|
+
* API 전용 프로젝트도 gaon dev 가 동작해야 한다). vite 실행 실패는 통합
|
|
12
|
+
* 콘솔에 알리되 dev 전체를 막지 않는다.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import { join, resolve } from 'node:path';
|
|
17
|
+
/** 프로젝트에 프론트 진입 HTML(apps/web/index.html)이 있는가. */
|
|
18
|
+
function hasFrontend(cwd) {
|
|
19
|
+
return existsSync(join(cwd, 'apps', 'web', 'index.html'));
|
|
20
|
+
}
|
|
21
|
+
/** cwd 의 로컬 vite bin 을 찾는다(없으면 undefined — 건너뜀). */
|
|
22
|
+
function resolveViteBin(cwd) {
|
|
23
|
+
const bin = join(cwd, 'node_modules', '.bin', 'vite');
|
|
24
|
+
return existsSync(bin) ? bin : undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* vite build --watch 를 띄운다. 프론트가 없거나 vite 가 없으면 아무 것도 하지
|
|
28
|
+
* 않고 no-op 핸들을 돌려준다(dev 는 계속 진행).
|
|
29
|
+
*/
|
|
30
|
+
export function startFrontendBuild(opts) {
|
|
31
|
+
const cwd = resolve(opts.cwd);
|
|
32
|
+
if (!hasFrontend(cwd)) {
|
|
33
|
+
opts.onLog('프론트 진입 HTML(apps/web/index.html)이 없어 vite 빌드를 건너뜁니다.', 'info');
|
|
34
|
+
return { close() { } };
|
|
35
|
+
}
|
|
36
|
+
const bin = resolveViteBin(cwd);
|
|
37
|
+
if (!bin) {
|
|
38
|
+
opts.onLog('vite 를 찾지 못해 프론트 watch 빌드를 건너뜁니다 → pnpm add -D vite @vitejs/plugin-vue', 'warn');
|
|
39
|
+
return { close() { } };
|
|
40
|
+
}
|
|
41
|
+
const child = spawn(bin, ['build', '--watch'], {
|
|
42
|
+
cwd,
|
|
43
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
44
|
+
env: { ...process.env },
|
|
45
|
+
});
|
|
46
|
+
child.stdout?.on('data', (b) => opts.onLog(b.toString().trimEnd(), 'info'));
|
|
47
|
+
child.stderr?.on('data', (b) => opts.onLog(b.toString().trimEnd(), 'error'));
|
|
48
|
+
child.on('error', (err) => opts.onLog(`vite 실행 실패: ${err.message}`, 'error'));
|
|
49
|
+
opts.onLog('▶ vite build --watch — 프론트 번들 감시 (dist/web)', 'info');
|
|
50
|
+
return {
|
|
51
|
+
close() {
|
|
52
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
53
|
+
child.kill('SIGTERM');
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
package/dist/dev/index.d.ts
CHANGED
|
@@ -12,5 +12,7 @@ export { startTscWatchers, killChild } from './tsc.js';
|
|
|
12
12
|
export type { TscWatcherOptions, TscWatcherHandle } from './tsc.js';
|
|
13
13
|
export { startRestartWatcher, isRestartChange, resolveWatchRoots } from './watcher.js';
|
|
14
14
|
export type { RestartWatcherOptions, RestartWatcherHandle } from './watcher.js';
|
|
15
|
+
export { startFrontendBuild } from './frontend-build.js';
|
|
16
|
+
export type { FrontendBuildHandle, StartFrontendBuildOptions } from './frontend-build.js';
|
|
15
17
|
export { createGaonViteServer } from './vite.js';
|
|
16
18
|
export type { GaonViteServer, CreateGaonViteServerOptions } from './vite.js';
|
package/dist/dev/index.js
CHANGED
|
@@ -8,5 +8,7 @@ export { createDevConsole } from './console.js';
|
|
|
8
8
|
export { findComposeFile, isDockerAvailable, inspectCompose, composeUp, composeDown, ensureInfra, } from './docker.js';
|
|
9
9
|
export { startTscWatchers, killChild } from './tsc.js';
|
|
10
10
|
export { startRestartWatcher, isRestartChange, resolveWatchRoots } from './watcher.js';
|
|
11
|
+
// 프론트 번들 watch 빌드 (결정 67)
|
|
12
|
+
export { startFrontendBuild } from './frontend-build.js';
|
|
11
13
|
// Vite dev server 프로그램적 기동 (M3-runtime) — dev.ts 배선은 M1 이 통합.
|
|
12
14
|
export { createGaonViteServer } from './vite.js';
|
|
@@ -456,6 +456,11 @@ gaon db seed # domain/seed.ts 실행
|
|
|
456
456
|
잡으므로 이 함정 자체가 없다 — **손작성 마이그는 diff 로 표현 못 하는 것에만**
|
|
457
457
|
쓴다(rename·백필·수동 DDL).
|
|
458
458
|
|
|
459
|
+
**단, 요구받은 마이그레이션을 생략하라는 뜻이 아니다** (결정 66). 지시나 리뷰가
|
|
460
|
+
마이그레이션 파일을 명시적으로 요구하면 만들되, 위 방식(참조 대상을 같은
|
|
461
|
+
파일에서 `ifNotExists()` 로 함께 보장)으로 함정을 피한다. 이 규칙은 마이그를
|
|
462
|
+
**쓸지 말지**가 아니라 **어떻게 쓸지**를 정한다.
|
|
463
|
+
|
|
459
464
|
- **손작성 마이그는 `db/migrations/<파일명>.ts`** 에 두고 `up(db)`·`down(db)`
|
|
460
465
|
(Kysely) 를 export 한다. **파일명이 곧 버전 키** — `0001_add_posts.ts`
|
|
461
466
|
시퀀스든 `20260724_add_posts.ts` 타임스탬프든 사전순 정렬이 안정적이면 된다
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaonjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Gaon CLI 구현: 제너레이터·스캐폴딩·로드맵 출력 (M1 스텁)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"typescript": "^5.9.0",
|
|
29
29
|
"vite": "^7.0.0",
|
|
30
30
|
"@gaonjs/async": "0.3.1",
|
|
31
|
-
"@gaonjs/
|
|
32
|
-
"@gaonjs/
|
|
31
|
+
"@gaonjs/config": "0.4.0",
|
|
32
|
+
"@gaonjs/web": "0.6.0",
|
|
33
33
|
"@gaonjs/data": "0.8.4",
|
|
34
|
-
"@gaonjs/
|
|
35
|
-
"@gaonjs/
|
|
34
|
+
"@gaonjs/core": "0.2.0",
|
|
35
|
+
"@gaonjs/mail": "0.1.1"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node -e \"require('fs').cpSync('src/templates','dist/templates',{recursive:true})\""
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// 웹 앱 팩토리 — gaon g auth 스캐폴드. createWebApp 으로 세션·인증을 배선한다.
|
|
2
|
-
import { createApp, type AppSessionOptions } from 'gaonjs/web'
|
|
3
|
-
import appRoutes from './routes.js'
|
|
4
|
-
import session from './controllers/session.js'
|
|
5
|
-
import registration from './controllers/registration.js'
|
|
6
|
-
import dashboard from './controllers/dashboard.js'
|
|
7
|
-
import { loadUser } from './auth.js'
|
|
8
|
-
|
|
9
|
-
export interface WebAppDeps {
|
|
10
|
-
/** 세션 설정 — { redisUrl, secret } (또는 redis 인스턴스). */
|
|
11
|
-
readonly session: AppSessionOptions
|
|
12
|
-
/** 서명 쿠키/CSRF 용 비밀. */
|
|
13
|
-
readonly cookieSecret?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function createWebApp(deps: WebAppDeps) {
|
|
17
|
-
return createApp({
|
|
18
|
-
apps: [
|
|
19
|
-
{
|
|
20
|
-
name: '{{APP_NAME}}',
|
|
21
|
-
routes: appRoutes,
|
|
22
|
-
controllers: { session, registration, dashboard },
|
|
23
|
-
session: deps.session,
|
|
24
|
-
auth: { loadUser, loginRedirect: '/session/new' },
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
cookieSecret: deps.cookieSecret,
|
|
28
|
-
})
|
|
29
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// 서버 진입점 — gaon g auth 스캐폴드. `node dist/server.js` 로 실행.
|
|
2
|
-
import { createWebApp } from './apps/{{APP_NAME}}/app.js'
|
|
3
|
-
|
|
4
|
-
const app = await createWebApp({
|
|
5
|
-
session: {
|
|
6
|
-
redisUrl: process.env.REDIS_URL ?? 'redis://127.0.0.1:6379',
|
|
7
|
-
secret: process.env.SESSION_SECRET ?? 'change-me-to-a-32+char-random-secret!!',
|
|
8
|
-
},
|
|
9
|
-
cookieSecret: process.env.COOKIE_SECRET,
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
const port = Number(process.env.PORT ?? 3000)
|
|
13
|
-
await app.listen({ port, host: '0.0.0.0' })
|
|
14
|
-
console.log(`web 앱이 http://localhost:${port} 에서 실행 중입니다.`)
|