@golemcloud/golem-ts-typegen 1.0.0-dev.4 → 1.0.0-dev.6

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/dist/index.js DELETED
@@ -1,830 +0,0 @@
1
- // Copyright 2024-2026 Golem Cloud
2
- //
3
- // Licensed under the Golem Source License v1.1 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://license.golem.cloud/LICENSE
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
- import { Node as TsMorphNode, Scope, SyntaxKind, ts, } from 'ts-morph';
15
- import { buildJSONFromType, Node, Symbol, TypeMetadata, } from '@golemcloud/golem-ts-types-core';
16
- import * as fs from 'node:fs';
17
- import path from 'path';
18
- export function getTypeFromTsMorph(tsMorphType, isOptional, sourceTypeNode) {
19
- try {
20
- return getTypeFromTsMorphInternal(tsMorphType, isOptional, new Set(), sourceTypeNode);
21
- }
22
- catch (e) {
23
- if (e instanceof Error) {
24
- let error = e.message;
25
- if (e.stack) {
26
- error = error + '\n\n' + e.stack;
27
- }
28
- return {
29
- kind: 'unresolved-type',
30
- name: undefined,
31
- owner: undefined,
32
- optional: isOptional,
33
- text: tsMorphType.getText(),
34
- error: error,
35
- };
36
- }
37
- else {
38
- throw e;
39
- }
40
- }
41
- }
42
- function getTypeFromTsMorphInternal(tsMorphType, isOptional, visitedTypes, sourceTypeNode) {
43
- const type = unwrapAlias(tsMorphType);
44
- const rawName = getRawTypeName(type);
45
- const aliasName = getAliasTypeName(tsMorphType) ?? getAliasTypeName(type);
46
- const owner = getTypeOwner(tsMorphType, sourceTypeNode) ?? getTypeOwner(type);
47
- if (visitedTypes.has(tsMorphType)) {
48
- return {
49
- kind: 'others',
50
- name: rawName ?? aliasName ?? type.getText(),
51
- owner,
52
- optional: isOptional,
53
- recursive: true,
54
- };
55
- }
56
- visitedTypes.add(tsMorphType);
57
- switch (rawName) {
58
- case 'Object':
59
- return {
60
- kind: 'others',
61
- name: rawName,
62
- owner,
63
- optional: isOptional,
64
- recursive: false,
65
- };
66
- case 'Float64Array':
67
- return {
68
- kind: 'array',
69
- name: 'Float64Array',
70
- owner,
71
- element: {
72
- kind: 'number',
73
- owner: undefined,
74
- optional: false,
75
- },
76
- optional: isOptional,
77
- };
78
- case 'Float32Array':
79
- return {
80
- kind: 'array',
81
- name: 'Float32Array',
82
- owner,
83
- element: {
84
- kind: 'number',
85
- owner: undefined,
86
- optional: false,
87
- },
88
- optional: isOptional,
89
- };
90
- case 'Int8Array':
91
- return {
92
- kind: 'array',
93
- name: 'Int8Array',
94
- owner,
95
- element: {
96
- kind: 'number',
97
- owner: undefined,
98
- optional: false,
99
- },
100
- optional: isOptional,
101
- };
102
- case 'Uint8Array':
103
- return {
104
- kind: 'array',
105
- name: 'Uint8Array',
106
- owner,
107
- element: {
108
- kind: 'number',
109
- owner: undefined,
110
- optional: false,
111
- },
112
- optional: isOptional,
113
- };
114
- case 'Int16Array':
115
- return {
116
- kind: 'array',
117
- name: 'Int16Array',
118
- owner,
119
- element: {
120
- kind: 'number',
121
- owner: undefined,
122
- optional: false,
123
- },
124
- optional: isOptional,
125
- };
126
- case 'Uint16Array':
127
- return {
128
- kind: 'array',
129
- name: 'Uint16Array',
130
- owner,
131
- element: {
132
- kind: 'number',
133
- owner: undefined,
134
- optional: false,
135
- },
136
- optional: isOptional,
137
- };
138
- case 'Int32Array':
139
- return {
140
- kind: 'array',
141
- name: 'Int32Array',
142
- owner,
143
- element: {
144
- kind: 'number',
145
- owner: undefined,
146
- optional: false,
147
- },
148
- optional: isOptional,
149
- };
150
- case 'Uint32Array':
151
- return {
152
- kind: 'array',
153
- name: 'Uint32Array',
154
- owner,
155
- element: {
156
- kind: 'number',
157
- owner: undefined,
158
- optional: false,
159
- },
160
- optional: isOptional,
161
- };
162
- case 'BigInt64Array':
163
- return {
164
- kind: 'array',
165
- name: 'BigInt64Array',
166
- owner,
167
- element: {
168
- kind: 'number',
169
- owner: undefined,
170
- optional: false,
171
- },
172
- optional: isOptional,
173
- };
174
- case 'BigUint64Array':
175
- return {
176
- kind: 'array',
177
- name: 'BigUint64Array',
178
- owner,
179
- element: {
180
- kind: 'number',
181
- owner: undefined,
182
- optional: false,
183
- },
184
- optional: isOptional,
185
- };
186
- }
187
- // sdk config type
188
- // because config type detection is unreliable currently, fall through to other detection if the node is malformed.
189
- // Once we can reliably detect the node using symbols, we should surface an error to users here that they are using
190
- // config in an unsupported way.
191
- const sdkConfigType = getSdkConfigTypeFromTsMorph(type, rawName, aliasName, isOptional);
192
- // eslint-disable-next-line eqeqeq
193
- if (sdkConfigType != null)
194
- return sdkConfigType;
195
- // sdk quota-token type — detected by name; TODO switch to symbol-based matching
196
- const sdkQuotaTokenType = getSdkQuotaTokenTypeFromTsMorph(rawName, aliasName, isOptional);
197
- // eslint-disable-next-line eqeqeq
198
- if (sdkQuotaTokenType != null)
199
- return sdkQuotaTokenType;
200
- // These will handle record types. However, record type is devoid
201
- // of details, and hence we don't support record type at the SDK level
202
- if (type.isObject() && type.getProperties().length === 0) {
203
- const name = rawName ?? aliasName ?? type.getText();
204
- return {
205
- kind: 'others',
206
- name: name,
207
- owner,
208
- optional: isOptional,
209
- recursive: false,
210
- };
211
- }
212
- if (rawName === 'Promise' && type.getTypeArguments().length === 1) {
213
- const inner = type.getTypeArguments()[0];
214
- const promiseType = getTypeFromTsMorphInternal(inner, false, visitedTypes);
215
- return {
216
- kind: 'promise',
217
- name: aliasName,
218
- owner,
219
- element: promiseType,
220
- optional: isOptional,
221
- };
222
- }
223
- if (rawName === 'Map' && type.getTypeArguments().length === 2) {
224
- const [keyT, valT] = type.getTypeArguments();
225
- const key = getTypeFromTsMorphInternal(keyT, false, new Set(visitedTypes));
226
- const value = getTypeFromTsMorphInternal(valT, false, new Set(visitedTypes));
227
- return {
228
- kind: 'map',
229
- name: aliasName,
230
- owner,
231
- key: key,
232
- value: value,
233
- optional: isOptional,
234
- };
235
- }
236
- if (type.isVoid()) {
237
- return { kind: 'void', name: 'void', owner, optional: isOptional };
238
- }
239
- if (type.isBoolean()) {
240
- return { kind: 'boolean', owner, optional: isOptional };
241
- }
242
- if (type.isLiteral()) {
243
- const literalValue = type.getLiteralValue() ?? type.getText();
244
- return {
245
- kind: 'literal',
246
- name: aliasName,
247
- owner,
248
- literalValue: literalValue.toString(),
249
- optional: isOptional,
250
- };
251
- }
252
- if (type.isTuple()) {
253
- const tupleElems = type
254
- .getTupleElements()
255
- .map((el) => getTypeFromTsMorphInternal(el, false, new Set(visitedTypes)));
256
- return {
257
- kind: 'tuple',
258
- name: aliasName,
259
- owner,
260
- elements: tupleElems,
261
- optional: isOptional,
262
- };
263
- }
264
- if (type.isArray()) {
265
- const elementType = type.getArrayElementType();
266
- let resolvedElementType;
267
- if (elementType?.isTypeParameter()) {
268
- resolvedElementType = tsMorphType.getAliasTypeArguments()[0];
269
- }
270
- else {
271
- resolvedElementType = elementType;
272
- }
273
- if (!resolvedElementType) {
274
- throw new Error('Array type without element type');
275
- }
276
- const element = getTypeFromTsMorphInternal(resolvedElementType, false, visitedTypes);
277
- return {
278
- kind: 'array',
279
- name: aliasName,
280
- owner,
281
- element,
282
- optional: isOptional,
283
- };
284
- }
285
- if (type.isUnion()) {
286
- const argsInternal = tsMorphType.getAliasTypeArguments();
287
- const aliased = getAliasTypeArgumentsSafe(tsMorphType);
288
- const unionTypes = getSourceOrderedUnionTypes(type, sourceTypeNode, visitedTypes) ??
289
- getCanonicalFallbackUnionTypes(type.getUnionTypes(), visitedTypes);
290
- const [aliasRawName, aliasedTypeArgs] = aliased;
291
- if (argsInternal.length > 0 || !aliasRawName) {
292
- const args = argsInternal.map((arg) => getTypeFromTsMorph(arg, false));
293
- return {
294
- kind: 'union',
295
- name: aliasName,
296
- owner,
297
- unionTypes,
298
- optional: isOptional,
299
- typeParams: args,
300
- originalTypeName: undefined,
301
- };
302
- }
303
- const aliasedArgs = aliasedTypeArgs.map((arg) => getTypeFromTsMorph(arg, false));
304
- return {
305
- kind: 'union',
306
- name: aliasName,
307
- owner,
308
- unionTypes,
309
- optional: isOptional,
310
- typeParams: aliasedArgs,
311
- originalTypeName: aliasRawName,
312
- };
313
- }
314
- if (type.isClass()) {
315
- return {
316
- kind: 'class',
317
- name: aliasName ?? rawName,
318
- owner,
319
- properties: propertiesAsSymbols(type, visitedTypes),
320
- optional: isOptional,
321
- };
322
- }
323
- if (type.isInterface()) {
324
- return {
325
- kind: 'interface',
326
- name: aliasName ?? rawName,
327
- owner,
328
- properties: propertiesAsSymbols(type, visitedTypes),
329
- optional: isOptional,
330
- typeParams: type.getAliasTypeArguments().map((arg) => getTypeFromTsMorph(arg, false)),
331
- };
332
- }
333
- if (type.isObject()) {
334
- const args = tsMorphType.getAliasTypeArguments().map((arg) => getTypeFromTsMorph(arg, false));
335
- return {
336
- kind: 'object',
337
- name: aliasName,
338
- owner,
339
- properties: propertiesAsSymbols(type, visitedTypes),
340
- typeParams: args,
341
- optional: isOptional,
342
- };
343
- }
344
- if (type.isNull()) {
345
- return { kind: 'null', name: aliasName, owner, optional: isOptional };
346
- }
347
- if (type.isBigInt()) {
348
- return { kind: 'bigint', name: aliasName, owner, optional: isOptional };
349
- }
350
- if (type.isUndefined()) {
351
- return { kind: 'undefined', name: aliasName, owner, optional: isOptional };
352
- }
353
- if (type.isNumber()) {
354
- return { kind: 'number', name: aliasName, owner, optional: isOptional };
355
- }
356
- if (type.isString()) {
357
- return { kind: 'string', name: aliasName, owner, optional: isOptional };
358
- }
359
- if (type.getTypeArguments().length === 1) {
360
- throw new Error(`Unhandled type with single type argument: ${type.getText()}`);
361
- }
362
- return {
363
- kind: 'others',
364
- name: aliasName ?? type.getText(),
365
- owner,
366
- optional: isOptional,
367
- recursive: false,
368
- };
369
- }
370
- // This is intentionally used as a deterministic fallback order for union members.
371
- // Source-order recovery (when AST nodes are available) overrides this fallback.
372
- function getCanonicalFallbackUnionTypes(unionTypes, visitedTypes) {
373
- const withKeys = unionTypes.map((member, index) => {
374
- const mapped = getTypeFromTsMorphInternal(member, false, new Set(visitedTypes));
375
- return {
376
- index,
377
- mapped,
378
- key: getCanonicalUnionSortKey(mapped),
379
- };
380
- });
381
- withKeys.sort((a, b) => {
382
- if (a.key < b.key)
383
- return -1;
384
- if (a.key > b.key)
385
- return 1;
386
- return a.index - b.index;
387
- });
388
- return withKeys.map(({ mapped }) => mapped);
389
- }
390
- function getSourceOrderedUnionTypes(unionType, sourceTypeNode, visitedTypes) {
391
- const sourceUnionTypeNode = resolveUnionTypeNode(sourceTypeNode) ?? resolveUnionTypeNodeFromType(unionType);
392
- if (!sourceUnionTypeNode)
393
- return undefined;
394
- return sourceUnionTypeNode
395
- .getTypeNodes()
396
- .map((member) => getTypeFromTsMorphInternal(member.getType(), false, new Set(visitedTypes), member));
397
- }
398
- function resolveUnionTypeNodeFromType(type) {
399
- const aliasSymbol = type.getAliasSymbol();
400
- if (!aliasSymbol)
401
- return undefined;
402
- for (const declaration of aliasSymbol.getDeclarations()) {
403
- if (!TsMorphNode.isTypeAliasDeclaration(declaration))
404
- continue;
405
- const unionTypeNode = resolveUnionTypeNode(declaration.getTypeNode());
406
- if (unionTypeNode)
407
- return unionTypeNode;
408
- }
409
- return undefined;
410
- }
411
- function resolveUnionTypeNode(node) {
412
- if (!node)
413
- return undefined;
414
- if (TsMorphNode.isUnionTypeNode(node)) {
415
- return node;
416
- }
417
- if (TsMorphNode.isParenthesizedTypeNode(node)) {
418
- return resolveUnionTypeNode(node.getTypeNode());
419
- }
420
- return undefined;
421
- }
422
- function getCanonicalUnionSortKey(type) {
423
- const rank = getUnionTypeKindRank(type.kind).toString().padStart(2, '0');
424
- return `${rank}:${JSON.stringify(buildJSONFromType(type))}`;
425
- }
426
- function getUnionTypeKindRank(kind) {
427
- switch (kind) {
428
- case 'undefined':
429
- return 0;
430
- case 'null':
431
- return 1;
432
- case 'void':
433
- return 2;
434
- case 'string':
435
- return 3;
436
- case 'number':
437
- return 4;
438
- case 'bigint':
439
- return 5;
440
- case 'boolean':
441
- return 6;
442
- case 'literal':
443
- return 7;
444
- case 'tuple':
445
- return 8;
446
- case 'array':
447
- return 9;
448
- case 'map':
449
- return 10;
450
- case 'object':
451
- return 11;
452
- case 'interface':
453
- return 12;
454
- case 'class':
455
- return 13;
456
- case 'promise':
457
- return 14;
458
- case 'union':
459
- return 15;
460
- case 'config':
461
- return 16;
462
- case 'quota-token':
463
- return 16;
464
- case 'alias':
465
- return 17;
466
- case 'others':
467
- return 18;
468
- case 'unresolved-type':
469
- return 19;
470
- }
471
- }
472
- function getSdkConfigTypeFromTsMorph(type, rawName, aliasName, isOptional) {
473
- if (rawName !== 'Config')
474
- return undefined;
475
- if (type.getTypeArguments().length !== 1)
476
- return undefined;
477
- const rawInner = type.getTypeArguments()[0];
478
- const innerType = unwrapAlias(rawInner);
479
- const typeLiteral = resolveStrictTypeLiteralNode(innerType);
480
- if (!typeLiteral)
481
- return undefined;
482
- const properties = extractConfigPropertiesFromTypeLiteral(typeLiteral, []);
483
- if (!properties)
484
- return undefined;
485
- return {
486
- kind: 'config',
487
- name: aliasName,
488
- owner: getTypeOwner(type),
489
- optional: isOptional,
490
- properties,
491
- };
492
- }
493
- function getSdkQuotaTokenTypeFromTsMorph(rawName, aliasName, isOptional) {
494
- // Detected by name, same as Config above — TODO: switch to symbol-based matching
495
- if (rawName !== 'QuotaToken')
496
- return undefined;
497
- return { kind: 'quota-token', name: aliasName, optional: isOptional };
498
- }
499
- // TypeLiteral in TS AST is `type A = {}`. Union types, etc. get other node types
500
- function resolveStrictTypeLiteralNode(type) {
501
- const symbol = type.getSymbol();
502
- if (!symbol)
503
- return undefined;
504
- const typeLiteralDecl = symbol.getDeclarations().find(TsMorphNode.isTypeLiteral);
505
- return typeLiteralDecl;
506
- }
507
- function extractConfigPropertiesFromTypeLiteral(node, path) {
508
- const members = node.getMembers();
509
- if (!members.every(TsMorphNode.isPropertySignature)) {
510
- return undefined;
511
- }
512
- const results = [];
513
- for (const member of members) {
514
- const name = member.getName();
515
- const nextPath = [...path, name];
516
- const propType = unwrapAlias(member.getType());
517
- // 1. secret wrapper
518
- // TODO: switch to nominal matching using symbols instead of string
519
- if (propType.getSymbol()?.getName() === 'Secret' && propType.getTypeArguments().length === 1) {
520
- results.push({
521
- path: nextPath,
522
- secret: true,
523
- type: getTypeFromTsMorph(propType.getTypeArguments()[0], member.hasQuestionToken(), member.getTypeNode()),
524
- });
525
- continue;
526
- }
527
- // 2. nested type literal
528
- const nestedTypeLiteral = resolveStrictTypeLiteralNode(propType);
529
- // eslint-disable-next-line eqeqeq
530
- if (nestedTypeLiteral != null) {
531
- const nested = extractConfigPropertiesFromTypeLiteral(nestedTypeLiteral, nextPath);
532
- // eslint-disable-next-line eqeqeq
533
- if (nested == null)
534
- return undefined;
535
- results.push(...nested);
536
- continue;
537
- }
538
- // 3. leaf node
539
- results.push({
540
- path: nextPath,
541
- secret: false,
542
- type: getTypeFromTsMorph(propType, member.hasQuestionToken(), member.getTypeNode()),
543
- });
544
- }
545
- return results;
546
- }
547
- function getAliasTypeArgumentsSafe(type) {
548
- const aliasSymbol = type.getAliasSymbol();
549
- if (!aliasSymbol)
550
- return [undefined, []];
551
- const decl = aliasSymbol.getDeclarations()[0];
552
- if (!decl || !decl.isKind(ts.SyntaxKind.TypeAliasDeclaration))
553
- return [undefined, []];
554
- const typeNode = decl.getTypeNodeOrThrow();
555
- const typeRef = typeNode.asKind(ts.SyntaxKind.TypeReference);
556
- if (!typeRef)
557
- return [undefined, []];
558
- return [typeRef.getTypeName().getText(), typeRef.getTypeArguments().map((arg) => arg.getType())];
559
- }
560
- export function getRawTypeName(type) {
561
- const rawName = type.getSymbol()?.getName();
562
- if (!rawName || rawName === '__type') {
563
- const alias = type.getAliasSymbol()?.getName();
564
- if (!alias || alias === '__type') {
565
- return type.getText();
566
- }
567
- return alias;
568
- }
569
- return rawName;
570
- }
571
- export function getAliasTypeName(type) {
572
- const alias = type.getAliasSymbol()?.getName();
573
- if (!alias || alias === '__type') {
574
- return undefined;
575
- }
576
- return alias;
577
- }
578
- export function getTypeOwner(type, sourceTypeNode) {
579
- return (getOwnerFromSourceTypeNode(sourceTypeNode) ??
580
- getOwnerFromDeclarations(type.getAliasSymbol()?.getDeclarations()) ??
581
- getOwnerFromDeclarations(type.getSymbol()?.getDeclarations()));
582
- }
583
- function getOwnerFromSourceTypeNode(node) {
584
- if (!node)
585
- return undefined;
586
- if (TsMorphNode.isTypeReference(node)) {
587
- const typeNameSymbol = node.getTypeName().getSymbol();
588
- const fromTypeName = getOwnerFromDeclarations(typeNameSymbol?.getDeclarations());
589
- if (fromTypeName)
590
- return fromTypeName;
591
- return getOwnerFromDeclarations(node.getType().getAliasSymbol()?.getDeclarations());
592
- }
593
- return undefined;
594
- }
595
- function getOwnerFromDeclarations(declarations) {
596
- if (!declarations || declarations.length === 0)
597
- return undefined;
598
- for (const declaration of declarations) {
599
- const importDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
600
- if (importDeclaration) {
601
- return importDeclaration.getModuleSpecifierValue();
602
- }
603
- const moduleDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ModuleDeclaration);
604
- if (moduleDeclaration) {
605
- const moduleName = moduleDeclaration.getName();
606
- return moduleName.replace(/^['"]|['"]$/g, '');
607
- }
608
- }
609
- return undefined;
610
- }
611
- export function unwrapAlias(type) {
612
- let current = type;
613
- const visited = new Set();
614
- while (true) {
615
- const aliasSymbol = current.getAliasSymbol();
616
- if (!aliasSymbol || visited.has(current))
617
- break;
618
- visited.add(current);
619
- const decl = aliasSymbol.getDeclarations()[0];
620
- if (!decl)
621
- break;
622
- const realType = decl.getType();
623
- if (realType === current)
624
- break;
625
- current = realType;
626
- }
627
- return current;
628
- }
629
- export function generateClassMetadata(classMetadataGenConfig) {
630
- updateMetadataFromSourceFiles(classMetadataGenConfig);
631
- return saveAndClearInMemoryMetadata();
632
- }
633
- export function updateMetadataFromSourceFiles(classMetadataGenConfig) {
634
- for (const sourceFile of classMetadataGenConfig.sourceFiles) {
635
- const classes = sourceFile.getClasses();
636
- for (const classDecl of classes) {
637
- if (classMetadataGenConfig.classDecorators.length > 0) {
638
- const hasAnyConfiguredDecorator = classDecl
639
- .getDecorators()
640
- .some((d) => classMetadataGenConfig.classDecorators.includes(d.getName()));
641
- if (!hasAnyConfiguredDecorator) {
642
- continue;
643
- }
644
- }
645
- const className = classDecl.getName();
646
- if (!className)
647
- continue;
648
- const publicConstructors = classMetadataGenConfig.includeOnlyPublicScope
649
- ? classDecl.getConstructors().filter((ctor) => ctor.getScope() === Scope.Public)
650
- : classDecl.getConstructors();
651
- const constructorArgs = publicConstructors.length === 0
652
- ? []
653
- : publicConstructors[0].getParameters().map((p) => ({
654
- name: p.getName(),
655
- type: getTypeFromTsMorph(p.getType(), p.isOptional(), p.getTypeNode()),
656
- }));
657
- const methods = new Map();
658
- const publicMethods = classMetadataGenConfig.includeOnlyPublicScope
659
- ? classDecl.getMethods().filter((m) => m.getScope() === Scope.Public)
660
- : classDecl.getMethods();
661
- for (const method of publicMethods) {
662
- if (classMetadataGenConfig.excludeOverriddenMethods &&
663
- (method.hasOverrideKeyword() || isOverriddenMethod(method))) {
664
- continue;
665
- }
666
- const methodParams = new Map(method.getParameters().map((p) => {
667
- return [p.getName(), getTypeFromTsMorph(p.getType(), p.isOptional(), p.getTypeNode())];
668
- }));
669
- const returnType = getTypeFromTsMorph(method.getReturnType(), false, method.getReturnTypeNode());
670
- methods.set(method.getName(), { methodParams, returnType });
671
- }
672
- const isScopeAllowed = (decl) => !classMetadataGenConfig.includeOnlyPublicScope || decl.getScope() === Scope.Public;
673
- const publicArrows = classDecl
674
- .getProperties()
675
- .filter((p) => isScopeAllowed(p) &&
676
- p.getType().getCallSignatures().length > 0 &&
677
- p.hasInitializer() &&
678
- (p.getInitializerIfKind(SyntaxKind.ArrowFunction) ||
679
- p.getInitializerIfKind(SyntaxKind.FunctionExpression)));
680
- for (const publicArrow of publicArrows) {
681
- if (classMetadataGenConfig.excludeOverriddenMethods &&
682
- (publicArrow.hasOverrideKeyword() || isOverriddenProperty(publicArrow))) {
683
- continue;
684
- }
685
- const arrowType = publicArrow.getType();
686
- const callSignature = arrowType.getCallSignatures()[0];
687
- if (!callSignature)
688
- continue;
689
- const methodParams = new Map(callSignature.getParameters().map((p) => {
690
- const decl = p.getDeclarations()[0];
691
- if (!decl) {
692
- throw new Error(`No declaration found for parameter ${p.getName()} in arrow method ${publicArrow.getName()} of class ${className}`);
693
- }
694
- const paramType = p.getTypeAtLocation(decl);
695
- const isOptional = TsMorphNode.isParameterDeclaration(decl) ? decl.isOptional() : false;
696
- const sourceTypeNode = TsMorphNode.isParameterDeclaration(decl)
697
- ? decl.getTypeNode()
698
- : undefined;
699
- return [p.getName(), getTypeFromTsMorph(paramType, isOptional, sourceTypeNode)];
700
- }));
701
- const returnType = getTypeFromTsMorph(callSignature.getReturnType(), false);
702
- methods.set(publicArrow.getName(), { methodParams, returnType });
703
- }
704
- TypeMetadata.update(className, constructorArgs, methods);
705
- }
706
- }
707
- }
708
- function isOverriddenMethod(method) {
709
- const classDecl = method.getFirstAncestorByKind(SyntaxKind.ClassDeclaration);
710
- if (!classDecl)
711
- return false;
712
- let currentBase = classDecl.getBaseClass();
713
- const methodName = method.getName();
714
- while (currentBase) {
715
- const baseMethod = currentBase.getInstanceMethod(methodName);
716
- if (baseMethod)
717
- return true;
718
- currentBase = currentBase.getBaseClass();
719
- }
720
- return false;
721
- }
722
- function isOverriddenProperty(prop) {
723
- const classDecl = prop.getFirstAncestorByKind(SyntaxKind.ClassDeclaration);
724
- if (!classDecl)
725
- return false;
726
- let currentBase = classDecl.getBaseClass();
727
- const propName = prop.getName();
728
- while (currentBase) {
729
- const baseProp = currentBase.getInstanceProperty(propName);
730
- if (baseProp)
731
- return true;
732
- // See if overriding a method with an arrow
733
- const baseMethod = currentBase.getInstanceMethod(propName);
734
- if (baseMethod)
735
- return true;
736
- currentBase = currentBase.getBaseClass();
737
- }
738
- return false;
739
- }
740
- const METADATA_DIR = '.metadata';
741
- const METADATA_TS_FILE = 'generated-types.ts';
742
- const METADATA_JSON_FILE = 'generated-types.json';
743
- export function saveAndClearInMemoryMetadata() {
744
- if (!fs.existsSync(METADATA_DIR)) {
745
- fs.mkdirSync(METADATA_DIR);
746
- }
747
- const json = {};
748
- for (const [className, meta] of TypeMetadata.getAll().entries()) {
749
- const constructorArgsJSON = meta.constructorArgs.map((arg) => ({
750
- name: arg.name,
751
- type: buildJSONFromType(arg.type),
752
- }));
753
- const methodsObj = {};
754
- for (const [methodName, { methodParams, returnType }] of meta.methods) {
755
- const paramsJSON = {};
756
- for (const [paramName, paramType] of methodParams.entries()) {
757
- paramsJSON[paramName] = buildJSONFromType(paramType);
758
- }
759
- methodsObj[methodName] = {
760
- methodParams: paramsJSON,
761
- returnType: buildJSONFromType(returnType),
762
- };
763
- }
764
- json[className] = {
765
- constructorArgs: constructorArgsJSON,
766
- methods: methodsObj,
767
- };
768
- }
769
- const tsFilePath = path.join(METADATA_DIR, METADATA_TS_FILE);
770
- const jsonFilePath = path.join(METADATA_DIR, METADATA_JSON_FILE);
771
- const tsContent = `export const Metadata = ${JSON.stringify(json, null, 2)};`;
772
- const jsonContent = JSON.stringify(json, null, 2);
773
- fs.writeFileSync(tsFilePath, tsContent, 'utf-8');
774
- fs.writeFileSync(jsonFilePath, jsonContent, 'utf-8');
775
- TypeMetadata.clearAll();
776
- return tsFilePath;
777
- }
778
- export function lazyLoadTypeMetadata() {
779
- if (TypeMetadata.getAll().size === 0) {
780
- loadTypeMetadataFromJsonFile();
781
- }
782
- }
783
- export function loadTypeMetadataFromJsonFile() {
784
- TypeMetadata.clearMetadata();
785
- const filePath = path.join(METADATA_DIR, METADATA_JSON_FILE);
786
- if (!fs.existsSync(filePath)) {
787
- throw new Error(`${filePath} does not exist`);
788
- }
789
- const raw = fs.readFileSync(filePath, 'utf-8');
790
- const json = JSON.parse(raw);
791
- TypeMetadata.loadFromJson(json);
792
- }
793
- function propertiesAsSymbols(type, visitedTypes) {
794
- return type.getProperties().map((prop) => {
795
- const firstDeclaration = prop.getDeclarations()[0];
796
- // NOTE: falling back to firstDeclaration if no value declaration found,
797
- // to support runtime generated or manipulated types
798
- const type = prop.getTypeAtLocation(getValueDeclaration(prop) ?? firstDeclaration);
799
- const sourceTypeNode = TsMorphNode.isPropertySignature(firstDeclaration) ||
800
- TsMorphNode.isPropertyDeclaration(firstDeclaration)
801
- ? firstDeclaration.getTypeNode()
802
- : undefined;
803
- const tsType = getTypeFromTsMorphInternal(type, false, new Set(visitedTypes), sourceTypeNode);
804
- const propName = prop.getName();
805
- if ((TsMorphNode.isPropertySignature(firstDeclaration) ||
806
- TsMorphNode.isPropertyDeclaration(firstDeclaration)) &&
807
- firstDeclaration.hasQuestionToken()) {
808
- return new Symbol({
809
- name: propName,
810
- declarations: [new Node('PropertyDeclaration', true)],
811
- typeAtLocation: tsType,
812
- });
813
- }
814
- else {
815
- return new Symbol({
816
- name: propName,
817
- declarations: [new Node('PropertyDeclaration', false)],
818
- typeAtLocation: tsType,
819
- });
820
- }
821
- });
822
- }
823
- function getValueDeclaration(symbol) {
824
- try {
825
- return symbol.getValueDeclarationOrThrow();
826
- }
827
- catch {
828
- return undefined;
829
- }
830
- }