@goboost/scanner-typescript 1.2.1 → 1.2.2

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.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Controller extractor - finds @HybridRoute + @Controller decorated classes
3
+ * Extracts basePath, method decorators, handler names, path/query params, and guard decorators
4
+ */
5
+ import { Project } from 'ts-morph';
6
+ interface ControllerSpec {
7
+ name: string;
8
+ basePath: string;
9
+ sourceFile: string;
10
+ guards: string[];
11
+ dependencies: string[];
12
+ routes: RouteMapping[];
13
+ unsupportedFeatures: string[];
14
+ hasHybridRoute: boolean;
15
+ }
16
+ interface RouteMapping {
17
+ id: string;
18
+ httpMethod: string;
19
+ path: string;
20
+ handlerName: string;
21
+ pathParams: ParamDef[];
22
+ queryParams: ParamDef[];
23
+ requestBody: DtoRef | null;
24
+ responseBody: DtoRef | null;
25
+ statusCode: number;
26
+ guards: string[];
27
+ unsupportedFeatures: string[];
28
+ canGenerate: boolean;
29
+ }
30
+ interface DtoRef {
31
+ $ref: string;
32
+ }
33
+ interface ParamDef {
34
+ name: string;
35
+ type: string;
36
+ isRequired: boolean;
37
+ }
38
+ export declare function extractControllers(project: Project): ControllerSpec[];
39
+ export {};
40
+ //# sourceMappingURL=controller-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-extractor.d.ts","sourceRoot":"","sources":["../src/controller-extractor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAA0F,MAAM,UAAU,CAAC;AAE3H,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;CACrB;AAaD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,EAAE,CAgBrE"}
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractControllers = extractControllers;
4
+ /**
5
+ * Controller extractor - finds @HybridRoute + @Controller decorated classes
6
+ * Extracts basePath, method decorators, handler names, path/query params, and guard decorators
7
+ */
8
+ const ts_morph_1 = require("ts-morph");
9
+ const HTTP_METHOD_DECORATORS = ['Get', 'Post', 'Put', 'Delete', 'Patch'];
10
+ // Status code mapping based on HTTP method
11
+ const DEFAULT_STATUS_CODES = {
12
+ 'GET': 200,
13
+ 'POST': 201,
14
+ 'PUT': 200,
15
+ 'PATCH': 200,
16
+ 'DELETE': 200,
17
+ };
18
+ function extractControllers(project) {
19
+ const controllers = [];
20
+ for (const sourceFile of project.getSourceFiles()) {
21
+ for (const classDecl of sourceFile.getClasses()) {
22
+ if (hasHybridRouteDecorator(classDecl)) {
23
+ const controller = extractController(classDecl, sourceFile);
24
+ controllers.push(controller);
25
+ }
26
+ }
27
+ }
28
+ // Sort controllers alphabetically by name for determinism
29
+ controllers.sort((a, b) => a.name.localeCompare(b.name));
30
+ return controllers;
31
+ }
32
+ function hasHybridRouteDecorator(classDecl) {
33
+ const decorators = classDecl.getDecorators();
34
+ return decorators.some(d => d.getName() === 'HybridRoute');
35
+ }
36
+ function extractController(classDecl, sourceFile) {
37
+ const name = classDecl.getName() || 'AnonymousController';
38
+ const basePath = extractBasePath(classDecl);
39
+ const classGuards = extractClassGuards(classDecl);
40
+ const dependencies = extractDependencies(classDecl);
41
+ const { routes, unsupportedFeatures } = extractRoutes(classDecl, name, classGuards);
42
+ return {
43
+ name,
44
+ basePath,
45
+ sourceFile: getRelativePath(sourceFile.getFilePath()),
46
+ guards: classGuards,
47
+ dependencies,
48
+ routes,
49
+ unsupportedFeatures,
50
+ hasHybridRoute: true,
51
+ };
52
+ }
53
+ function extractBasePath(classDecl) {
54
+ const controllerDecorator = classDecl.getDecorators().find(d => d.getName() === 'Controller');
55
+ if (!controllerDecorator)
56
+ return '';
57
+ const args = controllerDecorator.getArguments();
58
+ if (args.length > 0) {
59
+ const arg = args[0];
60
+ if (ts_morph_1.Node.isStringLiteral(arg)) {
61
+ return arg.getText().replace(/['"]/g, '');
62
+ }
63
+ }
64
+ return '';
65
+ }
66
+ function extractClassGuards(classDecl) {
67
+ const guards = [];
68
+ const useGuardsDecorator = classDecl.getDecorators().find(d => d.getName() === 'UseGuards');
69
+ if (useGuardsDecorator) {
70
+ const args = useGuardsDecorator.getArguments();
71
+ for (const arg of args) {
72
+ const guardName = arg.getText().replace('()', '').trim();
73
+ guards.push(guardName);
74
+ }
75
+ }
76
+ return guards;
77
+ }
78
+ function extractDependencies(classDecl) {
79
+ const dependencies = [];
80
+ for (const prop of classDecl.getProperties()) {
81
+ const type = prop.getType().getText();
82
+ // Extract service names from injected properties
83
+ if (type.includes('Service')) {
84
+ dependencies.push(type);
85
+ }
86
+ }
87
+ return dependencies;
88
+ }
89
+ function extractRoutes(classDecl, controllerName, classGuards) {
90
+ const routes = [];
91
+ const controllerUnsupported = [];
92
+ for (const method of classDecl.getMethods()) {
93
+ for (const decoratorName of HTTP_METHOD_DECORATORS) {
94
+ const httpDecorator = method.getDecorators().find(d => d.getName() === decoratorName);
95
+ if (httpDecorator) {
96
+ const routePath = extractDecoratorPath(httpDecorator);
97
+ const methodGuards = extractMethodGuards(method);
98
+ const allGuards = [...new Set([...classGuards, ...methodGuards])];
99
+ const route = {
100
+ id: `${controllerName}:${decoratorName.toUpperCase()}:${routePath || '/'}`,
101
+ httpMethod: decoratorName.toUpperCase(),
102
+ path: routePath || '/',
103
+ handlerName: method.getName(),
104
+ pathParams: extractPathParams(method),
105
+ queryParams: extractQueryParams(method),
106
+ requestBody: extractRequestBody(method),
107
+ responseBody: extractResponseBody(method),
108
+ statusCode: extractStatusCode(method, decoratorName.toUpperCase()),
109
+ guards: methodGuards, // Only route-level guards
110
+ unsupportedFeatures: [],
111
+ canGenerate: true,
112
+ };
113
+ routes.push(route);
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ // Sort routes by method then path for determinism
119
+ routes.sort((a, b) => {
120
+ const methodCompare = a.httpMethod.localeCompare(b.httpMethod);
121
+ if (methodCompare !== 0)
122
+ return methodCompare;
123
+ return a.path.localeCompare(b.path);
124
+ });
125
+ // Edge case: Empty controller (no routes)
126
+ // Add warning flag if controller has @HybridRoute but no HTTP method decorators
127
+ if (routes.length === 0) {
128
+ controllerUnsupported.push('empty-controller');
129
+ }
130
+ return { routes, unsupportedFeatures: controllerUnsupported };
131
+ }
132
+ function extractDecoratorPath(decorator) {
133
+ const args = decorator.getArguments();
134
+ if (args.length > 0) {
135
+ const arg = args[0];
136
+ if (ts_morph_1.Node.isStringLiteral(arg)) {
137
+ return arg.getText().replace(/['"]/g, '');
138
+ }
139
+ }
140
+ return '';
141
+ }
142
+ function extractPathParams(method) {
143
+ const params = [];
144
+ for (const param of method.getParameters()) {
145
+ const paramDecorator = param.getDecorators().find(d => d.getName() === 'Param');
146
+ if (paramDecorator) {
147
+ const args = paramDecorator.getArguments();
148
+ const paramName = args.length > 0 && ts_morph_1.Node.isStringLiteral(args[0])
149
+ ? args[0].getText().replace(/['"]/g, '')
150
+ : param.getName();
151
+ params.push({
152
+ name: paramName,
153
+ type: mapTypeToGo(param.getType().getText()),
154
+ isRequired: true,
155
+ });
156
+ }
157
+ }
158
+ return params;
159
+ }
160
+ function extractQueryParams(method) {
161
+ const params = [];
162
+ for (const param of method.getParameters()) {
163
+ const queryDecorator = param.getDecorators().find(d => d.getName() === 'Query');
164
+ if (queryDecorator) {
165
+ const args = queryDecorator.getArguments();
166
+ const paramName = args.length > 0 && ts_morph_1.Node.isStringLiteral(args[0])
167
+ ? args[0].getText().replace(/['"]/g, '')
168
+ : param.getName();
169
+ params.push({
170
+ name: paramName,
171
+ type: mapTypeToGo(param.getType().getText()),
172
+ isRequired: !param.hasQuestionToken(),
173
+ });
174
+ }
175
+ }
176
+ return params;
177
+ }
178
+ function extractRequestBody(method) {
179
+ for (const param of method.getParameters()) {
180
+ const bodyDecorator = param.getDecorators().find(d => d.getName() === 'Body');
181
+ if (bodyDecorator) {
182
+ const type = param.getType();
183
+ const typeName = extractTypeName(type.getText());
184
+ if (typeName) {
185
+ return { $ref: typeName };
186
+ }
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+ function extractResponseBody(method) {
192
+ const returnType = method.getReturnType();
193
+ const returnText = returnType.getText();
194
+ // Extract from Promise<T> or Observable<T>
195
+ const promiseMatch = returnText.match(/Promise<(.+)>/);
196
+ const observableMatch = returnText.match(/Observable<(.+)>/);
197
+ let typeName = returnText;
198
+ if (promiseMatch) {
199
+ typeName = promiseMatch[1];
200
+ }
201
+ else if (observableMatch) {
202
+ typeName = observableMatch[1];
203
+ }
204
+ // Skip void and primitive types
205
+ if (typeName === 'void' || isPrimitiveType(typeName)) {
206
+ return null;
207
+ }
208
+ const cleanName = extractTypeName(typeName);
209
+ if (cleanName) {
210
+ return { $ref: cleanName };
211
+ }
212
+ return null;
213
+ }
214
+ function extractStatusCode(method, httpMethod) {
215
+ const httpCodeDecorator = method.getDecorators().find(d => d.getName() === 'HttpCode');
216
+ if (httpCodeDecorator) {
217
+ const args = httpCodeDecorator.getArguments();
218
+ if (args.length > 0) {
219
+ const code = parseInt(args[0].getText(), 10);
220
+ if (!isNaN(code)) {
221
+ return code;
222
+ }
223
+ }
224
+ }
225
+ return DEFAULT_STATUS_CODES[httpMethod] || 200;
226
+ }
227
+ function extractMethodGuards(method) {
228
+ const guards = [];
229
+ const useGuardsDecorator = method.getDecorators().find(d => d.getName() === 'UseGuards');
230
+ if (useGuardsDecorator) {
231
+ const args = useGuardsDecorator.getArguments();
232
+ for (const arg of args) {
233
+ const guardName = arg.getText().replace('()', '').trim();
234
+ guards.push(guardName);
235
+ }
236
+ }
237
+ return guards;
238
+ }
239
+ function extractTypeName(typeText) {
240
+ // Handle arrays
241
+ if (typeText.endsWith('[]')) {
242
+ return extractTypeName(typeText.slice(0, -2));
243
+ }
244
+ // Handle generics like Array<T>
245
+ const arrayMatch = typeText.match(/Array<(.+)>/);
246
+ if (arrayMatch) {
247
+ return extractTypeName(arrayMatch[1]);
248
+ }
249
+ // Skip primitives
250
+ if (isPrimitiveType(typeText)) {
251
+ return null;
252
+ }
253
+ // Clean up type name (remove imports path, etc.)
254
+ const cleanName = typeText.split('.').pop()?.split('<')[0].trim();
255
+ return cleanName || null;
256
+ }
257
+ function isPrimitiveType(type) {
258
+ const primitives = [
259
+ 'string', 'number', 'boolean', 'void', 'any', 'unknown',
260
+ 'null', 'undefined', 'object', 'Date', 'Buffer',
261
+ 'Promise<void>', 'Observable<void>'
262
+ ];
263
+ return primitives.some(p => type === p || type.includes(p + '<') || type === p + '[]');
264
+ }
265
+ function mapTypeToGo(tsType) {
266
+ const typeMap = {
267
+ 'string': 'string',
268
+ 'number': 'float64',
269
+ 'boolean': 'bool',
270
+ 'Date': 'time.Time',
271
+ };
272
+ for (const [ts, go] of Object.entries(typeMap)) {
273
+ if (tsType.includes(ts)) {
274
+ return go;
275
+ }
276
+ }
277
+ return 'any';
278
+ }
279
+ function getRelativePath(filePath) {
280
+ // Convert absolute path to relative from project root
281
+ const parts = filePath.split(/[\\/]/);
282
+ const srcIndex = parts.indexOf('src');
283
+ if (srcIndex !== -1) {
284
+ return parts.slice(srcIndex).join('/');
285
+ }
286
+ return filePath;
287
+ }
288
+ //# sourceMappingURL=controller-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-extractor.js","sourceRoot":"","sources":["../src/controller-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * DTO extractor - finds DTO classes referenced by controller methods
3
+ * Extracts field names, TypeScript types, class-validator decorators, and nested DTO relationships
4
+ */
5
+ import { Project } from 'ts-morph';
6
+ interface DTODefinition {
7
+ name: string;
8
+ sourceFile: string;
9
+ extends: string | null;
10
+ fields: DTOField[];
11
+ isShared: boolean;
12
+ }
13
+ interface DTOField {
14
+ name: string;
15
+ tsType: string;
16
+ isOptional: boolean;
17
+ isArray: boolean;
18
+ nestedDto: string | null;
19
+ validationRules: ValidationRule[];
20
+ }
21
+ interface ValidationRule {
22
+ decorator: string;
23
+ args: any[];
24
+ message: string | null;
25
+ }
26
+ export declare function extractDTOs(project: Project, referencedDTOs: Set<string>): Record<string, DTODefinition>;
27
+ export declare function collectReferencedDTOs(controllers: any[]): Set<string>;
28
+ export declare function markSharedDTOs(dtos: Record<string, DTODefinition>, controllers: any[]): void;
29
+ export {};
30
+ //# sourceMappingURL=dto-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto-extractor.d.ts","sourceRoot":"","sources":["../src/dto-extractor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAsE,MAAM,UAAU,CAAC;AAEvG,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,EAAE,cAAc,EAAE,CAAC;CACnC;AAED,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAoCD,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CA8BxG;AAkOD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAerE;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,IAAI,CAyB5F"}
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractDTOs = extractDTOs;
4
+ exports.collectReferencedDTOs = collectReferencedDTOs;
5
+ exports.markSharedDTOs = markSharedDTOs;
6
+ /**
7
+ * DTO extractor - finds DTO classes referenced by controller methods
8
+ * Extracts field names, TypeScript types, class-validator decorators, and nested DTO relationships
9
+ */
10
+ const ts_morph_1 = require("ts-morph");
11
+ const VALIDATION_DECORATORS = [
12
+ 'IsString', 'IsNumber', 'IsEmail', 'IsInt', 'IsBoolean',
13
+ 'Min', 'Max', 'MinLength', 'MaxLength', 'IsOptional',
14
+ 'IsDate', 'IsArray', 'IsEnum', 'IsUUID', 'IsUrl',
15
+ 'Matches', 'IsNotEmpty', 'IsDefined', 'IsPositive',
16
+ 'IsNegative', 'IsDateString', 'IsISO8601', 'IsJSON',
17
+ 'IsObject', 'IsNotEmptyObject', 'ValidateNested',
18
+ ];
19
+ // Map class-validator decorators to Go validate tags
20
+ const VALIDATOR_TO_GO_TAG = {
21
+ 'IsString': '',
22
+ 'IsNumber': '',
23
+ 'IsEmail': 'email',
24
+ 'IsInt': '',
25
+ 'IsBoolean': '',
26
+ 'Min': 'min',
27
+ 'Max': 'max',
28
+ 'MinLength': 'min',
29
+ 'MaxLength': 'max',
30
+ 'IsOptional': 'omitempty',
31
+ 'IsDate': '',
32
+ 'IsArray': '',
33
+ 'IsEnum': 'oneof',
34
+ 'IsUUID': 'uuid',
35
+ 'IsUrl': 'url',
36
+ 'Matches': 'regexp',
37
+ 'IsNotEmpty': 'required',
38
+ 'IsDefined': 'required',
39
+ 'IsPositive': 'gt=0',
40
+ 'IsDateString': '',
41
+ 'IsJSON': '',
42
+ };
43
+ function extractDTOs(project, referencedDTOs) {
44
+ const dtos = {};
45
+ const processedDTOs = new Set();
46
+ // First pass: collect all DTO classes
47
+ const allDTOClasses = [];
48
+ for (const sourceFile of project.getSourceFiles()) {
49
+ for (const classDecl of sourceFile.getClasses()) {
50
+ const name = classDecl.getName();
51
+ if (name && (isDTO(classDecl) || referencedDTOs.has(name))) {
52
+ allDTOClasses.push({ classDecl, sourceFile });
53
+ }
54
+ }
55
+ }
56
+ // Second pass: extract DTO definitions
57
+ for (const { classDecl, sourceFile } of allDTOClasses) {
58
+ const name = classDecl.getName();
59
+ if (name && !processedDTOs.has(name)) {
60
+ const dto = extractDTO(classDecl, sourceFile, referencedDTOs);
61
+ dtos[name] = dto;
62
+ processedDTOs.add(name);
63
+ }
64
+ }
65
+ // Mark shared DTOs (referenced by multiple controllers)
66
+ // This will be done by the caller who has knowledge of controller references
67
+ return dtos;
68
+ }
69
+ function isDTO(classDecl) {
70
+ // Check if class has validation decorators on properties
71
+ for (const prop of classDecl.getProperties()) {
72
+ const decorators = prop.getDecorators();
73
+ if (decorators.some(d => VALIDATION_DECORATORS.includes(d.getName()))) {
74
+ return true;
75
+ }
76
+ }
77
+ // Also check if class name ends with 'Dto' or 'DTO'
78
+ const name = classDecl.getName() || '';
79
+ return name.endsWith('Dto') || name.endsWith('DTO');
80
+ }
81
+ function extractDTO(classDecl, sourceFile, referencedDTOs) {
82
+ const name = classDecl.getName() || 'AnonymousDTO';
83
+ const fields = extractFields(classDecl, referencedDTOs);
84
+ const extendsClass = extractExtends(classDecl);
85
+ return {
86
+ name,
87
+ sourceFile: getRelativePath(sourceFile.getFilePath()),
88
+ extends: extendsClass,
89
+ fields,
90
+ isShared: false, // Will be determined by caller
91
+ };
92
+ }
93
+ function extractExtends(classDecl) {
94
+ const ext = classDecl.getExtends();
95
+ if (ext) {
96
+ const text = ext.getText();
97
+ // Extract just the class name, not the full path
98
+ return text.split('.').pop() || text;
99
+ }
100
+ return null;
101
+ }
102
+ function extractFields(classDecl, referencedDTOs) {
103
+ const fields = [];
104
+ for (const prop of classDecl.getProperties()) {
105
+ const typeText = prop.getType().getText();
106
+ const isArray = typeText.includes('[]') || typeText.includes('Array<');
107
+ const baseType = extractBaseType(typeText);
108
+ // Check for unmappable custom types
109
+ let unmappableType = null;
110
+ if (!isPrimitiveType(baseType) && !isBuiltInType(baseType) && !referencedDTOs.has(baseType)) {
111
+ // Check if it's a known framework type
112
+ if (!isKnownType(baseType)) {
113
+ unmappableType = baseType;
114
+ }
115
+ }
116
+ const field = {
117
+ name: prop.getName(),
118
+ tsType: typeText,
119
+ isOptional: prop.hasQuestionToken() || hasOptionalDecorator(prop),
120
+ isArray,
121
+ nestedDto: null,
122
+ validationRules: extractValidation(prop),
123
+ };
124
+ // Check for nested DTO
125
+ if (!isPrimitiveType(baseType) && referencedDTOs.has(baseType)) {
126
+ field.nestedDto = baseType;
127
+ }
128
+ else if (!isPrimitiveType(baseType) && !isBuiltInType(baseType)) {
129
+ // Potentially a nested DTO we haven't seen yet
130
+ field.nestedDto = baseType;
131
+ }
132
+ // Add validation rule for unmappable types (will be flagged during generation)
133
+ if (unmappableType) {
134
+ field.validationRules.push({
135
+ decorator: 'UnmappableType',
136
+ args: [unmappableType],
137
+ message: `Custom TypeScript type '${unmappableType}' cannot be automatically mapped to Go`,
138
+ });
139
+ }
140
+ fields.push(field);
141
+ }
142
+ return fields;
143
+ }
144
+ // Check if type is a known NestJS/framework type that can be mapped
145
+ function isKnownType(type) {
146
+ const knownTypes = [
147
+ // NestJS common types
148
+ 'Request', 'Response', 'Next', 'ExecutionContext',
149
+ // Common third-party types
150
+ 'ObjectId', 'Buffer',
151
+ ];
152
+ return knownTypes.includes(type);
153
+ }
154
+ function extractValidation(prop) {
155
+ const rules = [];
156
+ const isOptional = prop.hasQuestionToken();
157
+ for (const decorator of prop.getDecorators()) {
158
+ const name = decorator.getName();
159
+ if (VALIDATION_DECORATORS.includes(name)) {
160
+ const rule = {
161
+ decorator: name,
162
+ args: extractDecoratorArgs(decorator),
163
+ message: extractValidationMessage(decorator),
164
+ };
165
+ rules.push(rule);
166
+ }
167
+ }
168
+ // Add required validation if not optional and no IsOptional decorator
169
+ if (!isOptional && !rules.some(r => r.decorator === 'IsOptional')) {
170
+ // Don't add required - Go validator uses required by default
171
+ // unless omitempty is present
172
+ }
173
+ return rules;
174
+ }
175
+ function extractDecoratorArgs(decorator) {
176
+ const args = [];
177
+ const decoratorArgs = decorator.getArguments();
178
+ for (const arg of decoratorArgs) {
179
+ const argText = arg.getText();
180
+ // Try to parse as number
181
+ const num = parseFloat(argText);
182
+ if (!isNaN(num)) {
183
+ args.push(num);
184
+ continue;
185
+ }
186
+ // Try to parse as string literal
187
+ if (ts_morph_1.Node.isStringLiteral(arg)) {
188
+ args.push(argText.replace(/['"]/g, ''));
189
+ continue;
190
+ }
191
+ // Try to parse as object (simplified)
192
+ if (argText.startsWith('{')) {
193
+ try {
194
+ args.push(JSON.parse(argText));
195
+ }
196
+ catch {
197
+ args.push(argText);
198
+ }
199
+ continue;
200
+ }
201
+ // Default: keep as string
202
+ args.push(argText);
203
+ }
204
+ return args;
205
+ }
206
+ function extractValidationMessage(decorator) {
207
+ const args = decorator.getArguments();
208
+ if (args.length > 0) {
209
+ const lastArg = args[args.length - 1];
210
+ const argText = lastArg.getText();
211
+ // Check if it's an options object with message
212
+ const messageMatch = argText.match(/message:\s*['"]([^'"]+)['"]/);
213
+ if (messageMatch) {
214
+ return messageMatch[1];
215
+ }
216
+ }
217
+ return null;
218
+ }
219
+ function hasOptionalDecorator(prop) {
220
+ return prop.getDecorators().some(d => d.getName() === 'IsOptional');
221
+ }
222
+ function extractBaseType(typeText) {
223
+ // Remove array notation
224
+ let base = typeText.replace(/\[\]$/, '').replace(/Array</, '').replace(/>$/, '');
225
+ // Remove generics
226
+ const genericMatch = base.match(/^(\w+)</);
227
+ if (genericMatch) {
228
+ base = genericMatch[1];
229
+ }
230
+ // Remove import paths
231
+ base = base.split('.').pop() || base;
232
+ return base.trim();
233
+ }
234
+ function isPrimitiveType(type) {
235
+ const primitives = [
236
+ 'string', 'number', 'boolean', 'Date', 'any', 'unknown',
237
+ 'null', 'undefined', 'void', 'object', 'Buffer'
238
+ ];
239
+ return primitives.includes(type);
240
+ }
241
+ function isBuiltInType(type) {
242
+ const builtIns = [
243
+ 'Promise', 'Observable', 'Array', 'Map', 'Set', 'Record',
244
+ 'Partial', 'Required', 'Readonly', 'Pick', 'Omit', 'Exclude', 'Extract'
245
+ ];
246
+ return builtIns.includes(type);
247
+ }
248
+ function getRelativePath(filePath) {
249
+ const parts = filePath.split(/[\\/]/);
250
+ const srcIndex = parts.indexOf('src');
251
+ if (srcIndex !== -1) {
252
+ return parts.slice(srcIndex).join('/');
253
+ }
254
+ return filePath;
255
+ }
256
+ // Helper to collect all referenced DTOs from controllers
257
+ function collectReferencedDTOs(controllers) {
258
+ const referenced = new Set();
259
+ for (const controller of controllers) {
260
+ for (const route of controller.routes) {
261
+ if (route.requestBody?.$ref) {
262
+ referenced.add(route.requestBody.$ref);
263
+ }
264
+ if (route.responseBody?.$ref) {
265
+ referenced.add(route.responseBody.$ref);
266
+ }
267
+ }
268
+ }
269
+ return referenced;
270
+ }
271
+ // Mark DTOs that are shared across multiple controllers
272
+ function markSharedDTOs(dtos, controllers) {
273
+ const referenceCounts = {};
274
+ for (const controller of controllers) {
275
+ const controllerDTOs = new Set();
276
+ for (const route of controller.routes) {
277
+ if (route.requestBody?.$ref) {
278
+ controllerDTOs.add(route.requestBody.$ref);
279
+ }
280
+ if (route.responseBody?.$ref) {
281
+ controllerDTOs.add(route.responseBody.$ref);
282
+ }
283
+ }
284
+ for (const dtoName of controllerDTOs) {
285
+ referenceCounts[dtoName] = (referenceCounts[dtoName] || 0) + 1;
286
+ }
287
+ }
288
+ for (const [name, count] of Object.entries(referenceCounts)) {
289
+ if (count > 1 && dtos[name]) {
290
+ dtos[name].isShared = true;
291
+ }
292
+ }
293
+ }
294
+ //# sourceMappingURL=dto-extractor.js.map