@mandujs/cli 0.9.3 → 0.9.5

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 CHANGED
@@ -1,181 +1,181 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/konamgil/mandu/main/mandu_only_simbol.png" alt="Mandu" width="200" />
3
- </p>
4
-
5
- <h1 align="center">@mandujs/cli</h1>
6
-
7
- <p align="center">
8
- <strong>Agent-Native Fullstack Framework CLI</strong><br/>
9
- 에이전트가 코딩해도 아키텍처가 무너지지 않는 개발 OS
10
- </p>
11
-
12
- <p align="center">
13
- <a href="./README.md"><strong>English</strong></a> | 한국어
14
- </p>
15
-
16
- ## 설치
17
-
18
- ```bash
19
- # Bun 필수
20
- bun add -D @mandujs/cli
21
- ```
22
-
23
- ## 빠른 시작
24
-
25
- ```bash
26
- # 새 프로젝트 생성
27
- bunx @mandujs/cli init my-app
28
- cd my-app
29
-
30
- # 개발 서버 시작
31
- bun run dev
32
- ```
33
-
34
- ## 명령어
35
-
36
- ### `mandu init <project-name>`
37
-
38
- 새 Mandu 프로젝트를 생성합니다.
39
-
40
- ```bash
41
- bunx @mandujs/cli init my-app
42
- ```
43
-
44
- 생성되는 구조:
45
- ```
46
- my-app/
47
- ├── apps/
48
- │ ├── server/main.ts # 서버 진입점
49
- │ └── web/entry.tsx # 클라이언트 진입점
50
- ├── spec/
51
- │ └── routes.manifest.json # SSOT - 라우트 정의
52
- ├── tests/ # 테스트 템플릿
53
- ├── package.json
54
- └── tsconfig.json
55
- ```
56
-
57
- ### `mandu dev`
58
-
59
- 개발 서버를 시작합니다 (HMR 지원).
60
-
61
- ```bash
62
- bun run dev
63
- # 또는
64
- bunx mandu dev
65
- ```
66
-
67
- ### `mandu spec`
68
-
69
- spec 파일을 검증하고 lock 파일을 갱신합니다.
70
-
71
- ```bash
72
- bun run spec
73
- ```
74
-
75
- ### `mandu generate`
76
-
77
- spec 기반으로 코드를 생성합니다.
78
-
79
- ```bash
80
- bun run generate
81
- ```
82
-
83
- ### `mandu guard`
84
-
85
- 아키텍처 규칙을 검사하고 위반 사항을 자동 수정합니다.
86
-
87
- ```bash
88
- bun run guard
89
-
90
- # 자동 수정 비활성화
91
- bunx mandu guard --no-auto-correct
92
- ```
93
-
94
- 자동 수정 가능한 규칙:
95
- - `SPEC_HASH_MISMATCH` → lock 파일 갱신
96
- - `GENERATED_MANUAL_EDIT` → 코드 재생성
97
- - `SLOT_NOT_FOUND` → slot 파일 생성
98
-
99
- ## Spec 파일 작성
100
-
101
- `spec/routes.manifest.json`이 모든 라우트의 단일 진실 공급원(SSOT)입니다.
102
-
103
- ```json
104
- {
105
- "version": "1.0.0",
106
- "routes": [
107
- {
108
- "id": "getUsers",
109
- "pattern": "/api/users",
110
- "kind": "api",
111
- "module": "apps/server/api/users.ts"
112
- },
113
- {
114
- "id": "homePage",
115
- "pattern": "/",
116
- "kind": "page",
117
- "module": "apps/server/pages/home.ts",
118
- "componentModule": "apps/web/pages/Home.tsx"
119
- }
120
- ]
121
- }
122
- ```
123
-
124
- ### Slot 시스템 (v0.2.0+)
125
-
126
- 비즈니스 로직을 분리하려면 `slotModule`을 추가합니다:
127
-
128
- ```json
129
- {
130
- "id": "getUsers",
131
- "pattern": "/api/users",
132
- "kind": "api",
133
- "module": "apps/server/api/users.generated.ts",
134
- "slotModule": "apps/server/api/users.slot.ts"
135
- }
136
- ```
137
-
138
- - `*.generated.ts` - 프레임워크가 관리 (수정 금지)
139
- - `*.slot.ts` - 개발자가 작성하는 비즈니스 로직
140
-
141
- ## 개발 워크플로우
142
-
143
- ```bash
144
- # 1. spec 수정
145
- # 2. spec 검증 및 lock 갱신
146
- bun run spec
147
-
148
- # 3. 코드 생성
149
- bun run generate
150
-
151
- # 4. 아키텍처 검사
152
- bun run guard
153
-
154
- # 5. 테스트
155
- bun test
156
-
157
- # 6. 개발 서버
158
- bun run dev
159
- ```
160
-
161
- ## 테스트
162
-
163
- Bun 테스트 프레임워크를 기본 지원합니다.
164
-
165
- ```bash
166
- bun test # 테스트 실행
167
- bun test --watch # 감시 모드
168
- ```
169
-
170
- ## 요구 사항
171
-
172
- - Bun >= 1.0.0
173
- - React >= 18.0.0
174
-
175
- ## 관련 패키지
176
-
177
- - [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - 핵심 런타임
178
-
179
- ## 라이선스
180
-
181
- MIT
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/konamgil/mandu/main/mandu_only_simbol.png" alt="Mandu" width="200" />
3
+ </p>
4
+
5
+ <h1 align="center">@mandujs/cli</h1>
6
+
7
+ <p align="center">
8
+ <strong>Agent-Native Fullstack Framework CLI</strong><br/>
9
+ 에이전트가 코딩해도 아키텍처가 무너지지 않는 개발 OS
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="./README.md"><strong>English</strong></a> | 한국어
14
+ </p>
15
+
16
+ ## 설치
17
+
18
+ ```bash
19
+ # Bun 필수
20
+ bun add -D @mandujs/cli
21
+ ```
22
+
23
+ ## 빠른 시작
24
+
25
+ ```bash
26
+ # 새 프로젝트 생성
27
+ bunx @mandujs/cli init my-app
28
+ cd my-app
29
+
30
+ # 개발 서버 시작
31
+ bun run dev
32
+ ```
33
+
34
+ ## 명령어
35
+
36
+ ### `mandu init <project-name>`
37
+
38
+ 새 Mandu 프로젝트를 생성합니다.
39
+
40
+ ```bash
41
+ bunx @mandujs/cli init my-app
42
+ ```
43
+
44
+ 생성되는 구조:
45
+ ```
46
+ my-app/
47
+ ├── apps/
48
+ │ ├── server/main.ts # 서버 진입점
49
+ │ └── web/entry.tsx # 클라이언트 진입점
50
+ ├── spec/
51
+ │ └── routes.manifest.json # SSOT - 라우트 정의
52
+ ├── tests/ # 테스트 템플릿
53
+ ├── package.json
54
+ └── tsconfig.json
55
+ ```
56
+
57
+ ### `mandu dev`
58
+
59
+ 개발 서버를 시작합니다 (HMR 지원).
60
+
61
+ ```bash
62
+ bun run dev
63
+ # 또는
64
+ bunx mandu dev
65
+ ```
66
+
67
+ ### `mandu spec`
68
+
69
+ spec 파일을 검증하고 lock 파일을 갱신합니다.
70
+
71
+ ```bash
72
+ bun run spec
73
+ ```
74
+
75
+ ### `mandu generate`
76
+
77
+ spec 기반으로 코드를 생성합니다.
78
+
79
+ ```bash
80
+ bun run generate
81
+ ```
82
+
83
+ ### `mandu guard`
84
+
85
+ 아키텍처 규칙을 검사하고 위반 사항을 자동 수정합니다.
86
+
87
+ ```bash
88
+ bun run guard
89
+
90
+ # 자동 수정 비활성화
91
+ bunx mandu guard --no-auto-correct
92
+ ```
93
+
94
+ 자동 수정 가능한 규칙:
95
+ - `SPEC_HASH_MISMATCH` → lock 파일 갱신
96
+ - `GENERATED_MANUAL_EDIT` → 코드 재생성
97
+ - `SLOT_NOT_FOUND` → slot 파일 생성
98
+
99
+ ## Spec 파일 작성
100
+
101
+ `spec/routes.manifest.json`이 모든 라우트의 단일 진실 공급원(SSOT)입니다.
102
+
103
+ ```json
104
+ {
105
+ "version": "1.0.0",
106
+ "routes": [
107
+ {
108
+ "id": "getUsers",
109
+ "pattern": "/api/users",
110
+ "kind": "api",
111
+ "module": "apps/server/api/users.ts"
112
+ },
113
+ {
114
+ "id": "homePage",
115
+ "pattern": "/",
116
+ "kind": "page",
117
+ "module": "apps/server/pages/home.ts",
118
+ "componentModule": "apps/web/pages/Home.tsx"
119
+ }
120
+ ]
121
+ }
122
+ ```
123
+
124
+ ### Slot 시스템 (v0.2.0+)
125
+
126
+ 비즈니스 로직을 분리하려면 `slotModule`을 추가합니다:
127
+
128
+ ```json
129
+ {
130
+ "id": "getUsers",
131
+ "pattern": "/api/users",
132
+ "kind": "api",
133
+ "module": "apps/server/api/users.generated.ts",
134
+ "slotModule": "apps/server/api/users.slot.ts"
135
+ }
136
+ ```
137
+
138
+ - `*.generated.ts` - 프레임워크가 관리 (수정 금지)
139
+ - `*.slot.ts` - 개발자가 작성하는 비즈니스 로직
140
+
141
+ ## 개발 워크플로우
142
+
143
+ ```bash
144
+ # 1. spec 수정
145
+ # 2. spec 검증 및 lock 갱신
146
+ bun run spec
147
+
148
+ # 3. 코드 생성
149
+ bun run generate
150
+
151
+ # 4. 아키텍처 검사
152
+ bun run guard
153
+
154
+ # 5. 테스트
155
+ bun test
156
+
157
+ # 6. 개발 서버
158
+ bun run dev
159
+ ```
160
+
161
+ ## 테스트
162
+
163
+ Bun 테스트 프레임워크를 기본 지원합니다.
164
+
165
+ ```bash
166
+ bun test # 테스트 실행
167
+ bun test --watch # 감시 모드
168
+ ```
169
+
170
+ ## 요구 사항
171
+
172
+ - Bun >= 1.0.0
173
+ - React >= 18.0.0
174
+
175
+ ## 관련 패키지
176
+
177
+ - [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - 핵심 런타임
178
+
179
+ ## 라이선스
180
+
181
+ MIT
package/README.md CHANGED
@@ -1,181 +1,181 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/konamgil/mandu/main/mandu_only_simbol.png" alt="Mandu" width="200" />
3
- </p>
4
-
5
- <h1 align="center">@mandujs/cli</h1>
6
-
7
- <p align="center">
8
- <strong>Agent-Native Fullstack Framework CLI</strong><br/>
9
- A development OS where architecture stays intact even when AI agents write your code
10
- </p>
11
-
12
- <p align="center">
13
- English | <a href="./README.ko.md"><strong>한국어</strong></a>
14
- </p>
15
-
16
- ## Installation
17
-
18
- ```bash
19
- # Bun required
20
- bun add -D @mandujs/cli
21
- ```
22
-
23
- ## Quick Start
24
-
25
- ```bash
26
- # Create a new project
27
- bunx @mandujs/cli init my-app
28
- cd my-app
29
-
30
- # Start development server
31
- bun run dev
32
- ```
33
-
34
- ## Commands
35
-
36
- ### `mandu init <project-name>`
37
-
38
- Creates a new Mandu project.
39
-
40
- ```bash
41
- bunx @mandujs/cli init my-app
42
- ```
43
-
44
- Generated structure:
45
- ```
46
- my-app/
47
- ├── apps/
48
- │ ├── server/main.ts # Server entry point
49
- │ └── web/entry.tsx # Client entry point
50
- ├── spec/
51
- │ └── routes.manifest.json # SSOT - Route definitions
52
- ├── tests/ # Test templates
53
- ├── package.json
54
- └── tsconfig.json
55
- ```
56
-
57
- ### `mandu dev`
58
-
59
- Starts the development server (with HMR support).
60
-
61
- ```bash
62
- bun run dev
63
- # or
64
- bunx mandu dev
65
- ```
66
-
67
- ### `mandu spec`
68
-
69
- Validates the spec file and updates the lock file.
70
-
71
- ```bash
72
- bun run spec
73
- ```
74
-
75
- ### `mandu generate`
76
-
77
- Generates code based on the spec.
78
-
79
- ```bash
80
- bun run generate
81
- ```
82
-
83
- ### `mandu guard`
84
-
85
- Checks architecture rules and auto-corrects violations.
86
-
87
- ```bash
88
- bun run guard
89
-
90
- # Disable auto-correction
91
- bunx mandu guard --no-auto-correct
92
- ```
93
-
94
- Auto-correctable rules:
95
- - `SPEC_HASH_MISMATCH` → Updates lock file
96
- - `GENERATED_MANUAL_EDIT` → Regenerates code
97
- - `SLOT_NOT_FOUND` → Creates slot file
98
-
99
- ## Writing Spec Files
100
-
101
- `spec/routes.manifest.json` is the Single Source of Truth (SSOT) for all routes.
102
-
103
- ```json
104
- {
105
- "version": "1.0.0",
106
- "routes": [
107
- {
108
- "id": "getUsers",
109
- "pattern": "/api/users",
110
- "kind": "api",
111
- "module": "apps/server/api/users.ts"
112
- },
113
- {
114
- "id": "homePage",
115
- "pattern": "/",
116
- "kind": "page",
117
- "module": "apps/server/pages/home.ts",
118
- "componentModule": "apps/web/pages/Home.tsx"
119
- }
120
- ]
121
- }
122
- ```
123
-
124
- ### Slot System (v0.2.0+)
125
-
126
- Add `slotModule` to separate business logic:
127
-
128
- ```json
129
- {
130
- "id": "getUsers",
131
- "pattern": "/api/users",
132
- "kind": "api",
133
- "module": "apps/server/api/users.generated.ts",
134
- "slotModule": "apps/server/api/users.slot.ts"
135
- }
136
- ```
137
-
138
- - `*.generated.ts` - Managed by framework (do not modify)
139
- - `*.slot.ts` - Business logic written by developers
140
-
141
- ## Development Workflow
142
-
143
- ```bash
144
- # 1. Edit spec
145
- # 2. Validate spec and update lock
146
- bun run spec
147
-
148
- # 3. Generate code
149
- bun run generate
150
-
151
- # 4. Check architecture
152
- bun run guard
153
-
154
- # 5. Run tests
155
- bun test
156
-
157
- # 6. Start dev server
158
- bun run dev
159
- ```
160
-
161
- ## Testing
162
-
163
- Built-in support for Bun test framework.
164
-
165
- ```bash
166
- bun test # Run tests
167
- bun test --watch # Watch mode
168
- ```
169
-
170
- ## Requirements
171
-
172
- - Bun >= 1.0.0
173
- - React >= 18.0.0
174
-
175
- ## Related Packages
176
-
177
- - [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - Core runtime
178
-
179
- ## License
180
-
181
- MIT
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/konamgil/mandu/main/mandu_only_simbol.png" alt="Mandu" width="200" />
3
+ </p>
4
+
5
+ <h1 align="center">@mandujs/cli</h1>
6
+
7
+ <p align="center">
8
+ <strong>Agent-Native Fullstack Framework CLI</strong><br/>
9
+ A development OS where architecture stays intact even when AI agents write your code
10
+ </p>
11
+
12
+ <p align="center">
13
+ English | <a href="./README.ko.md"><strong>한국어</strong></a>
14
+ </p>
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Bun required
20
+ bun add -D @mandujs/cli
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # Create a new project
27
+ bunx @mandujs/cli init my-app
28
+ cd my-app
29
+
30
+ # Start development server
31
+ bun run dev
32
+ ```
33
+
34
+ ## Commands
35
+
36
+ ### `mandu init <project-name>`
37
+
38
+ Creates a new Mandu project.
39
+
40
+ ```bash
41
+ bunx @mandujs/cli init my-app
42
+ ```
43
+
44
+ Generated structure:
45
+ ```
46
+ my-app/
47
+ ├── apps/
48
+ │ ├── server/main.ts # Server entry point
49
+ │ └── web/entry.tsx # Client entry point
50
+ ├── spec/
51
+ │ └── routes.manifest.json # SSOT - Route definitions
52
+ ├── tests/ # Test templates
53
+ ├── package.json
54
+ └── tsconfig.json
55
+ ```
56
+
57
+ ### `mandu dev`
58
+
59
+ Starts the development server (with HMR support).
60
+
61
+ ```bash
62
+ bun run dev
63
+ # or
64
+ bunx mandu dev
65
+ ```
66
+
67
+ ### `mandu spec`
68
+
69
+ Validates the spec file and updates the lock file.
70
+
71
+ ```bash
72
+ bun run spec
73
+ ```
74
+
75
+ ### `mandu generate`
76
+
77
+ Generates code based on the spec.
78
+
79
+ ```bash
80
+ bun run generate
81
+ ```
82
+
83
+ ### `mandu guard`
84
+
85
+ Checks architecture rules and auto-corrects violations.
86
+
87
+ ```bash
88
+ bun run guard
89
+
90
+ # Disable auto-correction
91
+ bunx mandu guard --no-auto-correct
92
+ ```
93
+
94
+ Auto-correctable rules:
95
+ - `SPEC_HASH_MISMATCH` → Updates lock file
96
+ - `GENERATED_MANUAL_EDIT` → Regenerates code
97
+ - `SLOT_NOT_FOUND` → Creates slot file
98
+
99
+ ## Writing Spec Files
100
+
101
+ `spec/routes.manifest.json` is the Single Source of Truth (SSOT) for all routes.
102
+
103
+ ```json
104
+ {
105
+ "version": "1.0.0",
106
+ "routes": [
107
+ {
108
+ "id": "getUsers",
109
+ "pattern": "/api/users",
110
+ "kind": "api",
111
+ "module": "apps/server/api/users.ts"
112
+ },
113
+ {
114
+ "id": "homePage",
115
+ "pattern": "/",
116
+ "kind": "page",
117
+ "module": "apps/server/pages/home.ts",
118
+ "componentModule": "apps/web/pages/Home.tsx"
119
+ }
120
+ ]
121
+ }
122
+ ```
123
+
124
+ ### Slot System (v0.2.0+)
125
+
126
+ Add `slotModule` to separate business logic:
127
+
128
+ ```json
129
+ {
130
+ "id": "getUsers",
131
+ "pattern": "/api/users",
132
+ "kind": "api",
133
+ "module": "apps/server/api/users.generated.ts",
134
+ "slotModule": "apps/server/api/users.slot.ts"
135
+ }
136
+ ```
137
+
138
+ - `*.generated.ts` - Managed by framework (do not modify)
139
+ - `*.slot.ts` - Business logic written by developers
140
+
141
+ ## Development Workflow
142
+
143
+ ```bash
144
+ # 1. Edit spec
145
+ # 2. Validate spec and update lock
146
+ bun run spec
147
+
148
+ # 3. Generate code
149
+ bun run generate
150
+
151
+ # 4. Check architecture
152
+ bun run guard
153
+
154
+ # 5. Run tests
155
+ bun test
156
+
157
+ # 6. Start dev server
158
+ bun run dev
159
+ ```
160
+
161
+ ## Testing
162
+
163
+ Built-in support for Bun test framework.
164
+
165
+ ```bash
166
+ bun test # Run tests
167
+ bun test --watch # Watch mode
168
+ ```
169
+
170
+ ## Requirements
171
+
172
+ - Bun >= 1.0.0
173
+ - React >= 18.0.0
174
+
175
+ ## Related Packages
176
+
177
+ - [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - Core runtime
178
+
179
+ ## License
180
+
181
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/cli",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Agent-Native Fullstack Framework - 에이전트가 코딩해도 아키텍처가 무너지지 않는 개발 OS",
5
5
  "type": "module",
6
6
  "main": "./src/main.ts",
@@ -32,7 +32,7 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@mandujs/core": "0.9.3"
35
+ "@mandujs/core": "0.9.5"
36
36
  },
37
37
  "engines": {
38
38
  "bun": ">=1.0.0"
@@ -1,88 +1,88 @@
1
- import path from "path";
2
- import fs from "fs/promises";
3
-
4
- export interface InitOptions {
5
- name?: string;
6
- template?: string;
7
- }
8
-
9
- async function copyDir(src: string, dest: string, projectName: string): Promise<void> {
10
- await fs.mkdir(dest, { recursive: true });
11
- const entries = await fs.readdir(src, { withFileTypes: true });
12
-
13
- for (const entry of entries) {
14
- const srcPath = path.join(src, entry.name);
15
- const destPath = path.join(dest, entry.name);
16
-
17
- if (entry.isDirectory()) {
18
- await copyDir(srcPath, destPath, projectName);
19
- } else {
20
- let content = await fs.readFile(srcPath, "utf-8");
21
- // Replace template variables
22
- content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
23
- await fs.writeFile(destPath, content);
24
- }
25
- }
26
- }
27
-
28
- function getTemplatesDir(): string {
29
- // When installed via npm, templates are in the CLI package
30
- const commandsDir = import.meta.dir;
31
- // packages/cli/src/commands -> go up 2 levels to cli package root
32
- return path.resolve(commandsDir, "../../templates");
33
- }
34
-
35
- export async function init(options: InitOptions = {}): Promise<boolean> {
36
- const projectName = options.name || "my-mandu-app";
37
- const template = options.template || "default";
38
- const targetDir = path.resolve(process.cwd(), projectName);
39
-
40
- console.log(`🥟 Mandu Init`);
41
- console.log(`📁 프로젝트: ${projectName}`);
42
- console.log(`📦 템플릿: ${template}\n`);
43
-
44
- // Check if target directory exists
45
- try {
46
- await fs.access(targetDir);
47
- console.error(`❌ 디렉토리가 이미 존재합니다: ${targetDir}`);
48
- return false;
49
- } catch {
50
- // Directory doesn't exist, good to proceed
51
- }
52
-
53
- const templatesDir = getTemplatesDir();
54
- const templateDir = path.join(templatesDir, template);
55
-
56
- // Check if template exists
57
- try {
58
- await fs.access(templateDir);
59
- } catch {
60
- console.error(`❌ 템플릿을 찾을 수 없습니다: ${template}`);
61
- console.error(` 사용 가능한 템플릿: default`);
62
- return false;
63
- }
64
-
65
- console.log(`📋 템플릿 복사 중...`);
66
-
67
- try {
68
- await copyDir(templateDir, targetDir, projectName);
69
- } catch (error) {
70
- console.error(`❌ 템플릿 복사 실패:`, error);
71
- return false;
72
- }
73
-
74
- // Create empty generated directories
75
- await fs.mkdir(path.join(targetDir, "apps/server/generated/routes"), { recursive: true });
76
- await fs.mkdir(path.join(targetDir, "apps/web/generated/routes"), { recursive: true });
77
-
78
- console.log(`\n✅ 프로젝트 생성 완료!\n`);
79
- console.log(`📍 위치: ${targetDir}`);
80
- console.log(`\n🚀 시작하기:`);
81
- console.log(` cd ${projectName}`);
82
- console.log(` bun install`);
83
- console.log(` bun run spec`);
84
- console.log(` bun run generate`);
85
- console.log(` bun run dev`);
86
-
87
- return true;
88
- }
1
+ import path from "path";
2
+ import fs from "fs/promises";
3
+
4
+ export interface InitOptions {
5
+ name?: string;
6
+ template?: string;
7
+ }
8
+
9
+ async function copyDir(src: string, dest: string, projectName: string): Promise<void> {
10
+ await fs.mkdir(dest, { recursive: true });
11
+ const entries = await fs.readdir(src, { withFileTypes: true });
12
+
13
+ for (const entry of entries) {
14
+ const srcPath = path.join(src, entry.name);
15
+ const destPath = path.join(dest, entry.name);
16
+
17
+ if (entry.isDirectory()) {
18
+ await copyDir(srcPath, destPath, projectName);
19
+ } else {
20
+ let content = await fs.readFile(srcPath, "utf-8");
21
+ // Replace template variables
22
+ content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
23
+ await fs.writeFile(destPath, content);
24
+ }
25
+ }
26
+ }
27
+
28
+ function getTemplatesDir(): string {
29
+ // When installed via npm, templates are in the CLI package
30
+ const commandsDir = import.meta.dir;
31
+ // packages/cli/src/commands -> go up 2 levels to cli package root
32
+ return path.resolve(commandsDir, "../../templates");
33
+ }
34
+
35
+ export async function init(options: InitOptions = {}): Promise<boolean> {
36
+ const projectName = options.name || "my-mandu-app";
37
+ const template = options.template || "default";
38
+ const targetDir = path.resolve(process.cwd(), projectName);
39
+
40
+ console.log(`🥟 Mandu Init`);
41
+ console.log(`📁 프로젝트: ${projectName}`);
42
+ console.log(`📦 템플릿: ${template}\n`);
43
+
44
+ // Check if target directory exists
45
+ try {
46
+ await fs.access(targetDir);
47
+ console.error(`❌ 디렉토리가 이미 존재합니다: ${targetDir}`);
48
+ return false;
49
+ } catch {
50
+ // Directory doesn't exist, good to proceed
51
+ }
52
+
53
+ const templatesDir = getTemplatesDir();
54
+ const templateDir = path.join(templatesDir, template);
55
+
56
+ // Check if template exists
57
+ try {
58
+ await fs.access(templateDir);
59
+ } catch {
60
+ console.error(`❌ 템플릿을 찾을 수 없습니다: ${template}`);
61
+ console.error(` 사용 가능한 템플릿: default`);
62
+ return false;
63
+ }
64
+
65
+ console.log(`📋 템플릿 복사 중...`);
66
+
67
+ try {
68
+ await copyDir(templateDir, targetDir, projectName);
69
+ } catch (error) {
70
+ console.error(`❌ 템플릿 복사 실패:`, error);
71
+ return false;
72
+ }
73
+
74
+ // Create empty generated directories
75
+ await fs.mkdir(path.join(targetDir, "apps/server/generated/routes"), { recursive: true });
76
+ await fs.mkdir(path.join(targetDir, "apps/web/generated/routes"), { recursive: true });
77
+
78
+ console.log(`\n✅ 프로젝트 생성 완료!\n`);
79
+ console.log(`📍 위치: ${targetDir}`);
80
+ console.log(`\n🚀 시작하기:`);
81
+ console.log(` cd ${projectName}`);
82
+ console.log(` bun install`);
83
+ console.log(` bun run spec`);
84
+ console.log(` bun run generate`);
85
+ console.log(` bun run dev`);
86
+
87
+ return true;
88
+ }
package/src/util/fs.ts CHANGED
@@ -1,9 +1,9 @@
1
- import path from "path";
2
-
3
- export function resolveFromCwd(...paths: string[]): string {
4
- return path.resolve(process.cwd(), ...paths);
5
- }
6
-
7
- export function getRootDir(): string {
8
- return process.cwd();
9
- }
1
+ import path from "path";
2
+
3
+ export function resolveFromCwd(...paths: string[]): string {
4
+ return path.resolve(process.cwd(), ...paths);
5
+ }
6
+
7
+ export function getRootDir(): string {
8
+ return process.cwd();
9
+ }
@@ -1,35 +1,35 @@
1
- import React from "react";
2
- import type { ReactElement } from "react";
3
-
4
- export interface AppContext {
5
- routeId: string;
6
- url: string;
7
- params: Record<string, string>;
8
- }
9
-
10
- type RouteComponent = (props: { params: Record<string, string> }) => ReactElement;
11
-
12
- const routeComponents: Record<string, RouteComponent> = {};
13
-
14
- export function registerRoute(routeId: string, component: RouteComponent): void {
15
- routeComponents[routeId] = component;
16
- }
17
-
18
- export function createApp(context: AppContext): ReactElement {
19
- const Component = routeComponents[context.routeId];
20
-
21
- if (!Component) {
22
- return (
23
- <div>
24
- <h1>404 - Route Not Found</h1>
25
- <p>Route ID: {context.routeId}</p>
26
- </div>
27
- );
28
- }
29
-
30
- return <Component params={context.params} />;
31
- }
32
-
33
- export function getRegisteredRoutes(): string[] {
34
- return Object.keys(routeComponents);
35
- }
1
+ import React from "react";
2
+ import type { ReactElement } from "react";
3
+
4
+ export interface AppContext {
5
+ routeId: string;
6
+ url: string;
7
+ params: Record<string, string>;
8
+ }
9
+
10
+ type RouteComponent = (props: { params: Record<string, string> }) => ReactElement;
11
+
12
+ const routeComponents: Record<string, RouteComponent> = {};
13
+
14
+ export function registerRoute(routeId: string, component: RouteComponent): void {
15
+ routeComponents[routeId] = component;
16
+ }
17
+
18
+ export function createApp(context: AppContext): ReactElement {
19
+ const Component = routeComponents[context.routeId];
20
+
21
+ if (!Component) {
22
+ return (
23
+ <div>
24
+ <h1>404 - Route Not Found</h1>
25
+ <p>Route ID: {context.routeId}</p>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ return <Component params={context.params} />;
31
+ }
32
+
33
+ export function getRegisteredRoutes(): string[] {
34
+ return Object.keys(routeComponents);
35
+ }
@@ -1,18 +1,18 @@
1
- {
2
- "version": 1,
3
- "routes": [
4
- {
5
- "id": "home",
6
- "pattern": "/",
7
- "kind": "page",
8
- "module": "apps/server/generated/routes/home.route.ts",
9
- "componentModule": "apps/web/generated/routes/home.route.tsx"
10
- },
11
- {
12
- "id": "health",
13
- "pattern": "/api/health",
14
- "kind": "api",
15
- "module": "apps/server/generated/routes/health.route.ts"
16
- }
17
- ]
18
- }
1
+ {
2
+ "version": 1,
3
+ "routes": [
4
+ {
5
+ "id": "home",
6
+ "pattern": "/",
7
+ "kind": "page",
8
+ "module": "apps/server/generated/routes/home.route.ts",
9
+ "componentModule": "apps/web/generated/routes/home.route.tsx"
10
+ },
11
+ {
12
+ "id": "health",
13
+ "pattern": "/api/health",
14
+ "kind": "api",
15
+ "module": "apps/server/generated/routes/health.route.ts"
16
+ }
17
+ ]
18
+ }
@@ -1,58 +1,58 @@
1
- // Mandu Example Test
2
- // 이 파일은 테스트 작성 방법을 보여주는 예제입니다.
3
-
4
- import { describe, it, expect } from "bun:test";
5
- import { createTestRequest, parseJsonResponse, assertStatus } from "./helpers";
6
-
7
- describe("Example Tests", () => {
8
- describe("Basic Assertions", () => {
9
- it("should pass basic equality test", () => {
10
- expect(1 + 1).toBe(2);
11
- });
12
-
13
- it("should pass object equality test", () => {
14
- const obj = { status: "ok", data: { message: "hello" } };
15
- expect(obj).toEqual({
16
- status: "ok",
17
- data: { message: "hello" },
18
- });
19
- });
20
- });
21
-
22
- describe("Test Helpers", () => {
23
- it("should create test request", () => {
24
- const req = createTestRequest("http://localhost:3000/api/test", {
25
- method: "POST",
26
- body: { name: "test" },
27
- });
28
-
29
- expect(req.method).toBe("POST");
30
- expect(req.url).toBe("http://localhost:3000/api/test");
31
- });
32
-
33
- it("should parse JSON response", async () => {
34
- const mockResponse = new Response(
35
- JSON.stringify({ status: "ok" }),
36
- { status: 200 }
37
- );
38
-
39
- const data = await parseJsonResponse<{ status: string }>(mockResponse);
40
- expect(data.status).toBe("ok");
41
- });
42
- });
43
- });
44
-
45
- // API 핸들러 테스트 예제 (실제 핸들러 import 후 사용)
46
- // import handler from "../apps/server/generated/routes/health.route";
47
- //
48
- // describe("API: GET /api/health", () => {
49
- // it("should return 200 with status ok", async () => {
50
- // const req = createTestRequest("http://localhost:3000/api/health");
51
- // const response = handler(req, {});
52
- //
53
- // assertStatus(response, 200);
54
- //
55
- // const data = await parseJsonResponse<{ status: string }>(response);
56
- // expect(data.status).toBe("ok");
57
- // });
58
- // });
1
+ // Mandu Example Test
2
+ // 이 파일은 테스트 작성 방법을 보여주는 예제입니다.
3
+
4
+ import { describe, it, expect } from "bun:test";
5
+ import { createTestRequest, parseJsonResponse, assertStatus } from "./helpers";
6
+
7
+ describe("Example Tests", () => {
8
+ describe("Basic Assertions", () => {
9
+ it("should pass basic equality test", () => {
10
+ expect(1 + 1).toBe(2);
11
+ });
12
+
13
+ it("should pass object equality test", () => {
14
+ const obj = { status: "ok", data: { message: "hello" } };
15
+ expect(obj).toEqual({
16
+ status: "ok",
17
+ data: { message: "hello" },
18
+ });
19
+ });
20
+ });
21
+
22
+ describe("Test Helpers", () => {
23
+ it("should create test request", () => {
24
+ const req = createTestRequest("http://localhost:3000/api/test", {
25
+ method: "POST",
26
+ body: { name: "test" },
27
+ });
28
+
29
+ expect(req.method).toBe("POST");
30
+ expect(req.url).toBe("http://localhost:3000/api/test");
31
+ });
32
+
33
+ it("should parse JSON response", async () => {
34
+ const mockResponse = new Response(
35
+ JSON.stringify({ status: "ok" }),
36
+ { status: 200 }
37
+ );
38
+
39
+ const data = await parseJsonResponse<{ status: string }>(mockResponse);
40
+ expect(data.status).toBe("ok");
41
+ });
42
+ });
43
+ });
44
+
45
+ // API 핸들러 테스트 예제 (실제 핸들러 import 후 사용)
46
+ // import handler from "../apps/server/generated/routes/health.route";
47
+ //
48
+ // describe("API: GET /api/health", () => {
49
+ // it("should return 200 with status ok", async () => {
50
+ // const req = createTestRequest("http://localhost:3000/api/health");
51
+ // const response = handler(req, {});
52
+ //
53
+ // assertStatus(response, 200);
54
+ //
55
+ // const data = await parseJsonResponse<{ status: string }>(response);
56
+ // expect(data.status).toBe("ok");
57
+ // });
58
+ // });
@@ -1,52 +1,52 @@
1
- // Mandu Test Helpers
2
- // 테스트에서 사용할 유틸리티 함수들
3
-
4
- import type { Request } from "bun";
5
-
6
- /**
7
- * API 핸들러 테스트용 Request 생성
8
- */
9
- export function createTestRequest(
10
- url: string,
11
- options?: {
12
- method?: string;
13
- body?: unknown;
14
- headers?: Record<string, string>;
15
- }
16
- ): Request {
17
- const { method = "GET", body, headers = {} } = options || {};
18
-
19
- return new Request(url, {
20
- method,
21
- headers: {
22
- "Content-Type": "application/json",
23
- ...headers,
24
- },
25
- body: body ? JSON.stringify(body) : undefined,
26
- });
27
- }
28
-
29
- /**
30
- * Response를 JSON으로 파싱
31
- */
32
- export async function parseJsonResponse<T = unknown>(response: Response): Promise<T> {
33
- return response.json() as Promise<T>;
34
- }
35
-
36
- /**
37
- * Response 상태 검증
38
- */
39
- export function assertStatus(response: Response, expectedStatus: number): void {
40
- if (response.status !== expectedStatus) {
41
- throw new Error(
42
- `Expected status ${expectedStatus}, got ${response.status}`
43
- );
44
- }
45
- }
46
-
47
- /**
48
- * 테스트용 라우트 파라미터 생성
49
- */
50
- export function createParams(params: Record<string, string>): Record<string, string> {
51
- return params;
52
- }
1
+ // Mandu Test Helpers
2
+ // 테스트에서 사용할 유틸리티 함수들
3
+
4
+ import type { Request } from "bun";
5
+
6
+ /**
7
+ * API 핸들러 테스트용 Request 생성
8
+ */
9
+ export function createTestRequest(
10
+ url: string,
11
+ options?: {
12
+ method?: string;
13
+ body?: unknown;
14
+ headers?: Record<string, string>;
15
+ }
16
+ ): Request {
17
+ const { method = "GET", body, headers = {} } = options || {};
18
+
19
+ return new Request(url, {
20
+ method,
21
+ headers: {
22
+ "Content-Type": "application/json",
23
+ ...headers,
24
+ },
25
+ body: body ? JSON.stringify(body) : undefined,
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Response를 JSON으로 파싱
31
+ */
32
+ export async function parseJsonResponse<T = unknown>(response: Response): Promise<T> {
33
+ return response.json() as Promise<T>;
34
+ }
35
+
36
+ /**
37
+ * Response 상태 검증
38
+ */
39
+ export function assertStatus(response: Response, expectedStatus: number): void {
40
+ if (response.status !== expectedStatus) {
41
+ throw new Error(
42
+ `Expected status ${expectedStatus}, got ${response.status}`
43
+ );
44
+ }
45
+ }
46
+
47
+ /**
48
+ * 테스트용 라우트 파라미터 생성
49
+ */
50
+ export function createParams(params: Record<string, string>): Record<string, string> {
51
+ return params;
52
+ }
@@ -1,9 +1,9 @@
1
- // Mandu Test Setup
2
- // Bun 테스트 환경 설정
3
-
4
- // 테스트 타임아웃 설정 (필요 시)
5
- // import { setDefaultTimeout } from "bun:test";
6
- // setDefaultTimeout(10000);
7
-
8
- // 환경 변수 설정
9
- process.env.NODE_ENV = "test";
1
+ // Mandu Test Setup
2
+ // Bun 테스트 환경 설정
3
+
4
+ // 테스트 타임아웃 설정 (필요 시)
5
+ // import { setDefaultTimeout } from "bun:test";
6
+ // setDefaultTimeout(10000);
7
+
8
+ // 환경 변수 설정
9
+ process.env.NODE_ENV = "test";
@@ -1,14 +1,14 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "esModuleInterop": true,
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "jsx": "react-jsx",
10
- "types": ["bun-types"]
11
- },
12
- "include": ["apps/**/*.ts", "apps/**/*.tsx"],
13
- "exclude": ["node_modules"]
14
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "jsx": "react-jsx",
10
+ "types": ["bun-types"]
11
+ },
12
+ "include": ["apps/**/*.ts", "apps/**/*.tsx"],
13
+ "exclude": ["node_modules"]
14
+ }