mapper-factory 4.0.0 → 4.0.2
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/README.md +215 -215
- package/dist/class.decorator.js +1 -1
- package/dist/field-decorators/array.decorator.d.ts +1 -1
- package/dist/field-decorators/array.decorator.js +1 -3
- package/dist/field-decorators/date.decorator.d.ts +1 -1
- package/dist/field-decorators/field.decorator.d.ts +2 -3
- package/dist/field-decorators/field.decorator.js +12 -9
- package/dist/field-decorators/object.decorator.d.ts +1 -1
- package/dist/functions.d.ts +13 -8
- package/dist/functions.js +68 -124
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/test.js +17 -28
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +43 -0
- package/package.json +47 -50
package/README.md
CHANGED
|
@@ -1,215 +1,215 @@
|
|
|
1
|
-
# Mapper-Factory
|
|
2
|
-
|
|
3
|
-
Mapper-Factory is a fully documented TypeScript library that provides a simple and easy-to-use way to map objects from one type to another. With just a few lines of code, you can convert complex, nested objects into the desired format.
|
|
4
|
-
|
|
5
|
-
Work well on data structure and after enjoy the coding process :)
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
To install the package, you can use npm by running the following command:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
npm i mapper-factory
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Or using yarn
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
yarn add mapper-factory
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Problem to solve
|
|
22
|
-
|
|
23
|
-
We want to solve the problem to trasform a JSON object to a specific JS object adding custom properties, with integrated mapping. As example this JSON object:
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
{
|
|
27
|
-
firstName: 'Rick',
|
|
28
|
-
lastName: 'Sanchez',
|
|
29
|
-
employees: [
|
|
30
|
-
{ firstName: 'Summer', lastName: 'Smith' },
|
|
31
|
-
{ firstName: 'Morty', lastName: 'Smith' }
|
|
32
|
-
],
|
|
33
|
-
rolesToMap: [ 'CEO', 'EMPLOYEE' ],
|
|
34
|
-
boss: { firstName: 'Jesus', lastName: 'Christ' }
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
must became a JS object:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
User {
|
|
42
|
-
name: 'Rick',
|
|
43
|
-
surname: 'Sanchez',
|
|
44
|
-
employees: [
|
|
45
|
-
User { name: 'Summer', surname: 'Smith' },
|
|
46
|
-
User { name: 'Morty', surname: 'Smith' }
|
|
47
|
-
],
|
|
48
|
-
roles: [ 'CEO TEST TRASFORMER', 'EMPLOYEE TEST TRASFORMER' ],
|
|
49
|
-
boss: User { name: 'Jesus', surname: 'Christ' }
|
|
50
|
-
/** maybe some methods... */
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Usage V2
|
|
55
|
-
|
|
56
|
-
### Mapping simple objects
|
|
57
|
-
|
|
58
|
-
Your class must use _MapClass_ decorator and set an interface that extends _MapInterface<T>_
|
|
59
|
-
In this way your class could extend another, but continue using intellisense methods for YourClass
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
@MapClass()
|
|
63
|
-
class YourClass extends YourCustomClassToExtend {
|
|
64
|
-
/** YOUR PROPERTIES */
|
|
65
|
-
}
|
|
66
|
-
interface YourClass extends MapInterface<YourClass> { }
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
To get new _YourClass_ instance it's simple as always:
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
new YourClass();
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
but now you can easly mapping an object just using _MapInterface_ method _from_:
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
const yourInstance: YourClass = new YourClass().from(/** YOUR OBJECT TO MAP HERE*/);
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
and reverse your mapping using using _MapInterface_ method _toMap_:
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
const reversedMapping: Object = yourInstance.toMap();
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
With _MapInterface_ you can use on your class instance also other methods:
|
|
88
|
-
|
|
89
|
-
- **_from_**: Create a new instance using model to map
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
const yourInstance: YourClass = new YourClass().from(/** YOUR OBJECT TO MAP HERE */);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- **_toMap_**: Create a JSON object using reverse mapping
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
const reverseMappedObject: Object = yourInstance.toMap();
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
- **_toModel_**: Create a new instance using same model of final JS object
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
const anotherInstance: YourClass = new YourClass.toModel(yourInstance);
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
- **_empty_**: Check if the object is empty
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
const isEmpty: boolean = yourInstance.empty();
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
- **_filled_**: Check if the object is filled
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
const isFilled: boolean = yourInstance.filled();
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
- **_get_**: Get specific property of the object
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
const specificProp: T = yourInstance.get('specificProp') as T;
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
- **_set_**: Set specific property of the object
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
yourInstance.set('specificProp', /** NEW VALUE FOR 'specificProp' */);
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
- **_copy_**: Deep copy of the object
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
const yourInstanceCopy: YourClass = yourInstance.copy();
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
After that, you can use _@MapField_ decorator over single property to specify the mapping. Let's dive into an example:
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
@MapClass()
|
|
141
|
-
class User {
|
|
142
|
-
|
|
143
|
-
@MapField({
|
|
144
|
-
src: 'firstName'
|
|
145
|
-
})
|
|
146
|
-
name: string;
|
|
147
|
-
|
|
148
|
-
@MapField({
|
|
149
|
-
src: 'obj.obj[0][1]',
|
|
150
|
-
transformer: (arr) => arr.map(role => role + " TEST TRASFORMER"),
|
|
151
|
-
reverser: (arr) => arr.map(role => role.replace(" TEST TRASFORMER", "")),
|
|
152
|
-
})
|
|
153
|
-
roles?: string[];
|
|
154
|
-
|
|
155
|
-
@MapField({
|
|
156
|
-
transformer: (user) => new User(user)
|
|
157
|
-
})
|
|
158
|
-
boss: User;
|
|
159
|
-
}
|
|
160
|
-
interface User extends MapInterface<User> { }
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
Inside _@MapField_ you can use:
|
|
164
|
-
|
|
165
|
-
- **_src_**: define a string of original field name (also using a path like _"obj.obj[0][1]"_)
|
|
166
|
-
- **_transform_**: function to transform data input in _constructor_ of the class
|
|
167
|
-
- **_reverse_**: function to transform data input in _toMap_ method of the class
|
|
168
|
-
|
|
169
|
-
In this example:
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
@MapClass()
|
|
173
|
-
class User {
|
|
174
|
-
|
|
175
|
-
id: string;
|
|
176
|
-
username: string;
|
|
177
|
-
|
|
178
|
-
@MapField({
|
|
179
|
-
src: 'firstName'
|
|
180
|
-
})
|
|
181
|
-
name: string;
|
|
182
|
-
|
|
183
|
-
@MapField({
|
|
184
|
-
src: 'lastName'
|
|
185
|
-
})
|
|
186
|
-
surname: string;
|
|
187
|
-
|
|
188
|
-
@MapField({
|
|
189
|
-
src: 'rolesToMap',
|
|
190
|
-
transformer: (arr) => arr.map(role => role + " TEST TRASFORMER"),
|
|
191
|
-
reverser: (arr) => arr.map(role => role.replace(" TEST TRASFORMER", "")),
|
|
192
|
-
})
|
|
193
|
-
roles?: string[];
|
|
194
|
-
|
|
195
|
-
@MapField({
|
|
196
|
-
transformer: (arr) => arr.map(user => new User(user))
|
|
197
|
-
})
|
|
198
|
-
employees?: User[];
|
|
199
|
-
|
|
200
|
-
@MapField({
|
|
201
|
-
transformer: (user) => new User(user)
|
|
202
|
-
})
|
|
203
|
-
boss?: User;
|
|
204
|
-
}
|
|
205
|
-
interface User extends MapInterface<User> { }
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
You can define a new User **_u_**, using two employees (using the same User model here but for a different object is the same):
|
|
209
|
-
|
|
210
|
-
```
|
|
211
|
-
let emp1: User = new User().from({ firstName: "Summer", lastName: "Smith" });
|
|
212
|
-
let emp2: User = new User().from({ firstName: "Morty", lastName: "Smith" });
|
|
213
|
-
|
|
214
|
-
let u = new User().from({ firstName: "Rick", lastName: "Sanchez", employees: [emp1.toMap(), emp2.toMap()], rolesToMap: ["CEO", "EMPLOYEE"] });
|
|
215
|
-
```
|
|
1
|
+
# Mapper-Factory
|
|
2
|
+
|
|
3
|
+
Mapper-Factory is a fully documented TypeScript library that provides a simple and easy-to-use way to map objects from one type to another. With just a few lines of code, you can convert complex, nested objects into the desired format.
|
|
4
|
+
|
|
5
|
+
Work well on data structure and after enjoy the coding process :)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
To install the package, you can use npm by running the following command:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
npm i mapper-factory
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or using yarn
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
yarn add mapper-factory
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Problem to solve
|
|
22
|
+
|
|
23
|
+
We want to solve the problem to trasform a JSON object to a specific JS object adding custom properties, with integrated mapping. As example this JSON object:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
{
|
|
27
|
+
firstName: 'Rick',
|
|
28
|
+
lastName: 'Sanchez',
|
|
29
|
+
employees: [
|
|
30
|
+
{ firstName: 'Summer', lastName: 'Smith' },
|
|
31
|
+
{ firstName: 'Morty', lastName: 'Smith' }
|
|
32
|
+
],
|
|
33
|
+
rolesToMap: [ 'CEO', 'EMPLOYEE' ],
|
|
34
|
+
boss: { firstName: 'Jesus', lastName: 'Christ' }
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
must became a JS object:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
User {
|
|
42
|
+
name: 'Rick',
|
|
43
|
+
surname: 'Sanchez',
|
|
44
|
+
employees: [
|
|
45
|
+
User { name: 'Summer', surname: 'Smith' },
|
|
46
|
+
User { name: 'Morty', surname: 'Smith' }
|
|
47
|
+
],
|
|
48
|
+
roles: [ 'CEO TEST TRASFORMER', 'EMPLOYEE TEST TRASFORMER' ],
|
|
49
|
+
boss: User { name: 'Jesus', surname: 'Christ' }
|
|
50
|
+
/** maybe some methods... */
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage V2
|
|
55
|
+
|
|
56
|
+
### Mapping simple objects
|
|
57
|
+
|
|
58
|
+
Your class must use _MapClass_ decorator and set an interface that extends _MapInterface<T>_
|
|
59
|
+
In this way your class could extend another, but continue using intellisense methods for YourClass
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
@MapClass()
|
|
63
|
+
class YourClass extends YourCustomClassToExtend {
|
|
64
|
+
/** YOUR PROPERTIES */
|
|
65
|
+
}
|
|
66
|
+
interface YourClass extends MapInterface<YourClass> { }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To get new _YourClass_ instance it's simple as always:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
new YourClass();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
but now you can easly mapping an object just using _MapInterface_ method _from_:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
const yourInstance: YourClass = new YourClass().from(/** YOUR OBJECT TO MAP HERE*/);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
and reverse your mapping using using _MapInterface_ method _toMap_:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
const reversedMapping: Object = yourInstance.toMap();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
With _MapInterface_ you can use on your class instance also other methods:
|
|
88
|
+
|
|
89
|
+
- **_from_**: Create a new instance using model to map
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
const yourInstance: YourClass = new YourClass().from(/** YOUR OBJECT TO MAP HERE */);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **_toMap_**: Create a JSON object using reverse mapping
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
const reverseMappedObject: Object = yourInstance.toMap();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- **_toModel_**: Create a new instance using same model of final JS object
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
const anotherInstance: YourClass = new YourClass.toModel(yourInstance);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- **_empty_**: Check if the object is empty
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
const isEmpty: boolean = yourInstance.empty();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- **_filled_**: Check if the object is filled
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
const isFilled: boolean = yourInstance.filled();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- **_get_**: Get specific property of the object
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
const specificProp: T = yourInstance.get('specificProp') as T;
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- **_set_**: Set specific property of the object
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
yourInstance.set('specificProp', /** NEW VALUE FOR 'specificProp' */);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- **_copy_**: Deep copy of the object
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
const yourInstanceCopy: YourClass = yourInstance.copy();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
After that, you can use _@MapField_ decorator over single property to specify the mapping. Let's dive into an example:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
@MapClass()
|
|
141
|
+
class User {
|
|
142
|
+
|
|
143
|
+
@MapField({
|
|
144
|
+
src: 'firstName'
|
|
145
|
+
})
|
|
146
|
+
name: string;
|
|
147
|
+
|
|
148
|
+
@MapField({
|
|
149
|
+
src: 'obj.obj[0][1]',
|
|
150
|
+
transformer: (arr) => arr.map(role => role + " TEST TRASFORMER"),
|
|
151
|
+
reverser: (arr) => arr.map(role => role.replace(" TEST TRASFORMER", "")),
|
|
152
|
+
})
|
|
153
|
+
roles?: string[];
|
|
154
|
+
|
|
155
|
+
@MapField({
|
|
156
|
+
transformer: (user) => new User(user)
|
|
157
|
+
})
|
|
158
|
+
boss: User;
|
|
159
|
+
}
|
|
160
|
+
interface User extends MapInterface<User> { }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Inside _@MapField_ you can use:
|
|
164
|
+
|
|
165
|
+
- **_src_**: define a string of original field name (also using a path like _"obj.obj[0][1]"_)
|
|
166
|
+
- **_transform_**: function to transform data input in _constructor_ of the class
|
|
167
|
+
- **_reverse_**: function to transform data input in _toMap_ method of the class
|
|
168
|
+
|
|
169
|
+
In this example:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
@MapClass()
|
|
173
|
+
class User {
|
|
174
|
+
|
|
175
|
+
id: string;
|
|
176
|
+
username: string;
|
|
177
|
+
|
|
178
|
+
@MapField({
|
|
179
|
+
src: 'firstName'
|
|
180
|
+
})
|
|
181
|
+
name: string;
|
|
182
|
+
|
|
183
|
+
@MapField({
|
|
184
|
+
src: 'lastName'
|
|
185
|
+
})
|
|
186
|
+
surname: string;
|
|
187
|
+
|
|
188
|
+
@MapField({
|
|
189
|
+
src: 'rolesToMap',
|
|
190
|
+
transformer: (arr) => arr.map(role => role + " TEST TRASFORMER"),
|
|
191
|
+
reverser: (arr) => arr.map(role => role.replace(" TEST TRASFORMER", "")),
|
|
192
|
+
})
|
|
193
|
+
roles?: string[];
|
|
194
|
+
|
|
195
|
+
@MapField({
|
|
196
|
+
transformer: (arr) => arr.map(user => new User(user))
|
|
197
|
+
})
|
|
198
|
+
employees?: User[];
|
|
199
|
+
|
|
200
|
+
@MapField({
|
|
201
|
+
transformer: (user) => new User(user)
|
|
202
|
+
})
|
|
203
|
+
boss?: User;
|
|
204
|
+
}
|
|
205
|
+
interface User extends MapInterface<User> { }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
You can define a new User **_u_**, using two employees (using the same User model here but for a different object is the same):
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
let emp1: User = new User().from({ firstName: "Summer", lastName: "Smith" });
|
|
212
|
+
let emp2: User = new User().from({ firstName: "Morty", lastName: "Smith" });
|
|
213
|
+
|
|
214
|
+
let u = new User().from({ firstName: "Rick", lastName: "Sanchez", employees: [emp1.toMap(), emp2.toMap()], rolesToMap: ["CEO", "EMPLOYEE"] });
|
|
215
|
+
```
|
package/dist/class.decorator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copy, empty, filled, from, get, objToModel, set, toMap } from "./functions";
|
|
1
|
+
import { copy, empty, filled, from, get, objToModel, set, toMap, } from "./functions";
|
|
2
2
|
export function MapClass() {
|
|
3
3
|
return function (constructor) {
|
|
4
4
|
constructor.prototype.from = from;
|
|
@@ -3,9 +3,7 @@ export function ArrayField(clsFactory, opt) {
|
|
|
3
3
|
const Ctor = clsFactory;
|
|
4
4
|
return MapField({
|
|
5
5
|
src: opt?.src,
|
|
6
|
-
transformer: (arr) => Array.isArray(arr)
|
|
7
|
-
? arr.map((item) => new Ctor().from(item))
|
|
8
|
-
: [],
|
|
6
|
+
transformer: (arr) => Array.isArray(arr) ? arr.map((item) => new Ctor().from(item)) : [],
|
|
9
7
|
reverser: (arr) => Array.isArray(arr) ? arr.map((item) => item.toMap()) : [],
|
|
10
8
|
});
|
|
11
9
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ClassType } from '../types';
|
|
1
|
+
import { ClassType } from "../types";
|
|
3
2
|
export declare const MAP_FIELD: unique symbol;
|
|
4
3
|
export interface MapperMetadata<T = any> {
|
|
5
4
|
src?: keyof T;
|
|
@@ -13,7 +12,7 @@ export interface MapperMetadata<T = any> {
|
|
|
13
12
|
}
|
|
14
13
|
export declare function isClass(func: any): func is ClassType;
|
|
15
14
|
export declare function getPrototype(target: Record<string, unknown> | ClassType): any;
|
|
16
|
-
export declare const MapField: <T = any>({ transformer, reverser, src, initialize, }?: MapperMetadata<T>) =>
|
|
15
|
+
export declare const MapField: <T = any>({ transformer, reverser, src, initialize, }?: MapperMetadata<T>) => ((target: unknown, propertyKey: string | symbol) => void);
|
|
17
16
|
export declare const getMapFieldMetadataList: (target: Record<string, unknown> | ClassType | any) => {
|
|
18
17
|
[key: string]: MapperMetadata;
|
|
19
18
|
} | undefined;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
export const MAP_FIELD = Symbol('MAP_FIELD');
|
|
1
|
+
export const MAP_FIELD = Symbol("MAP_FIELD");
|
|
3
2
|
export function isClass(func) {
|
|
4
|
-
return (typeof func ===
|
|
3
|
+
return (typeof func === "function" &&
|
|
5
4
|
/^class\s/.test(Function.prototype.toString.call(func)));
|
|
6
5
|
}
|
|
7
6
|
export function getPrototype(target) {
|
|
8
|
-
return isClass(target) || !target?.prototype
|
|
7
|
+
return isClass(target) || !target?.prototype
|
|
8
|
+
? !target?.constructor
|
|
9
|
+
? target
|
|
10
|
+
: target?.constructor
|
|
11
|
+
: target?.prototype;
|
|
9
12
|
}
|
|
10
13
|
export const MapField = ({ transformer, reverser, src, initialize = false, } = {}) => {
|
|
11
14
|
return (target, property) => {
|
|
12
15
|
const classConstructor = target.constructor;
|
|
13
16
|
const propertyName = property.toString();
|
|
14
|
-
const metadata =
|
|
17
|
+
const metadata = classConstructor[MAP_FIELD] || {};
|
|
15
18
|
// create new object reference to avoid this issue: https://github.com/rbuckton/reflect-metadata/issues/62
|
|
16
19
|
const newMetadata = { ...metadata };
|
|
17
20
|
const previousValues = metadata[propertyName];
|
|
@@ -22,14 +25,14 @@ export const MapField = ({ transformer, reverser, src, initialize = false, } = {
|
|
|
22
25
|
transformer,
|
|
23
26
|
reverser,
|
|
24
27
|
};
|
|
25
|
-
|
|
28
|
+
classConstructor[MAP_FIELD] = newMetadata;
|
|
26
29
|
};
|
|
27
30
|
};
|
|
28
31
|
export const getMapFieldMetadataList = (target) => {
|
|
29
|
-
return
|
|
32
|
+
return getPrototype(target)[MAP_FIELD];
|
|
30
33
|
};
|
|
31
34
|
export const hasMapFieldMetadataList = (target) => {
|
|
32
|
-
return
|
|
35
|
+
return !!getPrototype(target)[MAP_FIELD];
|
|
33
36
|
};
|
|
34
37
|
export const getMapFieldMetadata = (target, propertyName) => {
|
|
35
38
|
const metadata = getMapFieldMetadataList(target);
|
|
@@ -39,6 +42,6 @@ export const getMapFieldMetadata = (target, propertyName) => {
|
|
|
39
42
|
return metadata[name];
|
|
40
43
|
};
|
|
41
44
|
export const hasMapFieldMetadata = (target, propertyName) => {
|
|
42
|
-
const metadata =
|
|
45
|
+
const metadata = getPrototype(target)[MAP_FIELD];
|
|
43
46
|
return metadata && !!metadata[propertyName];
|
|
44
47
|
};
|
package/dist/functions.d.ts
CHANGED
|
@@ -1,49 +1,54 @@
|
|
|
1
|
+
export interface MapperTarget {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
from?(obj: unknown): MapperTarget;
|
|
4
|
+
toMap?(): Record<string, unknown>;
|
|
5
|
+
}
|
|
1
6
|
/**
|
|
2
7
|
* Convert the instance of this class to JSON Object.
|
|
3
8
|
*
|
|
4
9
|
* @returns JSON object mapped considering metadata "src" and "reverser"
|
|
5
10
|
*/
|
|
6
|
-
export declare function toMap():
|
|
11
|
+
export declare function toMap(this: MapperTarget): Record<string, unknown>;
|
|
7
12
|
/**
|
|
8
13
|
* Convert a JSON Object to an instance of this class.
|
|
9
14
|
*
|
|
10
15
|
* @param obj JSON Object
|
|
11
16
|
* @returns Instance of this class
|
|
12
17
|
*/
|
|
13
|
-
export declare function objToModel(obj:
|
|
18
|
+
export declare function objToModel(this: MapperTarget, obj: Record<string, any>): MapperTarget;
|
|
14
19
|
/**
|
|
15
20
|
* Check if this instance is empty.
|
|
16
21
|
*
|
|
17
22
|
* @returns true or false
|
|
18
23
|
*/
|
|
19
|
-
export declare function empty(): boolean;
|
|
24
|
+
export declare function empty(this: MapperTarget): boolean;
|
|
20
25
|
/**
|
|
21
26
|
* Check if this instance is filled.
|
|
22
27
|
*
|
|
23
28
|
* @returns true or false
|
|
24
29
|
*/
|
|
25
|
-
export declare function filled(): boolean;
|
|
30
|
+
export declare function filled(this: MapperTarget): boolean;
|
|
26
31
|
/**
|
|
27
32
|
* GET property value from a string path.
|
|
28
33
|
*
|
|
29
34
|
* @param path String path
|
|
30
35
|
* @returns Value of the property
|
|
31
36
|
*/
|
|
32
|
-
export declare function get<T>(path: string): T;
|
|
37
|
+
export declare function get<T>(this: MapperTarget, path: string): T;
|
|
33
38
|
/**
|
|
34
39
|
* SET property value from a string path.
|
|
35
40
|
*
|
|
36
41
|
* @param path String path
|
|
37
42
|
* @param value Value of the property
|
|
38
43
|
*/
|
|
39
|
-
export declare function set(path: string, value:
|
|
44
|
+
export declare function set(this: MapperTarget, path: string, value: unknown): void;
|
|
40
45
|
/**
|
|
41
46
|
* Deep copy of the object caller
|
|
42
47
|
*/
|
|
43
|
-
export declare function copy<T>(): T;
|
|
48
|
+
export declare function copy<T extends MapperTarget>(this: T): T;
|
|
44
49
|
/**
|
|
45
50
|
* Constructor of the mapper.
|
|
46
51
|
*
|
|
47
52
|
* @param object object to be mapped considering metadata "src", "transformer" and "reverser"
|
|
48
53
|
*/
|
|
49
|
-
export declare function from(object: any):
|
|
54
|
+
export declare function from(this: MapperTarget, object: Record<string, any>): MapperTarget;
|
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,43 @@ 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
|
-
if (
|
|
68
|
-
|
|
32
|
+
if (Array.isArray(value)) {
|
|
33
|
+
finalValue = value.map((item) => (item?.toMap ? item.toMap() : item));
|
|
34
|
+
}
|
|
35
|
+
else if (value?.toMap) {
|
|
36
|
+
finalValue = value.toMap();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
finalValue = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (finalValue !== undefined) {
|
|
43
|
+
setValueByPath(obj, src, finalValue);
|
|
69
44
|
}
|
|
70
45
|
});
|
|
46
|
+
}
|
|
71
47
|
return obj;
|
|
72
48
|
}
|
|
73
49
|
/**
|
|
@@ -111,8 +87,7 @@ export function filled() {
|
|
|
111
87
|
* @returns Value of the property
|
|
112
88
|
*/
|
|
113
89
|
export function get(path) {
|
|
114
|
-
|
|
115
|
-
return props.reduce((acc, prop) => acc && acc[prop], this);
|
|
90
|
+
return getValueByPath(this, path);
|
|
116
91
|
}
|
|
117
92
|
/**
|
|
118
93
|
* SET property value from a string path.
|
|
@@ -121,20 +96,16 @@ export function get(path) {
|
|
|
121
96
|
* @param value Value of the property
|
|
122
97
|
*/
|
|
123
98
|
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;
|
|
99
|
+
setValueByPath(this, path, value);
|
|
132
100
|
}
|
|
133
101
|
/**
|
|
134
102
|
* Deep copy of the object caller
|
|
135
103
|
*/
|
|
136
104
|
export function copy() {
|
|
137
|
-
|
|
105
|
+
if (this.from && this.toMap) {
|
|
106
|
+
return this.from(this.toMap());
|
|
107
|
+
}
|
|
108
|
+
return this;
|
|
138
109
|
}
|
|
139
110
|
/**
|
|
140
111
|
* Constructor of the mapper.
|
|
@@ -143,77 +114,50 @@ export function copy() {
|
|
|
143
114
|
*/
|
|
144
115
|
export function from(object) {
|
|
145
116
|
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);
|
|
117
|
+
const mappedSrcRoots = new Set();
|
|
118
|
+
// 1. Process Metadata
|
|
119
|
+
if (metadataList && object) {
|
|
120
|
+
Object.keys(metadataList).forEach((key) => {
|
|
121
|
+
const meta = metadataList[key];
|
|
122
|
+
const src = meta.src || key;
|
|
123
|
+
// Identify root property for this mapping to exclude it from direct copy later
|
|
124
|
+
const normalizedPath = src.replace(/\[(\w+)\]/g, ".$1");
|
|
125
|
+
const root = normalizedPath.split(".")[0];
|
|
126
|
+
mappedSrcRoots.add(root);
|
|
127
|
+
const value = getValueByPath(object, src);
|
|
128
|
+
// Only process if value exists in source (matches old behavior and avoids infinite recursion on undefined)
|
|
129
|
+
if (value !== undefined) {
|
|
130
|
+
if (meta.transformer) {
|
|
131
|
+
const transformed = meta.transformer(value, object);
|
|
132
|
+
if (transformed !== undefined) {
|
|
133
|
+
this[key] = transformed;
|
|
194
134
|
}
|
|
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
135
|
}
|
|
204
136
|
else {
|
|
205
|
-
|
|
137
|
+
this[key] = value;
|
|
206
138
|
}
|
|
207
139
|
}
|
|
208
140
|
});
|
|
209
|
-
|
|
210
|
-
|
|
141
|
+
}
|
|
142
|
+
// 2. Process Remaining Properties (Direct Copy)
|
|
143
|
+
// Only if they are not part of a mapped source root
|
|
144
|
+
if (object) {
|
|
145
|
+
Object.keys(object).forEach((prop) => {
|
|
146
|
+
if (!mappedSrcRoots.has(prop)) {
|
|
147
|
+
this[prop] = object[prop];
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// 3. Initialize properties with "initialize" metadata
|
|
152
|
+
if (metadataList) {
|
|
211
153
|
Object.keys(metadataList).forEach((metaName) => {
|
|
212
|
-
|
|
213
|
-
|
|
154
|
+
const meta = metadataList[metaName];
|
|
155
|
+
if (meta?.initialize &&
|
|
156
|
+
meta?.transformer &&
|
|
214
157
|
this[metaName] === undefined) {
|
|
215
|
-
this[metaName] =
|
|
158
|
+
this[metaName] = meta.transformer(null, object);
|
|
216
159
|
}
|
|
217
160
|
});
|
|
161
|
+
}
|
|
218
162
|
return this;
|
|
219
163
|
}
|
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";
|
|
@@ -16,11 +17,6 @@ import { ObjectField } from "./field-decorators/object.decorator";
|
|
|
16
17
|
console.log("\nMAPPER FACTORY - TEST");
|
|
17
18
|
console.log("\n");
|
|
18
19
|
let History = class History {
|
|
19
|
-
id;
|
|
20
|
-
name;
|
|
21
|
-
testControl;
|
|
22
|
-
daysActive;
|
|
23
|
-
testConcatenation;
|
|
24
20
|
};
|
|
25
21
|
__decorate([
|
|
26
22
|
MapField({
|
|
@@ -57,14 +53,6 @@ History = __decorate([
|
|
|
57
53
|
MapClass()
|
|
58
54
|
], History);
|
|
59
55
|
let User = class User {
|
|
60
|
-
id;
|
|
61
|
-
username;
|
|
62
|
-
name;
|
|
63
|
-
surname;
|
|
64
|
-
roles;
|
|
65
|
-
employees;
|
|
66
|
-
boss;
|
|
67
|
-
histories;
|
|
68
56
|
};
|
|
69
57
|
__decorate([
|
|
70
58
|
MapField({
|
|
@@ -111,6 +99,7 @@ User = __decorate([
|
|
|
111
99
|
const emp1 = new User().from({ firstName: "Summer", lastName: "Smith" });
|
|
112
100
|
const emp2 = new User().from({ firstName: "Morty", lastName: "Smith" });
|
|
113
101
|
const JSONObject = {
|
|
102
|
+
id: "0",
|
|
114
103
|
username: "god",
|
|
115
104
|
firstName: "Rick",
|
|
116
105
|
lastName: "Sanchez",
|
|
@@ -120,13 +109,19 @@ const JSONObject = {
|
|
|
120
109
|
};
|
|
121
110
|
//TEST constructor
|
|
122
111
|
const u = new User().from(JSONObject);
|
|
123
|
-
|
|
112
|
+
console.log("\n\n\n");
|
|
113
|
+
console.log(u);
|
|
114
|
+
console.log("\n\n\n");
|
|
115
|
+
console.log(JSONObject);
|
|
116
|
+
console.log("\n\n\n");
|
|
117
|
+
const constructorTest = !!(u.id == JSONObject.id &&
|
|
118
|
+
u.username == JSONObject.username &&
|
|
124
119
|
u.name == JSONObject.firstName &&
|
|
125
120
|
u.surname == JSONObject.lastName &&
|
|
126
121
|
u.employees?.map((emp) => emp.name == emp1.name) &&
|
|
127
122
|
u.roles?.map((role) => role == "CEO") &&
|
|
128
123
|
u.boss.name == "Nello" &&
|
|
129
|
-
u.boss.surname == "Stanco";
|
|
124
|
+
u.boss.surname == "Stanco");
|
|
130
125
|
console.log("TEST CONSTRUCTOR", constructorTest ? "✅" : "❌");
|
|
131
126
|
//TEST toModel method with JS Object
|
|
132
127
|
const u1 = new User().toModel(u);
|
|
@@ -167,7 +162,6 @@ const hTest2 = new History().from({
|
|
|
167
162
|
const toModelTest4 = hTest2.toMap().test.concatenation == "resolve ";
|
|
168
163
|
console.log("TEST CONCAT WITH POINT", toModelTest4 ? "✅" : "❌");
|
|
169
164
|
let Test = class Test {
|
|
170
|
-
a;
|
|
171
165
|
};
|
|
172
166
|
__decorate([
|
|
173
167
|
MapField({
|
|
@@ -190,10 +184,10 @@ console.log("TEST TO MODEL WITH INITIALIZE", model3.a == "test to model" ? "✅"
|
|
|
190
184
|
const model4 = model3.toMap();
|
|
191
185
|
console.log("TEST TO MAP WITH INITIALIZE", model4.b.a == "test reverser" ? "✅" : "❌");
|
|
192
186
|
const model5 = model3.copy();
|
|
193
|
-
console.log("TEST COPY WITH INITIALIZE", Object.keys(model5).every((k) => model5
|
|
187
|
+
console.log("TEST COPY WITH INITIALIZE", Object.keys(model5).every((k) => model5.get(k) == model3.get(k))
|
|
188
|
+
? "✅"
|
|
189
|
+
: "❌");
|
|
194
190
|
let TestFlag = class TestFlag {
|
|
195
|
-
flTest;
|
|
196
|
-
a;
|
|
197
191
|
};
|
|
198
192
|
__decorate([
|
|
199
193
|
MapField({
|
|
@@ -216,7 +210,9 @@ TestFlag = __decorate([
|
|
|
216
210
|
MapClass()
|
|
217
211
|
], TestFlag);
|
|
218
212
|
const testFlagInitialize = new TestFlag().from();
|
|
219
|
-
console.log("TEST INITIALIZE", testFlagInitialize && testFlagInitialize.a == "test transformer"
|
|
213
|
+
console.log("TEST INITIALIZE", testFlagInitialize && testFlagInitialize.a == "test transformer"
|
|
214
|
+
? "✅"
|
|
215
|
+
: "❌");
|
|
220
216
|
const testFlag0 = new TestFlag().from();
|
|
221
217
|
const testFlag0Map = testFlag0.toMap();
|
|
222
218
|
console.log("TEST FLAG0", testFlag0.flTest === false && testFlag0Map.flTest == "0" ? "✅" : "❌");
|
|
@@ -227,8 +223,6 @@ const testFlag2 = new TestFlag().from({ flTest: "0" });
|
|
|
227
223
|
const testFlag2Map = testFlag2.toMap();
|
|
228
224
|
console.log("TEST FLAG2", !testFlag2.flTest && testFlag2Map.flTest == "0" ? "✅" : "❌");
|
|
229
225
|
let TestWithoutMapField = class TestWithoutMapField {
|
|
230
|
-
id;
|
|
231
|
-
name;
|
|
232
226
|
};
|
|
233
227
|
TestWithoutMapField = __decorate([
|
|
234
228
|
MapClass()
|
|
@@ -240,8 +234,6 @@ const JSONObject2 = {
|
|
|
240
234
|
const testWOMF = new TestWithoutMapField().from(JSONObject2);
|
|
241
235
|
console.log("TEST WITHOUT MAP FIELD", testWOMF ? "✅" : "❌");
|
|
242
236
|
let ObjDecorator = class ObjDecorator {
|
|
243
|
-
id;
|
|
244
|
-
testObject;
|
|
245
237
|
};
|
|
246
238
|
__decorate([
|
|
247
239
|
ObjectField(ObjDecorator),
|
|
@@ -251,9 +243,6 @@ ObjDecorator = __decorate([
|
|
|
251
243
|
MapClass()
|
|
252
244
|
], ObjDecorator);
|
|
253
245
|
let TestDecorators = class TestDecorators {
|
|
254
|
-
date;
|
|
255
|
-
objList;
|
|
256
|
-
obj;
|
|
257
246
|
};
|
|
258
247
|
__decorate([
|
|
259
248
|
DateField({ src: "dateSrc" }),
|
|
@@ -279,7 +268,7 @@ const JSONTestDecorators = {
|
|
|
279
268
|
obj: { id: "5", testObject: { id: "6" } },
|
|
280
269
|
};
|
|
281
270
|
const testDecorators = new TestDecorators().from(JSONTestDecorators);
|
|
282
|
-
console.log("TEST WITH DECORATORS", testDecorators.date
|
|
271
|
+
console.log("TEST WITH DECORATORS", testDecorators.date?.toISOString() &&
|
|
283
272
|
testDecorators.objList?.length === 2 &&
|
|
284
273
|
testDecorators.obj instanceof ObjDecorator &&
|
|
285
274
|
testDecorators.objList[0] instanceof ObjDecorator &&
|
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,50 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mapper-factory",
|
|
3
|
-
"version": "4.0.
|
|
4
|
-
"description": "mapper for typescript object",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"/dist",
|
|
16
|
-
"/images"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "npx tsc",
|
|
20
|
-
"dev": "npx tsc --watch",
|
|
21
|
-
"test": "npx tsx src/test.ts",
|
|
22
|
-
"patch": "npm version patch",
|
|
23
|
-
"minor": "npm version minor",
|
|
24
|
-
"major": "npm version major",
|
|
25
|
-
"deploy": "npm publish"
|
|
26
|
-
},
|
|
27
|
-
"repository": {
|
|
28
|
-
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/lucaAngrisani/mapper-factory.git"
|
|
30
|
-
},
|
|
31
|
-
"keywords": [
|
|
32
|
-
"mapper-ts",
|
|
33
|
-
"mapper-factory",
|
|
34
|
-
"mapper",
|
|
35
|
-
"factory",
|
|
36
|
-
"mapper factory"
|
|
37
|
-
],
|
|
38
|
-
"author": "lucaAngrisani Angrigo",
|
|
39
|
-
"license": "ISC",
|
|
40
|
-
"bugs": {
|
|
41
|
-
"url": "https://github.com/lucaAngrisani/mapper-factory/issues"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://github.com/lucaAngrisani/mapper-factory#readme",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
"typescript": "^5.7.3"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mapper-factory",
|
|
3
|
+
"version": "4.0.2",
|
|
4
|
+
"description": "mapper for typescript object",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"/dist",
|
|
16
|
+
"/images"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "npx tsc",
|
|
20
|
+
"dev": "npx tsc --watch",
|
|
21
|
+
"test": "npx tsx src/test.ts",
|
|
22
|
+
"patch": "npm version patch",
|
|
23
|
+
"minor": "npm version minor",
|
|
24
|
+
"major": "npm version major",
|
|
25
|
+
"deploy": "npm publish"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/lucaAngrisani/mapper-factory.git"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mapper-ts",
|
|
33
|
+
"mapper-factory",
|
|
34
|
+
"mapper",
|
|
35
|
+
"factory",
|
|
36
|
+
"mapper factory"
|
|
37
|
+
],
|
|
38
|
+
"author": "lucaAngrisani Angrigo",
|
|
39
|
+
"license": "ISC",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/lucaAngrisani/mapper-factory/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/lucaAngrisani/mapper-factory#readme",
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.7.3"
|
|
46
|
+
}
|
|
47
|
+
}
|