1
1
import { TypeMetadata , ExposeMetadata , ExcludeMetadata , TransformMetadata } from './interfaces' ;
2
2
import { TransformationType } from './enums' ;
3
+ import { checkVersion , flatten , onlyUnique } from './utils' ;
3
4
4
5
/**
5
6
* Storage all library metadata.
@@ -11,10 +12,32 @@ export class MetadataStorage {
11
12
12
13
private _typeMetadatas = new Map < Function , Map < string , TypeMetadata > > ( ) ;
13
14
private _transformMetadatas = new Map < Function , Map < string , TransformMetadata [ ] > > ( ) ;
14
- private _exposeMetadatas = new Map < Function , Map < string , ExposeMetadata > > ( ) ;
15
- private _excludeMetadatas = new Map < Function , Map < string , ExcludeMetadata > > ( ) ;
15
+ private _exposeMetadatas = new Map < Function , Map < string , ExposeMetadata [ ] > > ( ) ;
16
+ private _excludeMetadatas = new Map < Function , Map < string , ExcludeMetadata [ ] > > ( ) ;
16
17
private _ancestorsMap = new Map < Function , Function [ ] > ( ) ;
17
18
19
+ // -------------------------------------------------------------------------
20
+ // Static Methods
21
+ // -------------------------------------------------------------------------
22
+
23
+ private static checkMetadataTransformationType <
24
+ T extends { options ?: { toClassOnly ?: boolean ; toPlainOnly ?: boolean } }
25
+ > ( transformationType : TransformationType , metadata : T ) : boolean {
26
+ if ( ! metadata . options ) return true ;
27
+ if ( metadata . options . toClassOnly === true && metadata . options . toPlainOnly === true ) return true ;
28
+
29
+ if ( metadata . options . toClassOnly === true ) {
30
+ return (
31
+ transformationType === TransformationType . CLASS_TO_CLASS ||
32
+ transformationType === TransformationType . PLAIN_TO_CLASS
33
+ ) ;
34
+ }
35
+ if ( metadata . options . toPlainOnly === true ) {
36
+ return transformationType === TransformationType . CLASS_TO_PLAIN ;
37
+ }
38
+ return true ;
39
+ }
40
+
18
41
// -------------------------------------------------------------------------
19
42
// Adder Methods
20
43
// -------------------------------------------------------------------------
@@ -37,17 +60,88 @@ export class MetadataStorage {
37
60
}
38
61
39
62
addExposeMetadata ( metadata : ExposeMetadata ) : void {
63
+ const { toPlainOnly, toClassOnly, name = metadata . propertyName } = metadata . options || { } ;
64
+
65
+ /**
66
+ * check if toPlainOnly and toClassOnly used correctly.
67
+ */
68
+ if (
69
+ metadata . propertyName &&
70
+ ! ( toPlainOnly === true || toClassOnly === true || ( toClassOnly === undefined && toPlainOnly === undefined ) )
71
+ ) {
72
+ throw Error (
73
+ `${ metadata . propertyName } : At least one of "toPlainOnly" and "toClassOnly" options must be "true" or both must be "undefined"`
74
+ ) ;
75
+ }
76
+
40
77
if ( ! this . _exposeMetadatas . has ( metadata . target ) ) {
41
- this . _exposeMetadatas . set ( metadata . target , new Map < string , ExposeMetadata > ( ) ) ;
78
+ this . _exposeMetadatas . set ( metadata . target , new Map < string , ExposeMetadata [ ] > ( ) ) ;
42
79
}
43
- this . _exposeMetadatas . get ( metadata . target ) . set ( metadata . propertyName , metadata ) ;
80
+ if ( ! this . _exposeMetadatas . get ( metadata . target ) . has ( metadata . propertyName ) ) {
81
+ this . _exposeMetadatas . get ( metadata . target ) . set ( metadata . propertyName , [ ] ) ;
82
+ }
83
+ const exposeArray = this . _exposeMetadatas . get ( metadata . target ) . get ( metadata . propertyName ) ;
84
+
85
+ /**
86
+ * check if the current @expose does not conflict with the former decorators.
87
+ */
88
+ const conflictedItemIndex = exposeArray ! . findIndex ( m => {
89
+ const { name : n = m . propertyName , since : s , until : u , toPlainOnly : tpo , toClassOnly : tco } = m . options ?? { } ;
90
+
91
+ /**
92
+ * check whether the intervals intersect or not.
93
+ */
94
+ const s1 = s ?? Number . NEGATIVE_INFINITY ;
95
+ const u1 = u ?? Number . POSITIVE_INFINITY ;
96
+ const s2 = metadata . options ?. since ?? Number . NEGATIVE_INFINITY ;
97
+ const u2 = metadata . options ?. until ?? Number . POSITIVE_INFINITY ;
98
+
99
+ const intervalIntersection = s1 < u2 && s2 < u1 ;
100
+
101
+ /**
102
+ * check whether the current decorator's transformation types,
103
+ * means "toPlainOnly" and "toClassOnly" options,
104
+ * are common with the previous decorators or not.
105
+ */
106
+ const mType = tpo === undefined && tco === undefined ? 3 : ( tpo ? 1 : 0 ) + ( tco ? 2 : 0 ) ;
107
+ const currentType =
108
+ toPlainOnly === undefined && toClassOnly === undefined ? 3 : ( toPlainOnly ? 1 : 0 ) + ( toClassOnly ? 2 : 0 ) ;
109
+ const commonInType = ! ! ( mType & currentType ) ;
110
+
111
+ /**
112
+ * check if the current "name" option
113
+ * is different with the imported decorators or not.
114
+ */
115
+ const differentName = n !== name ;
116
+
117
+ return intervalIntersection && commonInType && differentName ;
118
+ } ) ;
119
+ if ( conflictedItemIndex !== - 1 ) {
120
+ const conflictedItem = exposeArray ! [ conflictedItemIndex ] ;
121
+ throw Error (
122
+ `"${ metadata . propertyName ?? '' } " property:
123
+ The current decorator (decorator #${
124
+ exposeArray ! . length
125
+ } ) conflicts with the decorator #${ conflictedItemIndex } .
126
+ If the stacked decorators intersect, the name option must be the same.
127
+
128
+ @Expose(${ JSON . stringify ( metadata . options || { } ) } )
129
+ conflicts with
130
+ @Expose(${ JSON . stringify ( conflictedItem . options || { } ) } )`
131
+ ) ;
132
+ }
133
+
134
+ exposeArray ?. push ( metadata ) ;
44
135
}
45
136
46
137
addExcludeMetadata ( metadata : ExcludeMetadata ) : void {
47
138
if ( ! this . _excludeMetadatas . has ( metadata . target ) ) {
48
- this . _excludeMetadatas . set ( metadata . target , new Map < string , ExcludeMetadata > ( ) ) ;
139
+ this . _excludeMetadatas . set ( metadata . target , new Map < string , ExcludeMetadata [ ] > ( ) ) ;
140
+ }
141
+ if ( ! this . _excludeMetadatas . get ( metadata . target ) . has ( metadata . propertyName ) ) {
142
+ this . _excludeMetadatas . get ( metadata . target ) . set ( metadata . propertyName , [ ] ) ;
49
143
}
50
- this . _excludeMetadatas . get ( metadata . target ) . set ( metadata . propertyName , metadata ) ;
144
+ this . _excludeMetadatas . get ( metadata . target ) . get ( metadata . propertyName ) . push ( metadata ) ;
51
145
}
52
146
53
147
// -------------------------------------------------------------------------
@@ -59,34 +153,30 @@ export class MetadataStorage {
59
153
propertyName : string ,
60
154
transformationType : TransformationType
61
155
) : TransformMetadata [ ] {
62
- return this . findMetadatas ( this . _transformMetadatas , target , propertyName ) . filter ( metadata => {
63
- if ( ! metadata . options ) return true ;
64
- if ( metadata . options . toClassOnly === true && metadata . options . toPlainOnly === true ) return true ;
65
-
66
- if ( metadata . options . toClassOnly === true ) {
67
- return (
68
- transformationType === TransformationType . CLASS_TO_CLASS ||
69
- transformationType === TransformationType . PLAIN_TO_CLASS
70
- ) ;
71
- }
72
- if ( metadata . options . toPlainOnly === true ) {
73
- return transformationType === TransformationType . CLASS_TO_PLAIN ;
74
- }
75
-
76
- return true ;
77
- } ) ;
156
+ const typeChecker = MetadataStorage . checkMetadataTransformationType . bind ( this , transformationType ) ;
157
+ return this . findMetadatas ( this . _transformMetadatas , target , propertyName ) . filter ( typeChecker ) ;
78
158
}
79
159
80
- findExcludeMetadata ( target : Function , propertyName : string ) : ExcludeMetadata {
81
- return this . findMetadata ( this . _excludeMetadatas , target , propertyName ) ;
160
+ findExcludeMetadatas (
161
+ target : Function ,
162
+ propertyName : string ,
163
+ transformationType : TransformationType
164
+ ) : ExcludeMetadata [ ] {
165
+ const typeChecker = MetadataStorage . checkMetadataTransformationType . bind ( this , transformationType ) ;
166
+ return this . findMetadatas ( this . _excludeMetadatas , target , propertyName ) . filter ( typeChecker ) ;
82
167
}
83
168
84
- findExposeMetadata ( target : Function , propertyName : string ) : ExposeMetadata {
85
- return this . findMetadata ( this . _exposeMetadatas , target , propertyName ) ;
169
+ findExposeMetadatas (
170
+ target : Function ,
171
+ propertyName : string ,
172
+ transformationType : TransformationType
173
+ ) : ExposeMetadata [ ] {
174
+ const typeChecker = MetadataStorage . checkMetadataTransformationType . bind ( this , transformationType ) ;
175
+ return this . findMetadatas ( this . _exposeMetadatas , target , propertyName ) . filter ( typeChecker ) ;
86
176
}
87
177
88
- findExposeMetadataByCustomName ( target : Function , name : string ) : ExposeMetadata {
89
- return this . getExposedMetadatas ( target ) . find ( metadata => {
178
+ findExposeMetadatasByCustomName ( target : Function , name : string ) : ExposeMetadata [ ] {
179
+ return this . getExposedMetadatas ( target ) . filter ( metadata => {
90
180
return metadata . options && metadata . options . name === name ;
91
181
} ) ;
92
182
}
@@ -112,46 +202,25 @@ export class MetadataStorage {
112
202
return this . getMetadata ( this . _excludeMetadatas , target ) ;
113
203
}
114
204
115
- getExposedProperties ( target : Function , transformationType : TransformationType ) : string [ ] {
116
- return this . getExposedMetadatas ( target )
117
- . filter ( metadata => {
118
- if ( ! metadata . options ) return true ;
119
- if ( metadata . options . toClassOnly === true && metadata . options . toPlainOnly === true ) return true ;
120
-
121
- if ( metadata . options . toClassOnly === true ) {
122
- return (
123
- transformationType === TransformationType . CLASS_TO_CLASS ||
124
- transformationType === TransformationType . PLAIN_TO_CLASS
125
- ) ;
126
- }
127
- if ( metadata . options . toPlainOnly === true ) {
128
- return transformationType === TransformationType . CLASS_TO_PLAIN ;
129
- }
130
-
131
- return true ;
132
- } )
133
- . map ( metadata => metadata . propertyName ) ;
205
+ getExposedProperties (
206
+ target : Function ,
207
+ transformationType : TransformationType ,
208
+ options : { version ?: number } = { }
209
+ ) : string [ ] {
210
+ const typeChecker = MetadataStorage . checkMetadataTransformationType . bind ( this , transformationType ) ;
211
+ const { version } = options ;
212
+ let array = this . getExposedMetadatas ( target ) . filter ( typeChecker ) ;
213
+ if ( version ) {
214
+ array = array . filter ( metadata => checkVersion ( version , metadata ?. options ?. since , metadata ?. options ?. until ) ) ;
215
+ }
216
+ return array . map ( metadata => metadata . propertyName ! ) . filter ( onlyUnique ) ;
134
217
}
135
218
136
219
getExcludedProperties ( target : Function , transformationType : TransformationType ) : string [ ] {
220
+ const typeChecker = MetadataStorage . checkMetadataTransformationType . bind ( this , transformationType ) ;
137
221
return this . getExcludedMetadatas ( target )
138
- . filter ( metadata => {
139
- if ( ! metadata . options ) return true ;
140
- if ( metadata . options . toClassOnly === true && metadata . options . toPlainOnly === true ) return true ;
141
-
142
- if ( metadata . options . toClassOnly === true ) {
143
- return (
144
- transformationType === TransformationType . CLASS_TO_CLASS ||
145
- transformationType === TransformationType . PLAIN_TO_CLASS
146
- ) ;
147
- }
148
- if ( metadata . options . toPlainOnly === true ) {
149
- return transformationType === TransformationType . CLASS_TO_PLAIN ;
150
- }
151
-
152
- return true ;
153
- } )
154
- . map ( metadata => metadata . propertyName ) ;
222
+ . filter ( typeChecker )
223
+ . map ( metadata => metadata . propertyName ! ) ;
155
224
}
156
225
157
226
clear ( ) : void {
@@ -165,26 +234,28 @@ export class MetadataStorage {
165
234
// Private Methods
166
235
// -------------------------------------------------------------------------
167
236
168
- private getMetadata < T extends { target : Function ; propertyName : string } > (
169
- metadatas : Map < Function , Map < string , T > > ,
237
+ private getMetadata < T extends { target : Function ; propertyName : string | undefined } > (
238
+ metadatas : Map < Function , Map < string , T [ ] > > ,
170
239
target : Function
171
240
) : T [ ] {
172
241
const metadataFromTargetMap = metadatas . get ( target ) ;
173
- let metadataFromTarget : T [ ] ;
242
+ let metadataFromTarget : T [ ] = [ ] ;
174
243
if ( metadataFromTargetMap ) {
175
- metadataFromTarget = Array . from ( metadataFromTargetMap . values ( ) ) . filter ( meta => meta . propertyName !== undefined ) ;
244
+ metadataFromTarget = flatten ( Array . from ( metadataFromTargetMap . values ( ) ) ) . filter (
245
+ meta => meta . propertyName !== undefined
246
+ ) ;
176
247
}
177
248
const metadataFromAncestors : T [ ] = [ ] ;
178
249
for ( const ancestor of this . getAncestors ( target ) ) {
179
250
const ancestorMetadataMap = metadatas . get ( ancestor ) ;
180
251
if ( ancestorMetadataMap ) {
181
- const metadataFromAncestor = Array . from ( ancestorMetadataMap . values ( ) ) . filter (
252
+ const metadataFromAncestor = flatten ( Array . from ( ancestorMetadataMap . values ( ) ) ) . filter (
182
253
meta => meta . propertyName !== undefined
183
254
) ;
184
255
metadataFromAncestors . push ( ...metadataFromAncestor ) ;
185
256
}
186
257
}
187
- return metadataFromAncestors . concat ( metadataFromTarget || [ ] ) ;
258
+ return metadataFromAncestors . concat ( metadataFromTarget ) ;
188
259
}
189
260
190
261
private findMetadata < T extends { target : Function ; propertyName : string } > (
@@ -211,15 +282,15 @@ export class MetadataStorage {
211
282
return undefined ;
212
283
}
213
284
214
- private findMetadatas < T extends { target : Function ; propertyName : string } > (
285
+ private findMetadatas < T extends { target : Function ; propertyName : string | undefined } > (
215
286
metadatas : Map < Function , Map < string , T [ ] > > ,
216
287
target : Function ,
217
288
propertyName : string
218
289
) : T [ ] {
219
290
const metadataFromTargetMap = metadatas . get ( target ) ;
220
- let metadataFromTarget : T [ ] ;
291
+ let metadataFromTarget : T [ ] = [ ] ;
221
292
if ( metadataFromTargetMap ) {
222
- metadataFromTarget = metadataFromTargetMap . get ( propertyName ) ;
293
+ metadataFromTarget = metadataFromTargetMap . get ( propertyName ) ?? [ ] ;
223
294
}
224
295
const metadataFromAncestorsTarget : T [ ] = [ ] ;
225
296
for ( const ancestor of this . getAncestors ( target ) ) {
@@ -230,10 +301,7 @@ export class MetadataStorage {
230
301
}
231
302
}
232
303
}
233
- return metadataFromAncestorsTarget
234
- . slice ( )
235
- . reverse ( )
236
- . concat ( ( metadataFromTarget || [ ] ) . slice ( ) . reverse ( ) ) ;
304
+ return metadataFromAncestorsTarget . slice ( ) . reverse ( ) . concat ( metadataFromTarget . slice ( ) . reverse ( ) ) ;
237
305
}
238
306
239
307
private getAncestors ( target : Function ) : Function [ ] {
0 commit comments