UNPKG

15.4 kBJavaScriptView Raw
1"use strict";
2var storage_1 = require("./storage");
3var TransformOperationExecutor = (function () {
4 // -------------------------------------------------------------------------
5 // Constructor
6 // -------------------------------------------------------------------------
7 function TransformOperationExecutor(transformationType, options) {
8 this.transformationType = transformationType;
9 this.options = options;
10 // -------------------------------------------------------------------------
11 // Private Properties
12 // -------------------------------------------------------------------------
13 this.transformedTypes = [];
14 }
15 // -------------------------------------------------------------------------
16 // Public Methods
17 // -------------------------------------------------------------------------
18 TransformOperationExecutor.prototype.transform = function (source, value, targetType, arrayType, isMap, level) {
19 var _this = this;
20 if (level === void 0) { level = 0; }
21 if (value instanceof Array || value instanceof Set) {
22 var newValue_1 = arrayType && this.transformationType === "plainToClass" ? new arrayType() : [];
23 value.forEach(function (subValue, index) {
24 var subSource = source ? source[index] : undefined;
25 if (!_this.isCircular(subValue, level)) {
26 var value_1 = _this.transform(subSource, subValue, targetType, undefined, subValue instanceof Map, level + 1);
27 if (newValue_1 instanceof Set) {
28 newValue_1.add(value_1);
29 }
30 else {
31 newValue_1.push(value_1);
32 }
33 }
34 else if (_this.transformationType === "classToClass") {
35 if (newValue_1 instanceof Set) {
36 newValue_1.add(subValue);
37 }
38 else {
39 newValue_1.push(subValue);
40 }
41 }
42 });
43 return newValue_1;
44 }
45 else if (targetType === String && !isMap) {
46 return String(value);
47 }
48 else if (targetType === Number && !isMap) {
49 return Number(value);
50 }
51 else if (targetType === Boolean && !isMap) {
52 return Boolean(value);
53 }
54 else if ((targetType === Date || value instanceof Date) && !isMap) {
55 if (value instanceof Date) {
56 return new Date(value.valueOf());
57 }
58 if (value === null || value === undefined)
59 return value;
60 return new Date(value);
61 }
62 else if (value instanceof Object) {
63 // try to guess the type
64 if (!targetType && value.constructor !== Object /* && operationType === "classToPlain"*/)
65 targetType = value.constructor;
66 if (!targetType && source)
67 targetType = source.constructor;
68 // add transformed type to prevent circular references
69 this.transformedTypes.push({ level: level, object: value });
70 var keys = this.getKeys(targetType, value);
71 var newValue = source ? source : {};
72 if (!source && (this.transformationType === "plainToClass" || this.transformationType === "classToClass")) {
73 if (isMap) {
74 newValue = new Map();
75 }
76 else if (targetType) {
77 newValue = new targetType();
78 }
79 else {
80 newValue = {};
81 }
82 }
83 // traverse over keys
84 var _loop_1 = function(key) {
85 var valueKey = key, newValueKey = key, propertyName = key;
86 if (!this_1.options.ignoreDecorators && targetType) {
87 if (this_1.transformationType === "plainToClass") {
88 var exposeMetadata = storage_1.defaultMetadataStorage.findExposeMetadataByCustomName(targetType, key);
89 if (exposeMetadata) {
90 propertyName = exposeMetadata.propertyName;
91 newValueKey = exposeMetadata.propertyName;
92 }
93 }
94 else if (this_1.transformationType === "classToPlain" || this_1.transformationType === "classToClass") {
95 var exposeMetadata = storage_1.defaultMetadataStorage.findExposeMetadata(targetType, key);
96 if (exposeMetadata && exposeMetadata.options && exposeMetadata.options.name)
97 newValueKey = exposeMetadata.options.name;
98 }
99 }
100 // get a subvalue
101 var subValue = undefined;
102 if (value instanceof Map) {
103 subValue = value.get(valueKey);
104 }
105 else if (value[valueKey] instanceof Function) {
106 subValue = value[valueKey]();
107 }
108 else {
109 subValue = value[valueKey];
110 }
111 // determine a type
112 var type = undefined, isSubValueMap = subValue instanceof Map;
113 if (targetType && isMap) {
114 type = targetType;
115 }
116 else if (targetType) {
117 var metadata = storage_1.defaultMetadataStorage.findTypeMetadata(targetType, propertyName);
118 if (metadata) {
119 var options = { newObject: newValue, object: value, property: propertyName };
120 type = metadata.typeFunction(options);
121 isSubValueMap = isSubValueMap || metadata.reflectedType === Map;
122 }
123 else if (this_1.options.targetMaps) {
124 this_1.options.targetMaps
125 .filter(function (map) { return map.target === targetType && !!map.properties[propertyName]; })
126 .forEach(function (map) { return type = map.properties[propertyName]; });
127 }
128 }
129 // if value is an array try to get its custom array type
130 var arrayType_1 = value[valueKey] instanceof Array ? this_1.getReflectedType(targetType, propertyName) : undefined;
131 // const subValueKey = operationType === "plainToClass" && newKeyName ? newKeyName : key;
132 var subSource = source ? source[valueKey] : undefined;
133 // if its deserialization then type if required
134 // if we uncomment this types like string[] will not work
135 // if (this.transformationType === "plainToClass" && !type && subValue instanceof Object && !(subValue instanceof Date))
136 // throw new Error(`Cannot determine type for ${(targetType as any).name }.${propertyName}, did you forget to specify a @Type?`);
137 // if newValue is a source object that has method that match newKeyName then skip it
138 if (newValue.constructor.prototype) {
139 var descriptor = Object.getOwnPropertyDescriptor(newValue.constructor.prototype, newValueKey);
140 if ((this_1.transformationType === "plainToClass" || this_1.transformationType === "classToClass")
141 && (newValue[newValueKey] instanceof Function || (descriptor && !descriptor.set)))
142 return "continue";
143 }
144 if (!this_1.isCircular(subValue, level)) {
145 var finalValue = this_1.transform(subSource, subValue, type, arrayType_1, isSubValueMap, level + 1);
146 finalValue = this_1.applyCustomTransformations(finalValue, targetType, key);
147 if (newValue instanceof Map) {
148 newValue.set(newValueKey, finalValue);
149 }
150 else {
151 newValue[newValueKey] = finalValue;
152 }
153 }
154 else if (this_1.transformationType === "classToClass") {
155 var finalValue = subValue;
156 finalValue = this_1.applyCustomTransformations(finalValue, targetType, key);
157 if (newValue instanceof Map) {
158 newValue.set(newValueKey, finalValue);
159 }
160 else {
161 newValue[newValueKey] = finalValue;
162 }
163 }
164 };
165 var this_1 = this;
166 for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
167 var key = keys_1[_i];
168 var state_1 = _loop_1(key);
169 if (state_1 === "continue") continue;
170 }
171 return newValue;
172 }
173 else {
174 return value;
175 }
176 };
177 TransformOperationExecutor.prototype.applyCustomTransformations = function (value, target, key) {
178 var _this = this;
179 var metadatas = storage_1.defaultMetadataStorage.findTransformMetadatas(target, key, this.transformationType);
180 // apply versioning options
181 if (this.options.version !== undefined) {
182 metadatas = metadatas.filter(function (metadata) {
183 if (!metadata.options)
184 return true;
185 return _this.checkVersion(metadata.options.since, metadata.options.until);
186 });
187 }
188 // apply grouping options
189 if (this.options.groups && this.options.groups.length) {
190 metadatas = metadatas.filter(function (metadata) {
191 if (!metadata.options)
192 return true;
193 return _this.checkGroups(metadata.options.groups);
194 });
195 }
196 else {
197 metadatas = metadatas.filter(function (metadata) {
198 return !metadata.options ||
199 !metadata.options.groups ||
200 !metadata.options.groups.length;
201 });
202 }
203 metadatas.forEach(function (metadata) {
204 value = metadata.transformFn(value);
205 });
206 return value;
207 };
208 // preventing circular references
209 TransformOperationExecutor.prototype.isCircular = function (object, level) {
210 return !!this.transformedTypes.find(function (transformed) { return transformed.object === object && transformed.level < level; });
211 };
212 TransformOperationExecutor.prototype.getReflectedType = function (target, propertyName) {
213 if (!target)
214 return undefined;
215 var meta = storage_1.defaultMetadataStorage.findTypeMetadata(target, propertyName);
216 return meta ? meta.reflectedType : undefined;
217 };
218 TransformOperationExecutor.prototype.getKeys = function (target, object) {
219 var _this = this;
220 // determine exclusion strategy
221 var strategy = storage_1.defaultMetadataStorage.getStrategy(target);
222 if (strategy === "none")
223 strategy = this.options.strategy || "exposeAll"; // exposeAll is default strategy
224 // get all keys that need to expose
225 var keys = [];
226 if (strategy === "exposeAll") {
227 if (object instanceof Map) {
228 keys = Array.from(object.keys());
229 }
230 else {
231 keys = Object.keys(object);
232 }
233 }
234 if (!this.options.ignoreDecorators && target) {
235 // add all exposed to list of keys
236 var exposedProperties = storage_1.defaultMetadataStorage.getExposedProperties(target, this.transformationType);
237 if (this.transformationType === "plainToClass") {
238 exposedProperties = exposedProperties.map(function (key) {
239 var exposeMetadata = storage_1.defaultMetadataStorage.findExposeMetadata(target, key);
240 if (exposeMetadata && exposeMetadata.options && exposeMetadata.options.name) {
241 return exposeMetadata.options.name;
242 }
243 return key;
244 });
245 }
246 keys = keys.concat(exposedProperties);
247 // exclude excluded properties
248 var excludedProperties_1 = storage_1.defaultMetadataStorage.getExcludedProperties(target, this.transformationType);
249 if (excludedProperties_1.length > 0) {
250 keys = keys.filter(function (key) {
251 return excludedProperties_1.indexOf(key) === -1;
252 });
253 }
254 // apply versioning options
255 if (this.options.version !== undefined) {
256 keys = keys.filter(function (key) {
257 var exposeMetadata = storage_1.defaultMetadataStorage.findExposeMetadata(target, key);
258 if (!exposeMetadata || !exposeMetadata.options)
259 return true;
260 return _this.checkVersion(exposeMetadata.options.since, exposeMetadata.options.until);
261 });
262 }
263 // apply grouping options
264 if (this.options.groups && this.options.groups.length) {
265 keys = keys.filter(function (key) {
266 var exposeMetadata = storage_1.defaultMetadataStorage.findExposeMetadata(target, key);
267 if (!exposeMetadata || !exposeMetadata.options)
268 return true;
269 return _this.checkGroups(exposeMetadata.options.groups);
270 });
271 }
272 else {
273 keys = keys.filter(function (key) {
274 var exposeMetadata = storage_1.defaultMetadataStorage.findExposeMetadata(target, key);
275 return !exposeMetadata ||
276 !exposeMetadata.options ||
277 !exposeMetadata.options.groups ||
278 !exposeMetadata.options.groups.length;
279 });
280 }
281 }
282 // exclude prefixed properties
283 if (this.options.excludePrefixes && this.options.excludePrefixes.length) {
284 keys = keys.filter(function (key) { return _this.options.excludePrefixes.every(function (prefix) {
285 return key.substr(0, prefix.length) !== prefix;
286 }); });
287 }
288 // make sure we have unique keys
289 keys = keys.filter(function (key, index, self) {
290 return self.indexOf(key) === index;
291 });
292 return keys;
293 };
294 TransformOperationExecutor.prototype.checkVersion = function (since, until) {
295 var decision = true;
296 if (decision && since)
297 decision = this.options.version >= since;
298 if (decision && until)
299 decision = this.options.version < until;
300 return decision;
301 };
302 TransformOperationExecutor.prototype.checkGroups = function (groups) {
303 if (!groups)
304 return true;
305 return this.options.groups.some(function (optionGroup) { return groups.indexOf(optionGroup) !== -1; });
306 };
307 return TransformOperationExecutor;
308}());
309exports.TransformOperationExecutor = TransformOperationExecutor;
310
311//# sourceMappingURL=TransformOperationExecutor.js.map