@mandujs/cli 0.12.2 → 0.13.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandujs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
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.12.
|
|
35
|
+
"@mandujs/core": "0.12.2",
|
|
36
36
|
"cfonts": "^3.3.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
package/src/commands/contract.ts
CHANGED
|
@@ -3,63 +3,63 @@
|
|
|
3
3
|
* Contract 생성 및 검증 명령어
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
runContractGuardCheck,
|
|
8
|
-
generateContractTemplate,
|
|
9
|
-
buildContractRegistry,
|
|
10
|
-
writeContractRegistry,
|
|
11
|
-
readContractRegistry,
|
|
12
|
-
diffContractRegistry,
|
|
13
|
-
validateAndReport,
|
|
14
|
-
} from "@mandujs/core";
|
|
15
|
-
import path from "path";
|
|
16
|
-
import fs from "fs/promises";
|
|
17
|
-
import { resolveManifest } from "../util/manifest";
|
|
6
|
+
import {
|
|
7
|
+
runContractGuardCheck,
|
|
8
|
+
generateContractTemplate,
|
|
9
|
+
buildContractRegistry,
|
|
10
|
+
writeContractRegistry,
|
|
11
|
+
readContractRegistry,
|
|
12
|
+
diffContractRegistry,
|
|
13
|
+
validateAndReport,
|
|
14
|
+
} from "@mandujs/core";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import fs from "fs/promises";
|
|
17
|
+
import { resolveManifest } from "../util/manifest";
|
|
18
18
|
|
|
19
19
|
interface ContractCreateOptions {
|
|
20
20
|
routeId: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
interface ContractValidateOptions {
|
|
24
|
-
verbose?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface ContractBuildOptions {
|
|
28
|
-
output?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface ContractDiffOptions {
|
|
32
|
-
from?: string;
|
|
33
|
-
to?: string;
|
|
34
|
-
output?: string;
|
|
35
|
-
json?: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function loadRoutesManifest(rootDir: string) {
|
|
39
|
-
const config = await validateAndReport(rootDir);
|
|
40
|
-
if (!config) {
|
|
41
|
-
throw new Error("Invalid mandu.config");
|
|
42
|
-
}
|
|
43
|
-
const resolved = await resolveManifest(rootDir, { fsRoutes: config.fsRoutes });
|
|
44
|
-
return resolved.manifest;
|
|
45
|
-
}
|
|
23
|
+
interface ContractValidateOptions {
|
|
24
|
+
verbose?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ContractBuildOptions {
|
|
28
|
+
output?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ContractDiffOptions {
|
|
32
|
+
from?: string;
|
|
33
|
+
to?: string;
|
|
34
|
+
output?: string;
|
|
35
|
+
json?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function loadRoutesManifest(rootDir: string) {
|
|
39
|
+
const config = await validateAndReport(rootDir);
|
|
40
|
+
if (!config) {
|
|
41
|
+
throw new Error("Invalid mandu.config");
|
|
42
|
+
}
|
|
43
|
+
const resolved = await resolveManifest(rootDir, { fsRoutes: config.fsRoutes });
|
|
44
|
+
return resolved.manifest;
|
|
45
|
+
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Create a new contract file for a route
|
|
49
49
|
*/
|
|
50
|
-
export async function contractCreate(options: ContractCreateOptions): Promise<boolean> {
|
|
51
|
-
const rootDir = process.cwd();
|
|
52
|
-
|
|
53
|
-
console.log(`\n📜 Creating contract for route: ${options.routeId}\n`);
|
|
54
|
-
|
|
55
|
-
// Load manifest
|
|
56
|
-
let manifest;
|
|
57
|
-
try {
|
|
58
|
-
manifest = await loadRoutesManifest(rootDir);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
50
|
+
export async function contractCreate(options: ContractCreateOptions): Promise<boolean> {
|
|
51
|
+
const rootDir = process.cwd();
|
|
52
|
+
|
|
53
|
+
console.log(`\n📜 Creating contract for route: ${options.routeId}\n`);
|
|
54
|
+
|
|
55
|
+
// Load manifest
|
|
56
|
+
let manifest;
|
|
57
|
+
try {
|
|
58
|
+
manifest = await loadRoutesManifest(rootDir);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
63
|
|
|
64
64
|
// Find the route
|
|
65
65
|
const route = manifest.routes.find((r) => r.id === options.routeId);
|
|
@@ -113,22 +113,22 @@ export async function contractCreate(options: ContractCreateOptions): Promise<bo
|
|
|
113
113
|
/**
|
|
114
114
|
* Validate all contracts against their slot implementations
|
|
115
115
|
*/
|
|
116
|
-
export async function contractValidate(options: ContractValidateOptions = {}): Promise<boolean> {
|
|
117
|
-
const rootDir = process.cwd();
|
|
118
|
-
|
|
119
|
-
console.log(`\n🔍 Validating contracts...\n`);
|
|
120
|
-
|
|
121
|
-
// Load manifest
|
|
122
|
-
let manifest;
|
|
123
|
-
try {
|
|
124
|
-
manifest = await loadRoutesManifest(rootDir);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Run contract guard check
|
|
131
|
-
const violations = await runContractGuardCheck(manifest, rootDir);
|
|
116
|
+
export async function contractValidate(options: ContractValidateOptions = {}): Promise<boolean> {
|
|
117
|
+
const rootDir = process.cwd();
|
|
118
|
+
|
|
119
|
+
console.log(`\n🔍 Validating contracts...\n`);
|
|
120
|
+
|
|
121
|
+
// Load manifest
|
|
122
|
+
let manifest;
|
|
123
|
+
try {
|
|
124
|
+
manifest = await loadRoutesManifest(rootDir);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Run contract guard check
|
|
131
|
+
const violations = await runContractGuardCheck(manifest, rootDir);
|
|
132
132
|
|
|
133
133
|
if (violations.length === 0) {
|
|
134
134
|
console.log(`✅ All contracts are valid!\n`);
|
|
@@ -175,112 +175,112 @@ export async function contractValidate(options: ContractValidateOptions = {}): P
|
|
|
175
175
|
console.log(`💡 Use --verbose for fix suggestions\n`);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Build contract registry (.mandu/contracts.json)
|
|
183
|
-
*/
|
|
184
|
-
export async function contractBuild(options: ContractBuildOptions = {}): Promise<boolean> {
|
|
185
|
-
const rootDir = process.cwd();
|
|
186
|
-
const outputPath = options.output || path.join(rootDir, ".mandu", "contracts.json");
|
|
187
|
-
|
|
188
|
-
console.log(`\n📦 Building contract registry...\n`);
|
|
189
|
-
|
|
190
|
-
let manifest;
|
|
191
|
-
try {
|
|
192
|
-
manifest = await loadRoutesManifest(rootDir);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
const { registry, warnings } = await buildContractRegistry(manifest, rootDir);
|
|
198
|
-
|
|
199
|
-
if (warnings.length > 0) {
|
|
200
|
-
console.log(`⚠️ ${warnings.length} warning(s):`);
|
|
201
|
-
for (const warning of warnings) {
|
|
202
|
-
console.log(` - ${warning}`);
|
|
203
|
-
}
|
|
204
|
-
console.log();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
208
|
-
await writeContractRegistry(outputPath, registry);
|
|
209
|
-
|
|
210
|
-
console.log(`✅ Registry generated: ${path.relative(rootDir, outputPath)}`);
|
|
211
|
-
console.log(`📊 Contracts: ${registry.contracts.length}`);
|
|
212
|
-
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Diff current contracts against a registry
|
|
218
|
-
*/
|
|
219
|
-
export async function contractDiff(options: ContractDiffOptions = {}): Promise<boolean> {
|
|
220
|
-
const rootDir = process.cwd();
|
|
221
|
-
const fromPath = options.from || path.join(rootDir, ".mandu", "contracts.json");
|
|
222
|
-
|
|
223
|
-
console.log(`\n🔍 Diffing contracts...\n`);
|
|
224
|
-
|
|
225
|
-
const fromRegistry = await readContractRegistry(fromPath);
|
|
226
|
-
if (!fromRegistry) {
|
|
227
|
-
console.error(`❌ Registry not found: ${path.relative(rootDir, fromPath)}`);
|
|
228
|
-
console.log(`💡 Run \`mandu contract build\` first.`);
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let toRegistry = options.to ? await readContractRegistry(options.to) : null;
|
|
233
|
-
|
|
234
|
-
if (!toRegistry) {
|
|
235
|
-
let manifest;
|
|
236
|
-
try {
|
|
237
|
-
manifest = await loadRoutesManifest(rootDir);
|
|
238
|
-
} catch (error) {
|
|
239
|
-
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
const { registry } = await buildContractRegistry(manifest, rootDir);
|
|
243
|
-
toRegistry = registry;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const diff = diffContractRegistry(fromRegistry, toRegistry);
|
|
247
|
-
|
|
248
|
-
if (options.output) {
|
|
249
|
-
await fs.mkdir(path.dirname(options.output), { recursive: true });
|
|
250
|
-
await Bun.write(options.output, JSON.stringify(diff, null, 2));
|
|
251
|
-
console.log(`✅ Diff saved: ${path.relative(rootDir, options.output)}`);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (options.json) {
|
|
255
|
-
console.log(JSON.stringify(diff, null, 2));
|
|
256
|
-
return diff.summary.major === 0;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
console.log(`📊 Summary: major ${diff.summary.major}, minor ${diff.summary.minor}, patch ${diff.summary.patch}`);
|
|
260
|
-
|
|
261
|
-
if (diff.added.length > 0) {
|
|
262
|
-
console.log(`\n🟢 Added (${diff.added.length})`);
|
|
263
|
-
for (const entry of diff.added) {
|
|
264
|
-
console.log(` - ${entry.id} (${entry.routeId})`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (diff.removed.length > 0) {
|
|
269
|
-
console.log(`\n🔴 Removed (${diff.removed.length})`);
|
|
270
|
-
for (const entry of diff.removed) {
|
|
271
|
-
console.log(` - ${entry.id} (${entry.routeId})`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (diff.changed.length > 0) {
|
|
276
|
-
console.log(`\n🟡 Changed (${diff.changed.length})`);
|
|
277
|
-
for (const change of diff.changed) {
|
|
278
|
-
console.log(` - ${change.id} (${change.routeId}) [${change.severity}]`);
|
|
279
|
-
for (const detail of change.changes) {
|
|
280
|
-
console.log(` • ${detail}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return diff.summary.major === 0;
|
|
286
|
-
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Build contract registry (.mandu/contracts.json)
|
|
183
|
+
*/
|
|
184
|
+
export async function contractBuild(options: ContractBuildOptions = {}): Promise<boolean> {
|
|
185
|
+
const rootDir = process.cwd();
|
|
186
|
+
const outputPath = options.output || path.join(rootDir, ".mandu", "contracts.json");
|
|
187
|
+
|
|
188
|
+
console.log(`\n📦 Building contract registry...\n`);
|
|
189
|
+
|
|
190
|
+
let manifest;
|
|
191
|
+
try {
|
|
192
|
+
manifest = await loadRoutesManifest(rootDir);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
const { registry, warnings } = await buildContractRegistry(manifest, rootDir);
|
|
198
|
+
|
|
199
|
+
if (warnings.length > 0) {
|
|
200
|
+
console.log(`⚠️ ${warnings.length} warning(s):`);
|
|
201
|
+
for (const warning of warnings) {
|
|
202
|
+
console.log(` - ${warning}`);
|
|
203
|
+
}
|
|
204
|
+
console.log();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
208
|
+
await writeContractRegistry(outputPath, registry);
|
|
209
|
+
|
|
210
|
+
console.log(`✅ Registry generated: ${path.relative(rootDir, outputPath)}`);
|
|
211
|
+
console.log(`📊 Contracts: ${registry.contracts.length}`);
|
|
212
|
+
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Diff current contracts against a registry
|
|
218
|
+
*/
|
|
219
|
+
export async function contractDiff(options: ContractDiffOptions = {}): Promise<boolean> {
|
|
220
|
+
const rootDir = process.cwd();
|
|
221
|
+
const fromPath = options.from || path.join(rootDir, ".mandu", "contracts.json");
|
|
222
|
+
|
|
223
|
+
console.log(`\n🔍 Diffing contracts...\n`);
|
|
224
|
+
|
|
225
|
+
const fromRegistry = await readContractRegistry(fromPath);
|
|
226
|
+
if (!fromRegistry) {
|
|
227
|
+
console.error(`❌ Registry not found: ${path.relative(rootDir, fromPath)}`);
|
|
228
|
+
console.log(`💡 Run \`mandu contract build\` first.`);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let toRegistry = options.to ? await readContractRegistry(options.to) : null;
|
|
233
|
+
|
|
234
|
+
if (!toRegistry) {
|
|
235
|
+
let manifest;
|
|
236
|
+
try {
|
|
237
|
+
manifest = await loadRoutesManifest(rootDir);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.error("❌ Failed to load manifest:", error instanceof Error ? error.message : error);
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
const { registry } = await buildContractRegistry(manifest, rootDir);
|
|
243
|
+
toRegistry = registry;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const diff = diffContractRegistry(fromRegistry, toRegistry);
|
|
247
|
+
|
|
248
|
+
if (options.output) {
|
|
249
|
+
await fs.mkdir(path.dirname(options.output), { recursive: true });
|
|
250
|
+
await Bun.write(options.output, JSON.stringify(diff, null, 2));
|
|
251
|
+
console.log(`✅ Diff saved: ${path.relative(rootDir, options.output)}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (options.json) {
|
|
255
|
+
console.log(JSON.stringify(diff, null, 2));
|
|
256
|
+
return diff.summary.major === 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
console.log(`📊 Summary: major ${diff.summary.major}, minor ${diff.summary.minor}, patch ${diff.summary.patch}`);
|
|
260
|
+
|
|
261
|
+
if (diff.added.length > 0) {
|
|
262
|
+
console.log(`\n🟢 Added (${diff.added.length})`);
|
|
263
|
+
for (const entry of diff.added) {
|
|
264
|
+
console.log(` - ${entry.id} (${entry.routeId})`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (diff.removed.length > 0) {
|
|
269
|
+
console.log(`\n🔴 Removed (${diff.removed.length})`);
|
|
270
|
+
for (const entry of diff.removed) {
|
|
271
|
+
console.log(` - ${entry.id} (${entry.routeId})`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (diff.changed.length > 0) {
|
|
276
|
+
console.log(`\n🟡 Changed (${diff.changed.length})`);
|
|
277
|
+
for (const change of diff.changed) {
|
|
278
|
+
console.log(` - ${change.id} (${change.routeId}) [${change.severity}]`);
|
|
279
|
+
for (const detail of change.changes) {
|
|
280
|
+
console.log(` • ${detail}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return diff.summary.major === 0;
|
|
286
|
+
}
|
package/src/commands/dev.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startServer,
|
|
3
|
-
registerApiHandler,
|
|
4
|
-
registerPageLoader,
|
|
5
|
-
registerPageHandler,
|
|
6
|
-
registerLayoutLoader,
|
|
7
3
|
startDevBundler,
|
|
8
4
|
createHMRServer,
|
|
9
5
|
needsHydration,
|
|
@@ -36,6 +32,7 @@ import { CLI_ERROR_CODES, printCLIError } from "../errors";
|
|
|
36
32
|
import { importFresh } from "../util/bun";
|
|
37
33
|
import { resolveManifest } from "../util/manifest";
|
|
38
34
|
import { resolveAvailablePort } from "../util/port";
|
|
35
|
+
import { registerManifestHandlers } from "../util/handlers";
|
|
39
36
|
import path from "path";
|
|
40
37
|
|
|
41
38
|
export interface DevOptions {
|
|
@@ -202,70 +199,13 @@ export async function dev(options: DevOptions = {}): Promise<void> {
|
|
|
202
199
|
// Layout 경로 추적 (중복 등록 방지)
|
|
203
200
|
const registeredLayouts = new Set<string>();
|
|
204
201
|
|
|
205
|
-
// 핸들러 등록 함수
|
|
206
|
-
const registerHandlers = async (
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
registeredLayouts
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
for (const route of manifest.routes) {
|
|
213
|
-
if (route.kind === "api") {
|
|
214
|
-
const modulePath = path.resolve(rootDir, route.module);
|
|
215
|
-
try {
|
|
216
|
-
// 캐시 무효화 (HMR용)
|
|
217
|
-
const module = await importFresh(modulePath);
|
|
218
|
-
let handler = module.default || module.handler || module;
|
|
219
|
-
|
|
220
|
-
// ManduFilling 인스턴스를 핸들러 함수로 래핑
|
|
221
|
-
if (handler && typeof handler.handle === 'function') {
|
|
222
|
-
console.log(` 🔄 ManduFilling 래핑: ${route.id}`);
|
|
223
|
-
const filling = handler;
|
|
224
|
-
handler = async (req: Request, params?: Record<string, string>) => {
|
|
225
|
-
return filling.handle(req, params);
|
|
226
|
-
};
|
|
227
|
-
} else {
|
|
228
|
-
console.log(` ⚠️ 핸들러 타입: ${typeof handler}, handle: ${typeof handler?.handle}`);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
registerApiHandler(route.id, handler);
|
|
232
|
-
console.log(` 📡 API: ${route.pattern} -> ${route.id}`);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.error(` ❌ API 핸들러 로드 실패: ${route.id}`, error);
|
|
235
|
-
}
|
|
236
|
-
} else if (route.kind === "page" && route.componentModule) {
|
|
237
|
-
const componentPath = path.resolve(rootDir, route.componentModule);
|
|
238
|
-
const isIsland = needsHydration(route);
|
|
239
|
-
const hasLayout = route.layoutChain && route.layoutChain.length > 0;
|
|
240
|
-
|
|
241
|
-
// Layout 로더 등록
|
|
242
|
-
if (route.layoutChain) {
|
|
243
|
-
for (const layoutPath of route.layoutChain) {
|
|
244
|
-
if (!registeredLayouts.has(layoutPath)) {
|
|
245
|
-
const absLayoutPath = path.resolve(rootDir, layoutPath);
|
|
246
|
-
registerLayoutLoader(layoutPath, async () => {
|
|
247
|
-
// 캐시 무효화 (HMR용)
|
|
248
|
-
return importFresh(absLayoutPath);
|
|
249
|
-
});
|
|
250
|
-
registeredLayouts.add(layoutPath);
|
|
251
|
-
console.log(` 🎨 Layout: ${layoutPath}`);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// slotModule이 있으면 PageHandler 사용 (filling.loader 지원)
|
|
257
|
-
if (route.slotModule) {
|
|
258
|
-
registerPageHandler(route.id, async () => {
|
|
259
|
-
const module = await importFresh(componentPath);
|
|
260
|
-
return module.default;
|
|
261
|
-
});
|
|
262
|
-
console.log(` 📄 Page: ${route.pattern} -> ${route.id} (with loader)${isIsland ? " 🏝️" : ""}${hasLayout ? " 🎨" : ""}`);
|
|
263
|
-
} else {
|
|
264
|
-
registerPageLoader(route.id, () => importFresh(componentPath));
|
|
265
|
-
console.log(` 📄 Page: ${route.pattern} -> ${route.id}${isIsland ? " 🏝️" : ""}${hasLayout ? " 🎨" : ""}`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
202
|
+
// 핸들러 등록 함수 (공유 유틸 사용)
|
|
203
|
+
const registerHandlers = async (m: RoutesManifest, isReload = false) => {
|
|
204
|
+
await registerManifestHandlers(m, rootDir, {
|
|
205
|
+
importFn: importFresh,
|
|
206
|
+
registeredLayouts,
|
|
207
|
+
isReload,
|
|
208
|
+
});
|
|
269
209
|
};
|
|
270
210
|
|
|
271
211
|
// 초기 핸들러 등록
|
package/src/commands/doctor.ts
CHANGED
|
@@ -15,22 +15,22 @@ import {
|
|
|
15
15
|
getBrain,
|
|
16
16
|
} from "../../../core/src/index";
|
|
17
17
|
import { resolveFromCwd, getRootDir } from "../util/fs";
|
|
18
|
-
import path from "path";
|
|
18
|
+
import path from "path";
|
|
19
19
|
import fs from "fs/promises";
|
|
20
20
|
|
|
21
|
-
export interface DoctorOptions {
|
|
22
|
-
/** Output format: console, json, or markdown */
|
|
23
|
-
format?: "console" | "json" | "markdown";
|
|
21
|
+
export interface DoctorOptions {
|
|
22
|
+
/** Output format: console, json, or markdown */
|
|
23
|
+
format?: "console" | "json" | "markdown";
|
|
24
24
|
/** Whether to use LLM for enhanced analysis */
|
|
25
25
|
useLLM?: boolean;
|
|
26
26
|
/** Output file path (for json/markdown formats) */
|
|
27
|
-
output?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function doctor(options: DoctorOptions = {}): Promise<boolean> {
|
|
31
|
-
const { format, useLLM = true, output } = options;
|
|
32
|
-
const inferredFormat = format ?? (output ? (path.extname(output).toLowerCase() === ".json" ? "json" : "markdown") : undefined);
|
|
33
|
-
const resolvedFormat = inferredFormat ?? "console";
|
|
27
|
+
output?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function doctor(options: DoctorOptions = {}): Promise<boolean> {
|
|
31
|
+
const { format, useLLM = true, output } = options;
|
|
32
|
+
const inferredFormat = format ?? (output ? (path.extname(output).toLowerCase() === ".json" ? "json" : "markdown") : undefined);
|
|
33
|
+
const resolvedFormat = inferredFormat ?? "console";
|
|
34
34
|
|
|
35
35
|
const specPath = resolveFromCwd("spec/routes.manifest.json");
|
|
36
36
|
const rootDir = getRootDir();
|
|
@@ -82,12 +82,12 @@ export async function doctor(options: DoctorOptions = {}): Promise<boolean> {
|
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
// Output based on format
|
|
85
|
-
switch (resolvedFormat) {
|
|
86
|
-
case "console":
|
|
87
|
-
printDoctorReport(analysis);
|
|
88
|
-
break;
|
|
89
|
-
|
|
90
|
-
case "json": {
|
|
85
|
+
switch (resolvedFormat) {
|
|
86
|
+
case "console":
|
|
87
|
+
printDoctorReport(analysis);
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case "json": {
|
|
91
91
|
const json = JSON.stringify(
|
|
92
92
|
{
|
|
93
93
|
summary: analysis.summary,
|
|
@@ -106,10 +106,10 @@ export async function doctor(options: DoctorOptions = {}): Promise<boolean> {
|
|
|
106
106
|
} else {
|
|
107
107
|
console.log(json);
|
|
108
108
|
}
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
case "markdown": {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case "markdown": {
|
|
113
113
|
const md = generateDoctorMarkdownReport(analysis);
|
|
114
114
|
|
|
115
115
|
if (output) {
|
|
@@ -118,9 +118,9 @@ export async function doctor(options: DoctorOptions = {}): Promise<boolean> {
|
|
|
118
118
|
} else {
|
|
119
119
|
console.log(md);
|
|
120
120
|
}
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return false;
|
|
126
|
+
}
|