@mandujs/core 0.3.2 → 0.3.3
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 +200 -200
- package/README.md +200 -200
- package/package.json +4 -2
- package/src/change/history.ts +145 -0
- package/src/change/index.ts +40 -0
- package/src/change/integrity.ts +81 -0
- package/src/change/snapshot.ts +233 -0
- package/src/change/transaction.ts +293 -0
- package/src/change/types.ts +102 -0
- package/src/error/classifier.ts +314 -0
- package/src/error/formatter.ts +237 -0
- package/src/error/index.ts +25 -0
- package/src/error/stack-analyzer.ts +295 -0
- package/src/error/types.ts +140 -0
- package/src/filling/context.ts +228 -219
- package/src/filling/filling.ts +256 -234
- package/src/filling/index.ts +7 -7
- package/src/generator/generate.ts +85 -3
- package/src/generator/index.ts +2 -2
- package/src/guard/auto-correct.ts +257 -203
- package/src/index.ts +2 -0
- package/src/report/index.ts +1 -1
- package/src/runtime/index.ts +3 -3
- package/src/runtime/router.ts +65 -65
- package/src/runtime/server.ts +189 -139
- package/src/runtime/ssr.ts +38 -38
- package/src/spec/index.ts +3 -3
- package/src/spec/load.ts +76 -76
- package/src/spec/lock.ts +56 -56
package/src/filling/filling.ts
CHANGED
|
@@ -1,234 +1,256 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu Filling - 만두소 🥟
|
|
3
|
-
* 체이닝 API로 비즈니스 로직 정의
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ManduContext, NEXT_SYMBOL, ValidationError } from "./context";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* .
|
|
30
|
-
* .
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
all
|
|
88
|
-
|
|
89
|
-
methods
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* .guard(authCheck
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
guards.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
*
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
*
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Filling - 만두소 🥟
|
|
3
|
+
* 체이닝 API로 비즈니스 로직 정의
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ManduContext, NEXT_SYMBOL, ValidationError } from "./context";
|
|
7
|
+
import { ErrorClassifier, formatErrorResponse, ErrorCode } from "../error";
|
|
8
|
+
|
|
9
|
+
/** Handler function type */
|
|
10
|
+
export type Handler = (ctx: ManduContext) => Response | Promise<Response>;
|
|
11
|
+
|
|
12
|
+
/** Guard function type - returns next() or Response */
|
|
13
|
+
export type Guard = (ctx: ManduContext) => symbol | Response | Promise<symbol | Response>;
|
|
14
|
+
|
|
15
|
+
/** HTTP methods */
|
|
16
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
17
|
+
|
|
18
|
+
interface FillingConfig {
|
|
19
|
+
handlers: Map<HttpMethod, Handler>;
|
|
20
|
+
guards: Guard[];
|
|
21
|
+
methodGuards: Map<HttpMethod, Guard[]>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Mandu Filling Builder
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* export default Mandu.filling()
|
|
29
|
+
* .guard(authCheck)
|
|
30
|
+
* .get(ctx => ctx.ok({ message: 'Hello!' }))
|
|
31
|
+
* .post(ctx => ctx.created({ id: 1 }))
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class ManduFilling {
|
|
35
|
+
private config: FillingConfig = {
|
|
36
|
+
handlers: new Map(),
|
|
37
|
+
guards: [],
|
|
38
|
+
methodGuards: new Map(),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// 🥟 HTTP Method Handlers
|
|
43
|
+
// ============================================
|
|
44
|
+
|
|
45
|
+
/** Handle GET requests */
|
|
46
|
+
get(handler: Handler): this {
|
|
47
|
+
this.config.handlers.set("GET", handler);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Handle POST requests */
|
|
52
|
+
post(handler: Handler): this {
|
|
53
|
+
this.config.handlers.set("POST", handler);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Handle PUT requests */
|
|
58
|
+
put(handler: Handler): this {
|
|
59
|
+
this.config.handlers.set("PUT", handler);
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Handle PATCH requests */
|
|
64
|
+
patch(handler: Handler): this {
|
|
65
|
+
this.config.handlers.set("PATCH", handler);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Handle DELETE requests */
|
|
70
|
+
delete(handler: Handler): this {
|
|
71
|
+
this.config.handlers.set("DELETE", handler);
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Handle HEAD requests */
|
|
76
|
+
head(handler: Handler): this {
|
|
77
|
+
this.config.handlers.set("HEAD", handler);
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Handle OPTIONS requests */
|
|
82
|
+
options(handler: Handler): this {
|
|
83
|
+
this.config.handlers.set("OPTIONS", handler);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Handle all methods with single handler */
|
|
88
|
+
all(handler: Handler): this {
|
|
89
|
+
const methods: HttpMethod[] = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
90
|
+
methods.forEach((method) => this.config.handlers.set(method, handler));
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ============================================
|
|
95
|
+
// 🥟 Guards (만두 찜기)
|
|
96
|
+
// ============================================
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Add guard for all methods or specific methods
|
|
100
|
+
* @example
|
|
101
|
+
* .guard(authCheck) // all methods
|
|
102
|
+
* .guard(authCheck, 'POST', 'PUT') // specific methods
|
|
103
|
+
*/
|
|
104
|
+
guard(guardFn: Guard, ...methods: HttpMethod[]): this {
|
|
105
|
+
if (methods.length === 0) {
|
|
106
|
+
// Apply to all methods
|
|
107
|
+
this.config.guards.push(guardFn);
|
|
108
|
+
} else {
|
|
109
|
+
// Apply to specific methods
|
|
110
|
+
methods.forEach((method) => {
|
|
111
|
+
const guards = this.config.methodGuards.get(method) || [];
|
|
112
|
+
guards.push(guardFn);
|
|
113
|
+
this.config.methodGuards.set(method, guards);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Alias for guard - more semantic for middleware */
|
|
120
|
+
use(guardFn: Guard, ...methods: HttpMethod[]): this {
|
|
121
|
+
return this.guard(guardFn, ...methods);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ============================================
|
|
125
|
+
// 🥟 Execution
|
|
126
|
+
// ============================================
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Handle incoming request
|
|
130
|
+
* Called by generated route handler
|
|
131
|
+
* @param request The incoming request
|
|
132
|
+
* @param params URL path parameters
|
|
133
|
+
* @param routeContext Route context for error reporting
|
|
134
|
+
*/
|
|
135
|
+
async handle(
|
|
136
|
+
request: Request,
|
|
137
|
+
params: Record<string, string> = {},
|
|
138
|
+
routeContext?: { routeId: string; pattern: string }
|
|
139
|
+
): Promise<Response> {
|
|
140
|
+
const ctx = new ManduContext(request, params);
|
|
141
|
+
const method = request.method.toUpperCase() as HttpMethod;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Run global guards
|
|
145
|
+
for (const guard of this.config.guards) {
|
|
146
|
+
const result = await guard(ctx);
|
|
147
|
+
if (result !== NEXT_SYMBOL) {
|
|
148
|
+
return result as Response;
|
|
149
|
+
}
|
|
150
|
+
if (!ctx.shouldContinue) {
|
|
151
|
+
return ctx.getResponse()!;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Run method-specific guards
|
|
156
|
+
const methodGuards = this.config.methodGuards.get(method) || [];
|
|
157
|
+
for (const guard of methodGuards) {
|
|
158
|
+
const result = await guard(ctx);
|
|
159
|
+
if (result !== NEXT_SYMBOL) {
|
|
160
|
+
return result as Response;
|
|
161
|
+
}
|
|
162
|
+
if (!ctx.shouldContinue) {
|
|
163
|
+
return ctx.getResponse()!;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Get handler for method
|
|
168
|
+
const handler = this.config.handlers.get(method);
|
|
169
|
+
if (!handler) {
|
|
170
|
+
return ctx.json(
|
|
171
|
+
{
|
|
172
|
+
status: "error",
|
|
173
|
+
message: `Method ${method} not allowed`,
|
|
174
|
+
allowed: Array.from(this.config.handlers.keys()),
|
|
175
|
+
},
|
|
176
|
+
405
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Execute handler
|
|
181
|
+
return await handler(ctx);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
// Handle validation errors with enhanced error format
|
|
184
|
+
if (error instanceof ValidationError) {
|
|
185
|
+
return ctx.json(
|
|
186
|
+
{
|
|
187
|
+
errorType: "LOGIC_ERROR",
|
|
188
|
+
code: ErrorCode.SLOT_VALIDATION_ERROR,
|
|
189
|
+
message: "Validation failed",
|
|
190
|
+
summary: "입력 검증 실패 - 요청 데이터 확인 필요",
|
|
191
|
+
fix: {
|
|
192
|
+
file: routeContext ? `spec/slots/${routeContext.routeId}.slot.ts` : "spec/slots/",
|
|
193
|
+
suggestion: "요청 데이터가 스키마와 일치하는지 확인하세요",
|
|
194
|
+
},
|
|
195
|
+
route: routeContext,
|
|
196
|
+
errors: error.errors,
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
198
|
+
},
|
|
199
|
+
400
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Handle other errors with error classification
|
|
204
|
+
const classifier = new ErrorClassifier(null, routeContext);
|
|
205
|
+
const manduError = classifier.classify(error);
|
|
206
|
+
|
|
207
|
+
console.error(`[Mandu] ${manduError.errorType}:`, manduError.message);
|
|
208
|
+
|
|
209
|
+
const response = formatErrorResponse(manduError, {
|
|
210
|
+
isDev: process.env.NODE_ENV !== "production",
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return ctx.json(response, 500);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get list of registered methods
|
|
219
|
+
*/
|
|
220
|
+
getMethods(): HttpMethod[] {
|
|
221
|
+
return Array.from(this.config.handlers.keys());
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Check if method is registered
|
|
226
|
+
*/
|
|
227
|
+
hasMethod(method: HttpMethod): boolean {
|
|
228
|
+
return this.config.handlers.has(method);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Mandu namespace with factory methods
|
|
234
|
+
*/
|
|
235
|
+
export const Mandu = {
|
|
236
|
+
/**
|
|
237
|
+
* Create a new filling (slot logic builder)
|
|
238
|
+
* @example
|
|
239
|
+
* ```typescript
|
|
240
|
+
* import { Mandu } from '@mandujs/core'
|
|
241
|
+
*
|
|
242
|
+
* export default Mandu.filling()
|
|
243
|
+
* .get(ctx => ctx.ok({ message: 'Hello!' }))
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
filling(): ManduFilling {
|
|
247
|
+
return new ManduFilling();
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Create context manually (for testing)
|
|
252
|
+
*/
|
|
253
|
+
context(request: Request, params?: Record<string, string>): ManduContext {
|
|
254
|
+
return new ManduContext(request, params);
|
|
255
|
+
},
|
|
256
|
+
};
|
package/src/filling/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mandu Filling Module - 만두소 🥟
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { ManduContext, NEXT_SYMBOL, ValidationError } from "./context";
|
|
6
|
-
export { ManduFilling, Mandu } from "./filling";
|
|
7
|
-
export type { Handler, Guard, HttpMethod } from "./filling";
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Filling Module - 만두소 🥟
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { ManduContext, NEXT_SYMBOL, ValidationError } from "./context";
|
|
6
|
+
export { ManduFilling, Mandu } from "./filling";
|
|
7
|
+
export type { Handler, Guard, HttpMethod } from "./filling";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RoutesManifest, RouteSpec } from "../spec/schema";
|
|
2
2
|
import { generateApiHandler, generatePageComponent, generateSlotLogic } from "./templates";
|
|
3
|
+
import { computeHash } from "../spec/lock";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import fs from "fs/promises";
|
|
5
6
|
|
|
@@ -20,10 +21,64 @@ export interface GenerateResult {
|
|
|
20
21
|
errors: string[];
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Spec 파일 정보
|
|
26
|
+
*/
|
|
27
|
+
export interface SpecSource {
|
|
28
|
+
/** Spec 파일 경로 */
|
|
29
|
+
path: string;
|
|
30
|
+
/** SHA256 해시 */
|
|
31
|
+
hash: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Spec 내 라우트 위치 정보
|
|
36
|
+
*/
|
|
37
|
+
export interface SpecLocation {
|
|
38
|
+
/** Spec 파일 경로 */
|
|
39
|
+
file: string;
|
|
40
|
+
/** routes 배열 내 인덱스 */
|
|
41
|
+
routeIndex: number;
|
|
42
|
+
/** JSON 경로 (예: "routes[0]") */
|
|
43
|
+
jsonPath: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Slot 파일 매핑 정보
|
|
48
|
+
*/
|
|
49
|
+
export interface SlotMapping {
|
|
50
|
+
/** Slot 파일 경로 */
|
|
51
|
+
slotPath: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generated 파일 엔트리
|
|
56
|
+
*/
|
|
57
|
+
export interface GeneratedFileEntry {
|
|
58
|
+
/** 라우트 ID */
|
|
59
|
+
routeId: string;
|
|
60
|
+
/** 라우트 종류 */
|
|
61
|
+
kind: "api" | "page";
|
|
62
|
+
/** Spec 내 위치 */
|
|
63
|
+
specLocation: SpecLocation;
|
|
64
|
+
/** Slot 매핑 (있는 경우) */
|
|
65
|
+
slotMapping?: SlotMapping;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Generated Map 구조
|
|
70
|
+
*/
|
|
23
71
|
export interface GeneratedMap {
|
|
72
|
+
/** 버전 */
|
|
24
73
|
version: number;
|
|
74
|
+
/** 생성 시각 */
|
|
25
75
|
generatedAt: string;
|
|
26
|
-
|
|
76
|
+
/** Spec 소스 정보 */
|
|
77
|
+
specSource: SpecSource;
|
|
78
|
+
/** 생성된 파일 매핑 */
|
|
79
|
+
files: Record<string, GeneratedFileEntry>;
|
|
80
|
+
/** 프레임워크 내부 파일 패턴 */
|
|
81
|
+
frameworkPaths: string[];
|
|
27
82
|
}
|
|
28
83
|
|
|
29
84
|
async function ensureDir(dirPath: string): Promise<void> {
|
|
@@ -66,14 +121,37 @@ export async function generateRoutes(
|
|
|
66
121
|
const generatedMap: GeneratedMap = {
|
|
67
122
|
version: manifest.version,
|
|
68
123
|
generatedAt: new Date().toISOString(),
|
|
124
|
+
specSource: {
|
|
125
|
+
path: "spec/routes.manifest.json",
|
|
126
|
+
hash: computeHash(manifest),
|
|
127
|
+
},
|
|
69
128
|
files: {},
|
|
129
|
+
frameworkPaths: [
|
|
130
|
+
"@mandujs/core",
|
|
131
|
+
"packages/core/src",
|
|
132
|
+
"node_modules/@mandujs",
|
|
133
|
+
],
|
|
70
134
|
};
|
|
71
135
|
|
|
72
136
|
const expectedServerFiles = new Set<string>();
|
|
73
137
|
const expectedWebFiles = new Set<string>();
|
|
74
138
|
|
|
75
|
-
for (
|
|
139
|
+
for (let routeIndex = 0; routeIndex < manifest.routes.length; routeIndex++) {
|
|
140
|
+
const route = manifest.routes[routeIndex];
|
|
141
|
+
|
|
76
142
|
try {
|
|
143
|
+
// Spec 위치 정보
|
|
144
|
+
const specLocation: SpecLocation = {
|
|
145
|
+
file: "spec/routes.manifest.json",
|
|
146
|
+
routeIndex,
|
|
147
|
+
jsonPath: `routes[${routeIndex}]`,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Slot 매핑 정보 (있는 경우)
|
|
151
|
+
const slotMapping: SlotMapping | undefined = route.slotModule
|
|
152
|
+
? { slotPath: route.slotModule }
|
|
153
|
+
: undefined;
|
|
154
|
+
|
|
77
155
|
// Server handler
|
|
78
156
|
const serverFileName = `${route.id}.route.ts`;
|
|
79
157
|
const serverFilePath = path.join(serverRoutesDir, serverFileName);
|
|
@@ -85,7 +163,9 @@ export async function generateRoutes(
|
|
|
85
163
|
|
|
86
164
|
generatedMap.files[`apps/server/generated/routes/${serverFileName}`] = {
|
|
87
165
|
routeId: route.id,
|
|
88
|
-
kind: route.kind,
|
|
166
|
+
kind: route.kind as "api" | "page",
|
|
167
|
+
specLocation,
|
|
168
|
+
slotMapping,
|
|
89
169
|
};
|
|
90
170
|
|
|
91
171
|
// Slot file (only if slotModule is specified)
|
|
@@ -119,6 +199,8 @@ export async function generateRoutes(
|
|
|
119
199
|
generatedMap.files[`apps/web/generated/routes/${webFileName}`] = {
|
|
120
200
|
routeId: route.id,
|
|
121
201
|
kind: route.kind,
|
|
202
|
+
specLocation,
|
|
203
|
+
slotMapping,
|
|
122
204
|
};
|
|
123
205
|
}
|
|
124
206
|
} catch (error) {
|
package/src/generator/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./generate";
|
|
2
|
-
export * from "./templates";
|
|
1
|
+
export * from "./generate";
|
|
2
|
+
export * from "./templates";
|