1
1
import type { ReadonlyVisitor } from '@nestjs/cli/lib/compiler/interfaces/readonly-visitor.interface' ;
2
+ import { hasDecorators } from '@nestjs/graphql/dist/plugin/utils/ast-utils.js' ;
2
3
import * as ts from 'typescript' ;
3
4
5
+ const securedKeys = [ 'value' , 'canRead' , 'canEdit' ] ;
6
+
4
7
export class ResourceVisitor {
5
8
constructor ( readonly readonly = false ) { }
6
9
7
10
visit ( sf : ts . SourceFile , ctx : ts . TransformationContext , program : ts . Program ) {
11
+ if ( ! sf . fileName . endsWith ( 'dto.ts' ) ) {
12
+ return sf ;
13
+ }
14
+ const { factory } = ctx ;
8
15
const visitNode = ( node : ts . Node ) : ts . Node => {
9
- // TODO logic
16
+ const decorators =
17
+ ( ts . canHaveDecorators ( node ) && ts . getDecorators ( node ) ) || [ ] ;
18
+ if (
19
+ ts . isClassDeclaration ( node ) &&
20
+ hasDecorators ( decorators , [ 'RegisterResource' ] )
21
+ ) {
22
+ return this . enhanceDtoClass ( node , program , factory ) ;
23
+ }
10
24
11
25
if ( this . readonly ) {
12
26
ts . forEachChild ( node , visitNode ) ;
@@ -17,6 +31,69 @@ export class ResourceVisitor {
17
31
} ;
18
32
return ts . visitNode ( sf , visitNode ) ;
19
33
}
34
+
35
+ private enhanceDtoClass (
36
+ classNode : ts . ClassDeclaration ,
37
+ program : ts . Program ,
38
+ factory : ts . NodeFactory ,
39
+ ) {
40
+ const typeChecker = program . getTypeChecker ( ) ;
41
+
42
+ const classProps = typeChecker
43
+ . getTypeAtLocation ( classNode )
44
+ . getApparentProperties ( ) ;
45
+
46
+ const securedProps = classProps . flatMap ( ( member ) => {
47
+ const memberTypeProps = member . valueDeclaration
48
+ ? typeChecker . getTypeAtLocation ( member . valueDeclaration ) . getProperties ( )
49
+ : [ ] ;
50
+
51
+ const isSecured = securedKeys . every ( ( securedKey ) =>
52
+ memberTypeProps . find ( ( k ) => k . getName ( ) === securedKey ) ,
53
+ ) ;
54
+ return isSecured ? member : [ ] ;
55
+ } ) ;
56
+
57
+ return this . updateClassMembers ( factory , classNode , [
58
+ this . createStaticPropArray ( factory , 'Props' , classProps ) ,
59
+ this . createStaticPropArray ( factory , 'SecuredProps' , securedProps ) ,
60
+ ...classNode . members ,
61
+ ] ) ;
62
+ }
63
+
64
+ private createStaticPropArray (
65
+ factory : ts . NodeFactory ,
66
+ name : string ,
67
+ members : ts . Symbol [ ] ,
68
+ ) {
69
+ return factory . createPropertyDeclaration (
70
+ [
71
+ factory . createModifier ( ts . SyntaxKind . StaticKeyword ) ,
72
+ factory . createModifier ( ts . SyntaxKind . ReadonlyKeyword ) ,
73
+ ] ,
74
+ factory . createIdentifier ( name ) ,
75
+ undefined ,
76
+ undefined ,
77
+ factory . createArrayLiteralExpression (
78
+ members . map ( ( p ) => factory . createStringLiteral ( p . getName ( ) , true ) ) ,
79
+ ) ,
80
+ ) ;
81
+ }
82
+
83
+ private updateClassMembers (
84
+ factory : ts . NodeFactory ,
85
+ classNode : ts . ClassDeclaration ,
86
+ newMembers : readonly ts . ClassElement [ ] ,
87
+ ) {
88
+ return factory . updateClassDeclaration (
89
+ classNode ,
90
+ classNode . modifiers ,
91
+ classNode . name ,
92
+ classNode . typeParameters ,
93
+ classNode . heritageClauses ,
94
+ newMembers ,
95
+ ) ;
96
+ }
20
97
}
21
98
22
99
export class ResourceReadonlyVisitor implements ReadonlyVisitor {
0 commit comments