mapper-factory 4.0.0 → 4.0.1
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/field-decorators/field.decorator.d.ts +0 -1
- package/dist/field-decorators/field.decorator.js +5 -6
- package/dist/functions.js +56 -123
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/test.js +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +43 -0
- package/package.json +1 -4
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
1
|
export const MAP_FIELD = Symbol('MAP_FIELD');
|
|
3
2
|
export function isClass(func) {
|
|
4
3
|
return (typeof func === 'function' &&
|
|
@@ -11,7 +10,7 @@ export const MapField = ({ transformer, reverser, src, initialize = false, } = {
|
|
|
11
10
|
return (target, property) => {
|
|
12
11
|
const classConstructor = target.constructor;
|
|
13
12
|
const propertyName = property.toString();
|
|
14
|
-
const metadata =
|
|
13
|
+
const metadata = classConstructor[MAP_FIELD] || {};
|
|
15
14
|
// create new object reference to avoid this issue: https://github.com/rbuckton/reflect-metadata/issues/62
|
|
16
15
|
const newMetadata = { ...metadata };
|
|
17
16
|
const previousValues = metadata[propertyName];
|
|
@@ -22,14 +21,14 @@ export const MapField = ({ transformer, reverser, src, initialize = false, } = {
|
|
|
22
21
|
transformer,
|
|
23
22
|
reverser,
|
|
24
23
|
};
|
|
25
|
-
|
|
24
|
+
classConstructor[MAP_FIELD] = newMetadata;
|
|
26
25
|
};
|
|
27
26
|
};
|
|
28
27
|
export const getMapFieldMetadataList = (target) => {
|
|
29
|
-
return
|
|
28
|
+
return getPrototype(target)[MAP_FIELD];
|
|
30
29
|
};
|
|
31
30
|
export const hasMapFieldMetadataList = (target) => {
|
|
32
|
-
return
|
|
31
|
+
return !!getPrototype(target)[MAP_FIELD];
|
|
33
32
|
};
|
|
34
33
|
export const getMapFieldMetadata = (target, propertyName) => {
|
|
35
34
|
const metadata = getMapFieldMetadataList(target);
|
|
@@ -39,6 +38,6 @@ export const getMapFieldMetadata = (target, propertyName) => {
|
|
|
39
38
|
return metadata[name];
|
|
40
39
|
};
|
|
41
40
|
export const hasMapFieldMetadata = (target, propertyName) => {
|
|
42
|
-
const metadata =
|
|
41
|
+
const metadata = getPrototype(target)[MAP_FIELD];
|
|
43
42
|
return metadata && !!metadata[propertyName];
|
|
44
43
|
};
|
package/dist/functions.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getMapFieldMetadataList } from "./field-decorators/field.decorator";
|
|
2
|
+
import { getValueByPath, setValueByPath } from "./utils";
|
|
2
3
|
/**
|
|
3
4
|
* Convert the instance of this class to JSON Object.
|
|
4
5
|
*
|
|
@@ -6,68 +7,35 @@ import { getMapFieldMetadataList } from "./field-decorators/field.decorator";
|
|
|
6
7
|
*/
|
|
7
8
|
export function toMap() {
|
|
8
9
|
const metadataList = getMapFieldMetadataList(this);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
let lastIndex;
|
|
12
|
-
for (let i = 0; i < propsStereoid.length; i++) {
|
|
13
|
-
const prop = propsStereoid[i];
|
|
14
|
-
if (prop.isArray) {
|
|
15
|
-
let arrIndex = prop.arrIndex
|
|
16
|
-
.split(/\[(\w+)\]/g)
|
|
17
|
-
.filter((index) => index !== "");
|
|
18
|
-
objCopy[prop.prop] = objCopy[prop.prop] || [];
|
|
19
|
-
objCopy = objCopy[prop.prop];
|
|
20
|
-
arrIndex.forEach((index, i) => {
|
|
21
|
-
objCopy[index] =
|
|
22
|
-
objCopy[index] || (i == arrIndex.length - 1 ? {} : []);
|
|
23
|
-
if (i != arrIndex.length - 1)
|
|
24
|
-
objCopy = objCopy[index];
|
|
25
|
-
else
|
|
26
|
-
lastIndex = index;
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
objCopy[prop.prop] = objCopy[prop.prop] || {};
|
|
31
|
-
if (i != propsStereoid.length - 1)
|
|
32
|
-
objCopy = objCopy[prop.prop];
|
|
33
|
-
else
|
|
34
|
-
lastIndex = prop.prop;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
objCopy[lastIndex] = reverser ? reverser(value, this) : value;
|
|
38
|
-
};
|
|
39
|
-
this &&
|
|
10
|
+
const obj = {};
|
|
11
|
+
if (this) {
|
|
40
12
|
Object.keys(this).forEach((propertyName) => {
|
|
41
13
|
const metadata = metadataList && metadataList[propertyName];
|
|
42
14
|
const src = metadata?.src || propertyName;
|
|
15
|
+
const value = this[propertyName];
|
|
16
|
+
let finalValue;
|
|
43
17
|
if (metadata) {
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}));
|
|
53
|
-
processProperty(obj, propsStereoid, this[propertyName], metadata.reverser);
|
|
18
|
+
if (Array.isArray(value) && !metadata.reverser) {
|
|
19
|
+
finalValue = value.map((item) => item?.toMap ? item.toMap() : item);
|
|
20
|
+
}
|
|
21
|
+
else if (metadata.reverser) {
|
|
22
|
+
finalValue = metadata.reverser(value, this);
|
|
23
|
+
}
|
|
24
|
+
else if (value?.toMap) {
|
|
25
|
+
finalValue = value.toMap();
|
|
54
26
|
}
|
|
55
27
|
else {
|
|
56
|
-
|
|
57
|
-
Array.isArray(this[propertyName]) && !metadata.reverser
|
|
58
|
-
? this[propertyName].map((item) => item?.toMap ? item.toMap() : item)
|
|
59
|
-
: metadata.reverser
|
|
60
|
-
? metadata.reverser(this[propertyName], this)
|
|
61
|
-
: this[propertyName]?.toMap
|
|
62
|
-
? this[propertyName].toMap()
|
|
63
|
-
: this[propertyName];
|
|
28
|
+
finalValue = value;
|
|
64
29
|
}
|
|
65
30
|
}
|
|
66
31
|
else {
|
|
67
|
-
|
|
68
|
-
|
|
32
|
+
finalValue = value;
|
|
33
|
+
}
|
|
34
|
+
if (finalValue !== undefined) {
|
|
35
|
+
setValueByPath(obj, src, finalValue);
|
|
69
36
|
}
|
|
70
37
|
});
|
|
38
|
+
}
|
|
71
39
|
return obj;
|
|
72
40
|
}
|
|
73
41
|
/**
|
|
@@ -111,8 +79,7 @@ export function filled() {
|
|
|
111
79
|
* @returns Value of the property
|
|
112
80
|
*/
|
|
113
81
|
export function get(path) {
|
|
114
|
-
|
|
115
|
-
return props.reduce((acc, prop) => acc && acc[prop], this);
|
|
82
|
+
return getValueByPath(this, path);
|
|
116
83
|
}
|
|
117
84
|
/**
|
|
118
85
|
* SET property value from a string path.
|
|
@@ -121,14 +88,7 @@ export function get(path) {
|
|
|
121
88
|
* @param value Value of the property
|
|
122
89
|
*/
|
|
123
90
|
export function set(path, value) {
|
|
124
|
-
|
|
125
|
-
let obj = this;
|
|
126
|
-
props.slice(0, -1).forEach((prop) => {
|
|
127
|
-
if (!obj[prop])
|
|
128
|
-
obj[prop] = {};
|
|
129
|
-
obj = obj[prop];
|
|
130
|
-
});
|
|
131
|
-
obj[props[props.length - 1]] = value;
|
|
91
|
+
setValueByPath(this, path, value);
|
|
132
92
|
}
|
|
133
93
|
/**
|
|
134
94
|
* Deep copy of the object caller
|
|
@@ -143,77 +103,50 @@ export function copy() {
|
|
|
143
103
|
*/
|
|
144
104
|
export function from(object) {
|
|
145
105
|
const metadataList = getMapFieldMetadataList(this);
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
};
|
|
164
|
-
const setProperty = (metaKey, value, object) => {
|
|
165
|
-
const metaProp = metadataList?.[metaKey];
|
|
166
|
-
if (metaProp?.transformer) {
|
|
167
|
-
const valueTransformed = metaProp.transformer(value, object);
|
|
168
|
-
if (valueTransformed != undefined)
|
|
169
|
-
this[metaKey] = valueTransformed;
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
if (value != undefined)
|
|
173
|
-
this[metaKey] = value;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
object &&
|
|
177
|
-
Object.keys(object).forEach((propertyName) => {
|
|
178
|
-
let metaKeys = metadataList &&
|
|
179
|
-
Object.keys(metadataList).filter((metadata) => metadataList[metadata]?.src?.split(".")?.includes(propertyName));
|
|
180
|
-
if (metaKeys?.length) {
|
|
181
|
-
metaKeys.forEach((metaKey) => {
|
|
182
|
-
const metaProp = metadataList?.[metaKey];
|
|
183
|
-
if (metaProp) {
|
|
184
|
-
const props = metaProp.src.split(".");
|
|
185
|
-
const propsStereoid = props.map((prop) => ({
|
|
186
|
-
prop: prop.includes("[")
|
|
187
|
-
? prop.substring(0, prop.indexOf("["))
|
|
188
|
-
: prop,
|
|
189
|
-
isArray: prop.includes("[") && prop.includes("]"),
|
|
190
|
-
arrIndex: prop.substring(prop.indexOf("[")),
|
|
191
|
-
}));
|
|
192
|
-
const value = processProperty({ ...object }, propsStereoid);
|
|
193
|
-
setProperty(metaKey, value, object);
|
|
106
|
+
const mappedSrcRoots = new Set();
|
|
107
|
+
// 1. Process Metadata
|
|
108
|
+
if (metadataList && object) {
|
|
109
|
+
Object.keys(metadataList).forEach((key) => {
|
|
110
|
+
const meta = metadataList[key];
|
|
111
|
+
const src = meta.src || key;
|
|
112
|
+
// Identify root property for this mapping to exclude it from direct copy later
|
|
113
|
+
const normalizedPath = src.replace(/\[(\w+)\]/g, ".$1");
|
|
114
|
+
const root = normalizedPath.split(".")[0];
|
|
115
|
+
mappedSrcRoots.add(root);
|
|
116
|
+
const value = getValueByPath(object, src);
|
|
117
|
+
// Only process if value exists in source (matches old behavior and avoids infinite recursion on undefined)
|
|
118
|
+
if (value !== undefined) {
|
|
119
|
+
if (meta.transformer) {
|
|
120
|
+
const transformed = meta.transformer(value, object);
|
|
121
|
+
if (transformed !== undefined) {
|
|
122
|
+
this[key] = transformed;
|
|
194
123
|
}
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
let metaKey = metadataList &&
|
|
199
|
-
Object.keys(metadataList).find((metadata) => metadataList[metadata]?.src == propertyName);
|
|
200
|
-
if (metaKey) {
|
|
201
|
-
const src = metadataList?.[metaKey].src || propertyName;
|
|
202
|
-
setProperty(metaKey, object[src], object);
|
|
203
124
|
}
|
|
204
125
|
else {
|
|
205
|
-
|
|
126
|
+
this[key] = value;
|
|
206
127
|
}
|
|
207
128
|
}
|
|
208
129
|
});
|
|
209
|
-
|
|
210
|
-
|
|
130
|
+
}
|
|
131
|
+
// 2. Process Remaining Properties (Direct Copy)
|
|
132
|
+
// Only if they are not part of a mapped source root
|
|
133
|
+
if (object) {
|
|
134
|
+
Object.keys(object).forEach(prop => {
|
|
135
|
+
if (!mappedSrcRoots.has(prop)) {
|
|
136
|
+
this[prop] = object[prop];
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// 3. Initialize properties with "initialize" metadata
|
|
141
|
+
if (metadataList) {
|
|
211
142
|
Object.keys(metadataList).forEach((metaName) => {
|
|
212
|
-
|
|
213
|
-
|
|
143
|
+
const meta = metadataList[metaName];
|
|
144
|
+
if (meta?.initialize &&
|
|
145
|
+
meta?.transformer &&
|
|
214
146
|
this[metaName] === undefined) {
|
|
215
|
-
this[metaName] =
|
|
147
|
+
this[metaName] = meta.transformer(null, object);
|
|
216
148
|
}
|
|
217
149
|
});
|
|
150
|
+
}
|
|
218
151
|
return this;
|
|
219
152
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ClassType } from "./types";
|
|
|
7
7
|
export { ClassType, MapInterface, MapClass, MapField, DateField, ArrayField, ObjectField, };
|
|
8
8
|
/**
|
|
9
9
|
* npx tsc
|
|
10
|
-
* npx
|
|
10
|
+
* npx tsx src/test.ts
|
|
11
11
|
* npm version ( patch | minor | major )
|
|
12
12
|
* npm publish
|
|
13
13
|
*/
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ObjectField } from "./field-decorators/object.decorator";
|
|
|
6
6
|
export { MapClass, MapField, DateField, ArrayField, ObjectField, };
|
|
7
7
|
/**
|
|
8
8
|
* npx tsc
|
|
9
|
-
* npx
|
|
9
|
+
* npx tsx src/test.ts
|
|
10
10
|
* npm version ( patch | minor | major )
|
|
11
11
|
* npm publish
|
|
12
12
|
*/
|
package/dist/test.js
CHANGED
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
//// import "reflect-metadata";
|
|
10
11
|
import { MapClass } from "./class.decorator";
|
|
11
12
|
import { ArrayField } from "./field-decorators/array.decorator";
|
|
12
13
|
import { DateField } from "./field-decorators/date.decorator";
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieve a value from an object using a string path (dot notation).
|
|
3
|
+
* Supports array indexing e.g. "users[0].name" or "users.0.name".
|
|
4
|
+
*/
|
|
5
|
+
export declare function getValueByPath(obj: any, path: string): any;
|
|
6
|
+
/**
|
|
7
|
+
* Set a value on an object using a string path (dot notation).
|
|
8
|
+
* Creates nested objects/arrays if they don't exist.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setValueByPath(obj: any, path: string, value: any): void;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieve a value from an object using a string path (dot notation).
|
|
3
|
+
* Supports array indexing e.g. "users[0].name" or "users.0.name".
|
|
4
|
+
*/
|
|
5
|
+
export function getValueByPath(obj, path) {
|
|
6
|
+
if (obj == null || !path)
|
|
7
|
+
return undefined;
|
|
8
|
+
// Normalize path: "a[0].b" -> "a.0.b"
|
|
9
|
+
const normalizedPath = path.replace(/\[(\w+)\]/g, ".$1");
|
|
10
|
+
const parts = normalizedPath.split(".");
|
|
11
|
+
let current = obj;
|
|
12
|
+
for (const part of parts) {
|
|
13
|
+
if (current == null)
|
|
14
|
+
return undefined;
|
|
15
|
+
current = current[part];
|
|
16
|
+
}
|
|
17
|
+
return current;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Set a value on an object using a string path (dot notation).
|
|
21
|
+
* Creates nested objects/arrays if they don't exist.
|
|
22
|
+
*/
|
|
23
|
+
export function setValueByPath(obj, path, value) {
|
|
24
|
+
if (obj == null || !path)
|
|
25
|
+
return;
|
|
26
|
+
const normalizedPath = path.replace(/\[(\w+)\]/g, ".$1");
|
|
27
|
+
const parts = normalizedPath.split(".");
|
|
28
|
+
let current = obj;
|
|
29
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
30
|
+
const part = parts[i];
|
|
31
|
+
// If the property doesn't exist, create it.
|
|
32
|
+
// Look ahead to decide if we need an array or an object.
|
|
33
|
+
if (current[part] == null) {
|
|
34
|
+
const nextPart = parts[i + 1];
|
|
35
|
+
// If next part is an integer, assume array.
|
|
36
|
+
const isNextIndex = !isNaN(parseInt(nextPart)) && isFinite(parseInt(nextPart));
|
|
37
|
+
current[part] = isNextIndex ? [] : {};
|
|
38
|
+
}
|
|
39
|
+
current = current[part];
|
|
40
|
+
}
|
|
41
|
+
const lastPart = parts[parts.length - 1];
|
|
42
|
+
current[lastPart] = value;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mapper-factory",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "mapper for typescript object",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,9 +41,6 @@
|
|
|
41
41
|
"url": "https://github.com/lucaAngrisani/mapper-factory/issues"
|
|
42
42
|
},
|
|
43
43
|
"homepage": "https://github.com/lucaAngrisani/mapper-factory#readme",
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"reflect-metadata": "^0.2.2"
|
|
46
|
-
},
|
|
47
44
|
"devDependencies": {
|
|
48
45
|
"typescript": "^5.7.3"
|
|
49
46
|
}
|