@mandujs/cli 0.18.7 → 0.18.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/cli",
3
- "version": "0.18.7",
3
+ "version": "0.18.9",
4
4
  "description": "Agent-Native Fullstack Framework - 에이전트가 코딩해도 아키텍처가 무너지지 않는 개발 OS",
5
5
  "type": "module",
6
6
  "main": "./src/main.ts",
@@ -32,8 +32,8 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@mandujs/core": "^0.18.18",
36
- "@mandujs/ate": "^0.17.0",
35
+ "@mandujs/core": "^0.18.22",
36
+ "@mandujs/ate": "^0.17.2",
37
37
  "cfonts": "^3.3.0"
38
38
  },
39
39
  "engines": {
@@ -272,6 +272,20 @@ export async function dev(options: DevOptions = {}): Promise<void> {
272
272
  });
273
273
  };
274
274
 
275
+ // SSR 파일 변경 콜백 (page.tsx, layout.tsx → 서버 핸들러 재등록 + 브라우저 리로드)
276
+ const handleSSRChange = async (filePath: string) => {
277
+ const timestamp = new Date().toLocaleTimeString();
278
+ console.log(`\n🔄 [${timestamp}] SSR 변경 감지 → 핸들러 재등록`);
279
+ clearDefaultRegistry();
280
+ registeredLayouts.clear();
281
+ await registerHandlers(manifest, true);
282
+ hmrServer?.broadcast({
283
+ type: "reload",
284
+ data: { timestamp: Date.now() },
285
+ });
286
+ console.log(` ✅ SSR 갱신 완료 — 브라우저 리로드`);
287
+ };
288
+
275
289
  if (hasIslands && hmrEnabled) {
276
290
  // HMR 서버 시작
277
291
  hmrServer = createHMRServer(port);
@@ -283,6 +297,7 @@ export async function dev(options: DevOptions = {}): Promise<void> {
283
297
  watchDirs: devConfig.watchDirs,
284
298
  onRebuild: handleRebuild,
285
299
  onError: handleBundlerError,
300
+ onSSRChange: handleSSRChange,
286
301
  });
287
302
 
288
303
  // 재시작 핸들러 등록
@@ -306,6 +321,7 @@ export async function dev(options: DevOptions = {}): Promise<void> {
306
321
  watchDirs: devConfig.watchDirs,
307
322
  onRebuild: handleRebuild,
308
323
  onError: handleBundlerError,
324
+ onSSRChange: handleSSRChange,
309
325
  });
310
326
 
311
327
  // 5. 브라우저 전체 리로드
@@ -8,13 +8,14 @@
8
8
  */
9
9
 
10
10
  import type { CLI_ERROR_CODES } from "../errors";
11
+ import type { CSSFramework, UILibrary } from "./init";
11
12
 
12
13
  /**
13
14
  * 명령어 실행 컨텍스트
14
15
  */
15
- export interface CommandContext {
16
+ export interface CommandContext<TOptions extends Record<string, unknown> = Record<string, string>> {
16
17
  args: string[];
17
- options: Record<string, string>;
18
+ options: TOptions;
18
19
  }
19
20
 
20
21
  /**
@@ -33,6 +34,24 @@ export interface CommandRegistration {
33
34
  run: (ctx: CommandContext) => Promise<boolean>;
34
35
  }
35
36
 
37
+ /**
38
+ * Mapped type: derive a handler map from a command options map.
39
+ *
40
+ * Given a mapping of command names to their option types, produces
41
+ * the corresponding handler signatures automatically.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * type Cmds = { build: { watch: boolean }; dev: { port: number } };
46
+ * type Handlers = CommandHandlers<Cmds>;
47
+ * // => { build: (ctx: CommandContext<{ watch: boolean }>) => Promise<boolean>;
48
+ * // dev: (ctx: CommandContext<{ port: number }>) => Promise<boolean>; }
49
+ * ```
50
+ */
51
+ export type CommandHandlers<TMap extends Record<string, Record<string, unknown>>> = {
52
+ [K in keyof TMap]: (ctx: CommandContext<TMap[K]>) => Promise<boolean>;
53
+ };
54
+
36
55
  /**
37
56
  * 명령어 레지스트리
38
57
  */
@@ -71,8 +90,8 @@ registerCommand({
71
90
  return init({
72
91
  name: ctx.options.name || ctx.options._positional,
73
92
  template: ctx.options.template,
74
- css: ctx.options.css as any,
75
- ui: ctx.options.ui as any,
93
+ css: ctx.options.css as CSSFramework | undefined,
94
+ ui: ctx.options.ui as UILibrary | undefined,
76
95
  theme: ctx.options.theme === "true",
77
96
  minimal: ctx.options.minimal === "true",
78
97
  withCi: ctx.options["with-ci"] === "true",
@@ -15,7 +15,7 @@ export async function testAuto(opts: { ci?: boolean; impact?: boolean; baseURL?:
15
15
 
16
16
  // 2) impact subset (optional)
17
17
  let onlyRoutes: string[] | undefined;
18
- let impactInfo: any = { mode: "full", changedFiles: [], selectedRoutes: [] };
18
+ let impactInfo: { mode: "full" | "subset"; changedFiles: string[]; selectedRoutes: string[] } = { mode: "full", changedFiles: [], selectedRoutes: [] };
19
19
  if (opts.impact) {
20
20
  const impactRes = await ateImpact({ repoRoot });
21
21
  onlyRoutes = impactRes.selectedRoutes.length ? impactRes.selectedRoutes : undefined;
@@ -178,7 +178,7 @@ export async function runPreAction(params: {
178
178
  const showBanner =
179
179
  !SKIP_BANNER_COMMANDS.has(command) &&
180
180
  !isTruthyEnv("MANDU_HIDE_BANNER") &&
181
- shouldShowBanner();
181
+ shouldShowBanner(process.argv);
182
182
 
183
183
  if (showBanner && version) {
184
184
  console.log(renderMiniBanner(version));