@mandujs/cli 0.12.2 → 0.13.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/README.ko.md +234 -234
- package/README.md +354 -354
- package/package.json +2 -2
- package/src/commands/contract.ts +173 -173
- package/src/commands/dev.ts +8 -68
- package/src/commands/doctor.ts +27 -27
- package/src/commands/guard-arch.ts +303 -303
- package/src/commands/guard-check.ts +3 -3
- package/src/commands/monitor.ts +300 -300
- package/src/commands/openapi.ts +107 -107
- package/src/commands/registry.ts +367 -357
- package/src/commands/routes.ts +228 -228
- package/src/commands/start.ts +184 -0
- package/src/errors/codes.ts +35 -35
- package/src/errors/index.ts +2 -2
- package/src/errors/messages.ts +143 -143
- package/src/hooks/index.ts +17 -17
- package/src/hooks/preaction.ts +256 -256
- package/src/main.ts +37 -34
- package/src/terminal/banner.ts +166 -166
- package/src/terminal/help.ts +306 -306
- package/src/terminal/index.ts +71 -71
- package/src/terminal/output.ts +295 -295
- package/src/terminal/palette.ts +30 -30
- package/src/terminal/progress.ts +327 -327
- package/src/terminal/stream-writer.ts +214 -214
- package/src/terminal/table.ts +354 -354
- package/src/terminal/theme.ts +142 -142
- package/src/util/bun.ts +6 -6
- package/src/util/fs.ts +23 -23
- package/src/util/handlers.ts +96 -0
- package/src/util/manifest.ts +52 -52
- package/src/util/output.ts +22 -22
- package/src/util/port.ts +71 -71
- package/templates/default/AGENTS.md +96 -96
- package/templates/default/app/api/health/route.ts +13 -13
- package/templates/default/app/globals.css +49 -49
- package/templates/default/app/layout.tsx +27 -27
- package/templates/default/app/page.tsx +38 -38
- package/templates/default/package.json +1 -0
- package/templates/default/src/client/shared/lib/utils.ts +16 -16
- package/templates/default/src/client/shared/ui/button.tsx +57 -57
- package/templates/default/src/client/shared/ui/card.tsx +78 -78
- package/templates/default/src/client/shared/ui/index.ts +21 -21
- package/templates/default/src/client/shared/ui/input.tsx +24 -24
- package/templates/default/tests/example.test.ts +58 -58
- package/templates/default/tests/helpers.ts +52 -52
- package/templates/default/tests/setup.ts +9 -9
- package/templates/default/tsconfig.json +12 -14
- package/templates/default/apps/server/main.ts +0 -67
- package/templates/default/apps/web/entry.tsx +0 -35
package/src/hooks/preaction.ts
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DNA-016: Pre-Action Hooks
|
|
3
|
-
*
|
|
4
|
-
* 명령어 실행 전 공통 작업 수행
|
|
5
|
-
* - 프로세스 타이틀 설정
|
|
6
|
-
* - 조건부 배너 표시
|
|
7
|
-
* - Verbose 모드 설정
|
|
8
|
-
* - 설정 로드
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { shouldShowBanner, renderMiniBanner } from "../terminal/banner.js";
|
|
12
|
-
import { loadManduConfig, type ManduConfig } from "@mandujs/core";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Pre-Action 컨텍스트
|
|
16
|
-
*/
|
|
17
|
-
export interface PreActionContext {
|
|
18
|
-
/** 현재 명령어 */
|
|
19
|
-
command: string;
|
|
20
|
-
/** 서브커맨드 */
|
|
21
|
-
subcommand?: string;
|
|
22
|
-
/** 명령어 옵션 */
|
|
23
|
-
options: Record<string, string>;
|
|
24
|
-
/** 로드된 설정 */
|
|
25
|
-
config?: ManduConfig;
|
|
26
|
-
/** verbose 모드 여부 */
|
|
27
|
-
verbose: boolean;
|
|
28
|
-
/** 작업 디렉토리 */
|
|
29
|
-
cwd: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Pre-Action 훅 타입
|
|
34
|
-
*/
|
|
35
|
-
export type PreActionHook = (ctx: PreActionContext) => void | Promise<void>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Pre-Action 훅 레지스트리
|
|
39
|
-
*/
|
|
40
|
-
class PreActionRegistry {
|
|
41
|
-
private hooks: PreActionHook[] = [];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 훅 등록
|
|
45
|
-
*/
|
|
46
|
-
register(hook: PreActionHook): void {
|
|
47
|
-
this.hooks.push(hook);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 훅 제거
|
|
52
|
-
*/
|
|
53
|
-
unregister(hook: PreActionHook): boolean {
|
|
54
|
-
const index = this.hooks.indexOf(hook);
|
|
55
|
-
if (index >= 0) {
|
|
56
|
-
this.hooks.splice(index, 1);
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 모든 훅 실행
|
|
64
|
-
*/
|
|
65
|
-
async runAll(ctx: PreActionContext): Promise<void> {
|
|
66
|
-
for (const hook of this.hooks) {
|
|
67
|
-
await hook(ctx);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 훅 초기화
|
|
73
|
-
*/
|
|
74
|
-
clear(): void {
|
|
75
|
-
this.hooks = [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 등록된 훅 수
|
|
80
|
-
*/
|
|
81
|
-
get size(): number {
|
|
82
|
-
return this.hooks.length;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 전역 Pre-Action 훅 레지스트리
|
|
88
|
-
*/
|
|
89
|
-
export const preActionRegistry = new PreActionRegistry();
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 설정 로드가 필요없는 명령어
|
|
93
|
-
*/
|
|
94
|
-
const SKIP_CONFIG_COMMANDS = new Set([
|
|
95
|
-
"init",
|
|
96
|
-
"help",
|
|
97
|
-
"version",
|
|
98
|
-
"completion",
|
|
99
|
-
]);
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 배너 표시가 필요없는 명령어
|
|
103
|
-
*/
|
|
104
|
-
const SKIP_BANNER_COMMANDS = new Set([
|
|
105
|
-
"completion",
|
|
106
|
-
"version",
|
|
107
|
-
]);
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* verbose 전역 상태
|
|
111
|
-
*/
|
|
112
|
-
let globalVerbose = false;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* verbose 모드 설정
|
|
116
|
-
*/
|
|
117
|
-
export function setVerbose(value: boolean): void {
|
|
118
|
-
globalVerbose = value;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* verbose 모드 확인
|
|
123
|
-
*/
|
|
124
|
-
export function isVerbose(): boolean {
|
|
125
|
-
return globalVerbose;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 프로세스 타이틀 설정
|
|
130
|
-
*/
|
|
131
|
-
export function setProcessTitle(command: string, subcommand?: string): void {
|
|
132
|
-
const title = subcommand
|
|
133
|
-
? `mandu ${command} ${subcommand}`
|
|
134
|
-
: `mandu ${command}`;
|
|
135
|
-
|
|
136
|
-
if (typeof process.title !== "undefined") {
|
|
137
|
-
process.title = title;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 기본 Pre-Action 실행
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```ts
|
|
146
|
-
* const ctx = await runPreAction({
|
|
147
|
-
* command: "dev",
|
|
148
|
-
* options: { port: "3000" },
|
|
149
|
-
* });
|
|
150
|
-
*
|
|
151
|
-
* // ctx.config 에서 로드된 설정 사용
|
|
152
|
-
* // ctx.verbose 로 verbose 모드 확인
|
|
153
|
-
* ```
|
|
154
|
-
*/
|
|
155
|
-
export async function runPreAction(params: {
|
|
156
|
-
command: string;
|
|
157
|
-
subcommand?: string;
|
|
158
|
-
options: Record<string, string>;
|
|
159
|
-
cwd?: string;
|
|
160
|
-
version?: string;
|
|
161
|
-
}): Promise<PreActionContext> {
|
|
162
|
-
const {
|
|
163
|
-
command,
|
|
164
|
-
subcommand,
|
|
165
|
-
options,
|
|
166
|
-
cwd = process.cwd(),
|
|
167
|
-
version,
|
|
168
|
-
} = params;
|
|
169
|
-
|
|
170
|
-
// 1. verbose 모드 확인
|
|
171
|
-
const verbose = options.verbose === "true" || process.env.MANDU_VERBOSE === "true";
|
|
172
|
-
setVerbose(verbose);
|
|
173
|
-
|
|
174
|
-
// 2. 프로세스 타이틀 설정
|
|
175
|
-
setProcessTitle(command, subcommand);
|
|
176
|
-
|
|
177
|
-
// 3. 조건부 배너 표시
|
|
178
|
-
const showBanner =
|
|
179
|
-
!SKIP_BANNER_COMMANDS.has(command) &&
|
|
180
|
-
!isTruthyEnv("MANDU_HIDE_BANNER") &&
|
|
181
|
-
shouldShowBanner();
|
|
182
|
-
|
|
183
|
-
if (showBanner && version) {
|
|
184
|
-
console.log(renderMiniBanner(version));
|
|
185
|
-
console.log();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// 4. 설정 로드 (필요한 명령어만)
|
|
189
|
-
let config: ManduConfig | undefined;
|
|
190
|
-
if (!SKIP_CONFIG_COMMANDS.has(command)) {
|
|
191
|
-
try {
|
|
192
|
-
config = await loadManduConfig(cwd);
|
|
193
|
-
} catch {
|
|
194
|
-
// 설정 로드 실패 시 무시 (옵션 설정만 사용)
|
|
195
|
-
if (verbose) {
|
|
196
|
-
console.warn("[mandu] Config load failed, using defaults");
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Pre-Action 컨텍스트 생성
|
|
202
|
-
const ctx: PreActionContext = {
|
|
203
|
-
command,
|
|
204
|
-
subcommand,
|
|
205
|
-
options,
|
|
206
|
-
config,
|
|
207
|
-
verbose,
|
|
208
|
-
cwd,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// 5. 등록된 훅 실행
|
|
212
|
-
await preActionRegistry.runAll(ctx);
|
|
213
|
-
|
|
214
|
-
return ctx;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* 환경변수가 truthy인지 확인
|
|
219
|
-
*/
|
|
220
|
-
function isTruthyEnv(key: string): boolean {
|
|
221
|
-
const value = process.env[key];
|
|
222
|
-
if (!value) return false;
|
|
223
|
-
return !["0", "false", "no", ""].includes(value.toLowerCase());
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Pre-Action 훅 등록 헬퍼
|
|
228
|
-
*
|
|
229
|
-
* @example
|
|
230
|
-
* ```ts
|
|
231
|
-
* registerPreActionHook(async (ctx) => {
|
|
232
|
-
* if (ctx.command === "dev") {
|
|
233
|
-
* console.log("Starting development mode...");
|
|
234
|
-
* }
|
|
235
|
-
* });
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
export function registerPreActionHook(hook: PreActionHook): () => void {
|
|
239
|
-
preActionRegistry.register(hook);
|
|
240
|
-
return () => preActionRegistry.unregister(hook);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* 기본 훅들 등록
|
|
245
|
-
*/
|
|
246
|
-
export function registerDefaultHooks(): void {
|
|
247
|
-
// 예: 개발 모드에서 추가 정보 표시
|
|
248
|
-
registerPreActionHook((ctx) => {
|
|
249
|
-
if (ctx.verbose && ctx.config) {
|
|
250
|
-
console.log(`[mandu] Config loaded from ${ctx.cwd}`);
|
|
251
|
-
if (ctx.config.server?.port) {
|
|
252
|
-
console.log(`[mandu] Server port: ${ctx.config.server.port}`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* DNA-016: Pre-Action Hooks
|
|
3
|
+
*
|
|
4
|
+
* 명령어 실행 전 공통 작업 수행
|
|
5
|
+
* - 프로세스 타이틀 설정
|
|
6
|
+
* - 조건부 배너 표시
|
|
7
|
+
* - Verbose 모드 설정
|
|
8
|
+
* - 설정 로드
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { shouldShowBanner, renderMiniBanner } from "../terminal/banner.js";
|
|
12
|
+
import { loadManduConfig, type ManduConfig } from "@mandujs/core";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Pre-Action 컨텍스트
|
|
16
|
+
*/
|
|
17
|
+
export interface PreActionContext {
|
|
18
|
+
/** 현재 명령어 */
|
|
19
|
+
command: string;
|
|
20
|
+
/** 서브커맨드 */
|
|
21
|
+
subcommand?: string;
|
|
22
|
+
/** 명령어 옵션 */
|
|
23
|
+
options: Record<string, string>;
|
|
24
|
+
/** 로드된 설정 */
|
|
25
|
+
config?: ManduConfig;
|
|
26
|
+
/** verbose 모드 여부 */
|
|
27
|
+
verbose: boolean;
|
|
28
|
+
/** 작업 디렉토리 */
|
|
29
|
+
cwd: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pre-Action 훅 타입
|
|
34
|
+
*/
|
|
35
|
+
export type PreActionHook = (ctx: PreActionContext) => void | Promise<void>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Pre-Action 훅 레지스트리
|
|
39
|
+
*/
|
|
40
|
+
class PreActionRegistry {
|
|
41
|
+
private hooks: PreActionHook[] = [];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 훅 등록
|
|
45
|
+
*/
|
|
46
|
+
register(hook: PreActionHook): void {
|
|
47
|
+
this.hooks.push(hook);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 훅 제거
|
|
52
|
+
*/
|
|
53
|
+
unregister(hook: PreActionHook): boolean {
|
|
54
|
+
const index = this.hooks.indexOf(hook);
|
|
55
|
+
if (index >= 0) {
|
|
56
|
+
this.hooks.splice(index, 1);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 모든 훅 실행
|
|
64
|
+
*/
|
|
65
|
+
async runAll(ctx: PreActionContext): Promise<void> {
|
|
66
|
+
for (const hook of this.hooks) {
|
|
67
|
+
await hook(ctx);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 훅 초기화
|
|
73
|
+
*/
|
|
74
|
+
clear(): void {
|
|
75
|
+
this.hooks = [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 등록된 훅 수
|
|
80
|
+
*/
|
|
81
|
+
get size(): number {
|
|
82
|
+
return this.hooks.length;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 전역 Pre-Action 훅 레지스트리
|
|
88
|
+
*/
|
|
89
|
+
export const preActionRegistry = new PreActionRegistry();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 설정 로드가 필요없는 명령어
|
|
93
|
+
*/
|
|
94
|
+
const SKIP_CONFIG_COMMANDS = new Set([
|
|
95
|
+
"init",
|
|
96
|
+
"help",
|
|
97
|
+
"version",
|
|
98
|
+
"completion",
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 배너 표시가 필요없는 명령어
|
|
103
|
+
*/
|
|
104
|
+
const SKIP_BANNER_COMMANDS = new Set([
|
|
105
|
+
"completion",
|
|
106
|
+
"version",
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* verbose 전역 상태
|
|
111
|
+
*/
|
|
112
|
+
let globalVerbose = false;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* verbose 모드 설정
|
|
116
|
+
*/
|
|
117
|
+
export function setVerbose(value: boolean): void {
|
|
118
|
+
globalVerbose = value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* verbose 모드 확인
|
|
123
|
+
*/
|
|
124
|
+
export function isVerbose(): boolean {
|
|
125
|
+
return globalVerbose;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 프로세스 타이틀 설정
|
|
130
|
+
*/
|
|
131
|
+
export function setProcessTitle(command: string, subcommand?: string): void {
|
|
132
|
+
const title = subcommand
|
|
133
|
+
? `mandu ${command} ${subcommand}`
|
|
134
|
+
: `mandu ${command}`;
|
|
135
|
+
|
|
136
|
+
if (typeof process.title !== "undefined") {
|
|
137
|
+
process.title = title;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 기본 Pre-Action 실행
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const ctx = await runPreAction({
|
|
147
|
+
* command: "dev",
|
|
148
|
+
* options: { port: "3000" },
|
|
149
|
+
* });
|
|
150
|
+
*
|
|
151
|
+
* // ctx.config 에서 로드된 설정 사용
|
|
152
|
+
* // ctx.verbose 로 verbose 모드 확인
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export async function runPreAction(params: {
|
|
156
|
+
command: string;
|
|
157
|
+
subcommand?: string;
|
|
158
|
+
options: Record<string, string>;
|
|
159
|
+
cwd?: string;
|
|
160
|
+
version?: string;
|
|
161
|
+
}): Promise<PreActionContext> {
|
|
162
|
+
const {
|
|
163
|
+
command,
|
|
164
|
+
subcommand,
|
|
165
|
+
options,
|
|
166
|
+
cwd = process.cwd(),
|
|
167
|
+
version,
|
|
168
|
+
} = params;
|
|
169
|
+
|
|
170
|
+
// 1. verbose 모드 확인
|
|
171
|
+
const verbose = options.verbose === "true" || process.env.MANDU_VERBOSE === "true";
|
|
172
|
+
setVerbose(verbose);
|
|
173
|
+
|
|
174
|
+
// 2. 프로세스 타이틀 설정
|
|
175
|
+
setProcessTitle(command, subcommand);
|
|
176
|
+
|
|
177
|
+
// 3. 조건부 배너 표시
|
|
178
|
+
const showBanner =
|
|
179
|
+
!SKIP_BANNER_COMMANDS.has(command) &&
|
|
180
|
+
!isTruthyEnv("MANDU_HIDE_BANNER") &&
|
|
181
|
+
shouldShowBanner();
|
|
182
|
+
|
|
183
|
+
if (showBanner && version) {
|
|
184
|
+
console.log(renderMiniBanner(version));
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 4. 설정 로드 (필요한 명령어만)
|
|
189
|
+
let config: ManduConfig | undefined;
|
|
190
|
+
if (!SKIP_CONFIG_COMMANDS.has(command)) {
|
|
191
|
+
try {
|
|
192
|
+
config = await loadManduConfig(cwd);
|
|
193
|
+
} catch {
|
|
194
|
+
// 설정 로드 실패 시 무시 (옵션 설정만 사용)
|
|
195
|
+
if (verbose) {
|
|
196
|
+
console.warn("[mandu] Config load failed, using defaults");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Pre-Action 컨텍스트 생성
|
|
202
|
+
const ctx: PreActionContext = {
|
|
203
|
+
command,
|
|
204
|
+
subcommand,
|
|
205
|
+
options,
|
|
206
|
+
config,
|
|
207
|
+
verbose,
|
|
208
|
+
cwd,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// 5. 등록된 훅 실행
|
|
212
|
+
await preActionRegistry.runAll(ctx);
|
|
213
|
+
|
|
214
|
+
return ctx;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 환경변수가 truthy인지 확인
|
|
219
|
+
*/
|
|
220
|
+
function isTruthyEnv(key: string): boolean {
|
|
221
|
+
const value = process.env[key];
|
|
222
|
+
if (!value) return false;
|
|
223
|
+
return !["0", "false", "no", ""].includes(value.toLowerCase());
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Pre-Action 훅 등록 헬퍼
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* registerPreActionHook(async (ctx) => {
|
|
232
|
+
* if (ctx.command === "dev") {
|
|
233
|
+
* console.log("Starting development mode...");
|
|
234
|
+
* }
|
|
235
|
+
* });
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
export function registerPreActionHook(hook: PreActionHook): () => void {
|
|
239
|
+
preActionRegistry.register(hook);
|
|
240
|
+
return () => preActionRegistry.unregister(hook);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* 기본 훅들 등록
|
|
245
|
+
*/
|
|
246
|
+
export function registerDefaultHooks(): void {
|
|
247
|
+
// 예: 개발 모드에서 추가 정보 표시
|
|
248
|
+
registerPreActionHook((ctx) => {
|
|
249
|
+
if (ctx.verbose && ctx.config) {
|
|
250
|
+
console.log(`[mandu] Config loaded from ${ctx.cwd}`);
|
|
251
|
+
if (ctx.config.server?.port) {
|
|
252
|
+
console.log(`[mandu] Server port: ${ctx.config.server.port}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -27,6 +27,7 @@ Commands:
|
|
|
27
27
|
routes watch 실시간 라우트 감시
|
|
28
28
|
dev 개발 서버 실행 (FS Routes + Guard 기본)
|
|
29
29
|
build 클라이언트 번들 빌드 (Hydration)
|
|
30
|
+
start 프로덕션 서버 실행 (build 후)
|
|
30
31
|
guard 아키텍처 위반 검사 (기본)
|
|
31
32
|
guard arch 아키텍처 위반 검사 (FSD/Clean/Hexagonal)
|
|
32
33
|
guard legacy 레거시 Spec Guard 검사
|
|
@@ -125,7 +126,7 @@ Examples:
|
|
|
125
126
|
bunx mandu lock --diff --show-secrets # 변경사항 상세 비교
|
|
126
127
|
|
|
127
128
|
FS Routes Workflow (권장):
|
|
128
|
-
1. init → 2. app/ 폴더에 page.tsx 생성 → 3. dev → 4. build
|
|
129
|
+
1. init → 2. app/ 폴더에 page.tsx 생성 → 3. dev → 4. build → 5. start
|
|
129
130
|
|
|
130
131
|
Legacy Workflow:
|
|
131
132
|
1. init → 2. spec-upsert → 3. generate → 4. build → 5. guard → 6. dev
|
|
@@ -140,39 +141,39 @@ Brain (sLLM) Workflow:
|
|
|
140
141
|
/**
|
|
141
142
|
* 인자 파싱
|
|
142
143
|
*/
|
|
143
|
-
function parseArgs(args: string[]): { command: string; options: Record<string, string> } {
|
|
144
|
-
const options: Record<string, string> = {};
|
|
145
|
-
let command = "";
|
|
146
|
-
const shortFlags: Record<string, string> = {
|
|
147
|
-
h: "help",
|
|
148
|
-
q: "quiet",
|
|
149
|
-
v: "verify",
|
|
150
|
-
d: "diff",
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
for (let i = 0; i < args.length; i++) {
|
|
154
|
-
const arg = args[i];
|
|
155
|
-
|
|
156
|
-
// 플래그 처리
|
|
157
|
-
if (arg.startsWith("--")) {
|
|
158
|
-
const key = arg.slice(2);
|
|
159
|
-
const value = args[i + 1] && !args[i + 1].startsWith("--") ? args[++i] : "true";
|
|
160
|
-
options[key] = value;
|
|
161
|
-
} else if (arg.startsWith("-") && arg.length > 1) {
|
|
162
|
-
const flags = arg.slice(1).split("");
|
|
163
|
-
for (const flag of flags) {
|
|
164
|
-
const mapped = shortFlags[flag];
|
|
165
|
-
if (mapped) {
|
|
166
|
-
options[mapped] = "true";
|
|
167
|
-
} else {
|
|
168
|
-
options[flag] = "true";
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
} else if (!command) {
|
|
172
|
-
// 첫 번째 비플래그 인자가 명령어
|
|
173
|
-
command = arg;
|
|
174
|
-
} else if (!options._positional) {
|
|
175
|
-
// 두 번째 비플래그 인자가 positional
|
|
144
|
+
function parseArgs(args: string[]): { command: string; options: Record<string, string> } {
|
|
145
|
+
const options: Record<string, string> = {};
|
|
146
|
+
let command = "";
|
|
147
|
+
const shortFlags: Record<string, string> = {
|
|
148
|
+
h: "help",
|
|
149
|
+
q: "quiet",
|
|
150
|
+
v: "verify",
|
|
151
|
+
d: "diff",
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
for (let i = 0; i < args.length; i++) {
|
|
155
|
+
const arg = args[i];
|
|
156
|
+
|
|
157
|
+
// 플래그 처리
|
|
158
|
+
if (arg.startsWith("--")) {
|
|
159
|
+
const key = arg.slice(2);
|
|
160
|
+
const value = args[i + 1] && !args[i + 1].startsWith("--") ? args[++i] : "true";
|
|
161
|
+
options[key] = value;
|
|
162
|
+
} else if (arg.startsWith("-") && arg.length > 1) {
|
|
163
|
+
const flags = arg.slice(1).split("");
|
|
164
|
+
for (const flag of flags) {
|
|
165
|
+
const mapped = shortFlags[flag];
|
|
166
|
+
if (mapped) {
|
|
167
|
+
options[mapped] = "true";
|
|
168
|
+
} else {
|
|
169
|
+
options[flag] = "true";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else if (!command) {
|
|
173
|
+
// 첫 번째 비플래그 인자가 명령어
|
|
174
|
+
command = arg;
|
|
175
|
+
} else if (!options._positional) {
|
|
176
|
+
// 두 번째 비플래그 인자가 positional
|
|
176
177
|
options._positional = arg;
|
|
177
178
|
}
|
|
178
179
|
}
|
|
@@ -232,6 +233,8 @@ async function main(): Promise<void> {
|
|
|
232
233
|
}
|
|
233
234
|
process.exit(1);
|
|
234
235
|
}
|
|
236
|
+
|
|
237
|
+
process.exit(0);
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
main().catch((error) => handleCLIError(error));
|