/
serializer.ts
166 lines (142 loc) · 4.99 KB
/
serializer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import { EventDispatcher } from "../utils";
import { ProjectReflection } from "../models";
import { SerializerComponent } from "./components";
import { SerializeEvent, SerializeEventData } from "./events";
import { ModelToObject } from "./schema";
import * as S from "./serializers";
export class Serializer extends EventDispatcher {
/**
* Triggered when the [[Serializer]] begins transforming a project.
* @event EVENT_BEGIN
*/
static EVENT_BEGIN = "begin";
/**
* Triggered when the [[Serializer]] has finished transforming a project.
* @event EVENT_END
*/
static EVENT_END = "end";
/**
* Serializers, sorted by their `serializeGroup` function to enable higher performance.
*/
private serializers = new Map<
(instance: unknown) => boolean,
SerializerComponent<any>[]
>();
constructor() {
super();
addSerializers(this);
}
addSerializer(serializer: SerializerComponent<any>): void {
let group = this.serializers.get(serializer.serializeGroup);
if (!group) {
this.serializers.set(serializer.serializeGroup, (group = []));
}
group.push(serializer);
group.sort((a, b) => b.priority - a.priority);
}
toObject<T>(value: T, init: object = {}): ModelToObject<T> {
if (value == null || typeof value !== "object") {
return value as any; // Serializing some primitive
}
if (Array.isArray(value)) {
if (value.length === 0) {
return undefined as any;
}
return value.map((val) => this.toObject(val)) as any;
}
// Note: This type *could* potentially lie, if a serializer declares a partial type but fails to provide
// the defined property, but the benefit of being mostly typed is probably worth it.
// TypeScript errors out if init is correctly typed as `Partial<ModelToObject<T>>`
return this.findSerializers(value).reduce<any>(
(result, curr) => curr.toObject(value, result),
init
);
}
/**
* Same as toObject but emits [[ Serializer#EVENT_BEGIN ]] and [[ Serializer#EVENT_END ]] events.
* @param value
* @param eventData Partial information to set in the event
*/
projectToObject(
value: ProjectReflection,
eventData: { begin?: SerializeEventData; end?: SerializeEventData } = {}
): ModelToObject<ProjectReflection> {
const eventBegin = new SerializeEvent(
Serializer.EVENT_BEGIN,
value,
{}
);
if (eventData.begin) {
eventBegin.outputDirectory = eventData.begin.outputDirectory;
eventBegin.outputFile = eventData.begin.outputFile;
}
this.trigger(eventBegin);
const project = this.toObject(value, eventBegin.output);
const eventEnd = new SerializeEvent(
Serializer.EVENT_END,
value,
project
);
if (eventData.end) {
eventBegin.outputDirectory = eventData.end.outputDirectory;
eventBegin.outputFile = eventData.end.outputFile;
}
this.trigger(eventEnd);
return project;
}
private findSerializers<T>(value: T): SerializerComponent<T>[] {
const routes: SerializerComponent<any>[] = [];
for (const [groupSupports, components] of this.serializers.entries()) {
if (groupSupports(value)) {
for (const component of components) {
if (component.supports(value)) {
routes.push(component);
}
}
}
}
return routes as any;
}
}
const serializerComponents: (new (owner: Serializer) => SerializerComponent<
any
>)[] = [
S.CommentTagSerializer,
S.CommentSerializer,
S.ReflectionSerializer,
S.ReferenceReflectionSerializer,
S.ContainerReflectionSerializer,
S.DeclarationReflectionSerializer,
S.ParameterReflectionSerializer,
S.SignatureReflectionSerializer,
S.TypeParameterReflectionSerializer,
S.SourceReferenceContainerSerializer,
S.TypeSerializer,
S.ArrayTypeSerializer,
S.ConditionalTypeSerializer,
S.IndexedAccessTypeSerializer,
S.InferredTypeSerializer,
S.IntersectionTypeSerializer,
S.IntrinsicTypeSerializer,
S.QueryTypeSerializer,
S.PredicateTypeSerializer,
S.ReferenceTypeSerializer,
S.ReferenceTypeSerializer,
S.ReflectionTypeSerializer,
S.LiteralTypeSerializer,
S.TupleTypeSerializer,
S.NamedTupleMemberTypeSerializer,
S.MappedTypeSerializer,
S.TypeOperatorTypeSerializer,
S.TypeParameterTypeSerializer,
S.UnionTypeSerializer,
S.UnknownTypeSerializer,
S.DecoratorContainerSerializer,
S.ReflectionCategorySerializer,
S.ReflectionGroupSerializer,
];
function addSerializers(owner: Serializer) {
for (const component of serializerComponents) {
owner.addSerializer(new component(owner));
}
}