@colyseus/schema 2.0.4 → 2.0.6

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.
Files changed (107) hide show
  1. package/README.md +0 -4
  2. package/build/cjs/index.js +48 -48
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +130 -104
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +50 -50
  7. package/lib/Reflection.js +87 -119
  8. package/lib/Reflection.js.map +1 -1
  9. package/lib/Schema.js +195 -257
  10. package/lib/Schema.js.map +1 -1
  11. package/lib/annotations.d.ts +6 -6
  12. package/lib/annotations.js +64 -92
  13. package/lib/annotations.js.map +1 -1
  14. package/lib/changes/ChangeTree.d.ts +1 -1
  15. package/lib/changes/ChangeTree.js +63 -70
  16. package/lib/changes/ChangeTree.js.map +1 -1
  17. package/lib/changes/ReferenceTracker.js +24 -27
  18. package/lib/changes/ReferenceTracker.js.map +1 -1
  19. package/lib/codegen/api.js +9 -9
  20. package/lib/codegen/api.js.map +1 -1
  21. package/lib/codegen/argv.d.ts +1 -1
  22. package/lib/codegen/argv.js +11 -11
  23. package/lib/codegen/argv.js.map +1 -1
  24. package/lib/codegen/cli.js +21 -10
  25. package/lib/codegen/cli.js.map +1 -1
  26. package/lib/codegen/languages/cpp.js +126 -77
  27. package/lib/codegen/languages/cpp.js.map +1 -1
  28. package/lib/codegen/languages/csharp.js +121 -62
  29. package/lib/codegen/languages/csharp.js.map +1 -1
  30. package/lib/codegen/languages/haxe.js +34 -26
  31. package/lib/codegen/languages/haxe.js.map +1 -1
  32. package/lib/codegen/languages/java.js +39 -27
  33. package/lib/codegen/languages/java.js.map +1 -1
  34. package/lib/codegen/languages/js.js +48 -32
  35. package/lib/codegen/languages/js.js.map +1 -1
  36. package/lib/codegen/languages/lua.js +35 -24
  37. package/lib/codegen/languages/lua.js.map +1 -1
  38. package/lib/codegen/languages/ts.js +63 -68
  39. package/lib/codegen/languages/ts.js.map +1 -1
  40. package/lib/codegen/parser.d.ts +9 -1
  41. package/lib/codegen/parser.js +88 -46
  42. package/lib/codegen/parser.js.map +1 -1
  43. package/lib/codegen/types.d.ts +8 -0
  44. package/lib/codegen/types.js +64 -54
  45. package/lib/codegen/types.js.map +1 -1
  46. package/lib/encoding/decode.js +15 -15
  47. package/lib/encoding/decode.js.map +1 -1
  48. package/lib/encoding/encode.js +14 -14
  49. package/lib/encoding/encode.js.map +1 -1
  50. package/lib/events/EventEmitter.d.ts +1 -1
  51. package/lib/events/EventEmitter.js +16 -47
  52. package/lib/events/EventEmitter.js.map +1 -1
  53. package/lib/filters/index.js +7 -8
  54. package/lib/filters/index.js.map +1 -1
  55. package/lib/index.js +11 -11
  56. package/lib/index.js.map +1 -1
  57. package/lib/types/ArraySchema.d.ts +1 -1
  58. package/lib/types/ArraySchema.js +161 -219
  59. package/lib/types/ArraySchema.js.map +1 -1
  60. package/lib/types/CollectionSchema.d.ts +1 -1
  61. package/lib/types/CollectionSchema.js +63 -71
  62. package/lib/types/CollectionSchema.js.map +1 -1
  63. package/lib/types/HelperTypes.d.ts +9 -9
  64. package/lib/types/MapSchema.d.ts +16 -16
  65. package/lib/types/MapSchema.js +68 -78
  66. package/lib/types/MapSchema.js.map +1 -1
  67. package/lib/types/SetSchema.js +62 -71
  68. package/lib/types/SetSchema.js.map +1 -1
  69. package/lib/types/index.js +1 -1
  70. package/lib/types/index.js.map +1 -1
  71. package/lib/types/typeRegistry.js +1 -1
  72. package/lib/types/typeRegistry.js.map +1 -1
  73. package/lib/types/utils.js +9 -10
  74. package/lib/types/utils.js.map +1 -1
  75. package/lib/utils.js +10 -13
  76. package/lib/utils.js.map +1 -1
  77. package/package.json +18 -15
  78. package/src/Reflection.ts +159 -0
  79. package/src/Schema.ts +1024 -0
  80. package/src/annotations.ts +400 -0
  81. package/src/changes/ChangeTree.ts +295 -0
  82. package/src/changes/ReferenceTracker.ts +81 -0
  83. package/src/codegen/api.ts +46 -0
  84. package/src/codegen/argv.ts +40 -0
  85. package/src/codegen/cli.ts +65 -0
  86. package/src/codegen/languages/cpp.ts +297 -0
  87. package/src/codegen/languages/csharp.ts +208 -0
  88. package/src/codegen/languages/haxe.ts +110 -0
  89. package/src/codegen/languages/java.ts +115 -0
  90. package/src/codegen/languages/js.ts +115 -0
  91. package/src/codegen/languages/lua.ts +125 -0
  92. package/src/codegen/languages/ts.ts +129 -0
  93. package/src/codegen/parser.ts +299 -0
  94. package/src/codegen/types.ts +177 -0
  95. package/src/encoding/decode.ts +278 -0
  96. package/src/encoding/encode.ts +283 -0
  97. package/src/filters/index.ts +23 -0
  98. package/src/index.ts +59 -0
  99. package/src/spec.ts +49 -0
  100. package/src/types/ArraySchema.ts +612 -0
  101. package/src/types/CollectionSchema.ts +199 -0
  102. package/src/types/HelperTypes.ts +34 -0
  103. package/src/types/MapSchema.ts +268 -0
  104. package/src/types/SetSchema.ts +208 -0
  105. package/src/types/typeRegistry.ts +19 -0
  106. package/src/types/utils.ts +62 -0
  107. package/src/utils.ts +28 -0
@@ -0,0 +1,129 @@
1
+ import { Class, Property, File, getCommentHeader, getInheritanceTree, Context, Interface } from "../types";
2
+ import { GenerateOptions } from "../api";
3
+
4
+ const typeMaps = {
5
+ "string": "string",
6
+ "number": "number",
7
+ "boolean": "boolean",
8
+ "int8": "number",
9
+ "uint8": "number",
10
+ "int16": "number",
11
+ "uint16": "number",
12
+ "int32": "number",
13
+ "uint32": "number",
14
+ "int64": "number",
15
+ "uint64": "number",
16
+ "float32": "number",
17
+ "float64": "number",
18
+ }
19
+
20
+ const distinct = (value, index, self) => self.indexOf(value) === index;
21
+
22
+ export function generate (context: Context, options: GenerateOptions): File[] {
23
+ return [
24
+ ...context.classes.map(structure => ({
25
+ name: structure.name + ".ts",
26
+ content: generateClass(structure, options.namespace, context.classes)
27
+ })),
28
+ ...context.interfaces.map(structure => ({
29
+ name: structure.name + ".ts",
30
+ content: generateInterface(structure, options.namespace, context.classes),
31
+ }))
32
+ ];
33
+ }
34
+
35
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
36
+ const allRefs: Property[] = [];
37
+ klass.properties.forEach(property => {
38
+ let type = property.type;
39
+
40
+ // keep all refs list
41
+ if ((type === "ref" || type === "array" || type === "map" || type === "set")) {
42
+ allRefs.push(property);
43
+ }
44
+ });
45
+
46
+ return `${getCommentHeader()}
47
+
48
+ import { Schema, type, ArraySchema, MapSchema, SetSchema, DataChange } from '@colyseus/schema';
49
+ ${allRefs.
50
+ filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
51
+ map(ref => ref.childType).
52
+ concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
53
+ filter(distinct).
54
+ map(childType => `import { ${childType} } from './${childType}'`).
55
+ join("\n")}
56
+
57
+ export class ${klass.name} extends ${klass.extends} {
58
+ ${klass.properties.map(prop => ` ${generateProperty(prop)}`).join("\n")}
59
+ }
60
+ `;
61
+ }
62
+
63
+ function generateProperty(prop: Property) {
64
+ let langType: string;
65
+ let initializer = "";
66
+ let typeArgs: string;
67
+
68
+ if (prop.childType) {
69
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
70
+
71
+ if (isUpcaseFirst) {
72
+ typeArgs += `, ${prop.childType}`;
73
+
74
+ } else {
75
+ typeArgs += `, "${prop.childType}"`;
76
+ }
77
+
78
+ if(prop.type === "ref") {
79
+ langType = `${prop.childType}`;
80
+ initializer = `new ${prop.childType}()`;
81
+ typeArgs = `${prop.childType}`;
82
+
83
+ } else if(prop.type === "array") {
84
+ langType = (isUpcaseFirst)
85
+ ? `ArraySchema<${prop.childType}>`
86
+ : `ArraySchema<${typeMaps[prop.childType]}>`;
87
+ initializer = `new ${langType}()`;
88
+ typeArgs = (isUpcaseFirst)
89
+ ? `[ ${prop.childType} ]`
90
+ : `[ "${prop.childType}" ]`;
91
+
92
+ } else if(prop.type === "map") {
93
+ langType = (isUpcaseFirst)
94
+ ? `MapSchema<${prop.childType}>`
95
+ : `MapSchema<${typeMaps[prop.childType]}>`;
96
+ initializer = `new ${langType}()`;
97
+ typeArgs = (isUpcaseFirst)
98
+ ? `{ map: ${prop.childType} }`
99
+ : `{ map: "${prop.childType}" }`;
100
+ } else if (prop.type === "set") {
101
+ langType = (isUpcaseFirst)
102
+ ? `SetSchema<${prop.childType}>`
103
+ : `SetSchema<${typeMaps[prop.childType]}>`;
104
+ initializer = `new ${langType}()`;
105
+ typeArgs = (isUpcaseFirst)
106
+ ? `{ set: ${prop.childType} }`
107
+ : `{ set: "${prop.childType}" }`;
108
+ }
109
+
110
+ } else {
111
+ langType = typeMaps[prop.type];
112
+ typeArgs = `"${prop.type}"`;
113
+ }
114
+
115
+ // TS1263: "Declarations with initializers cannot also have definite assignment assertions"
116
+ const definiteAssertion = initializer ? "" : "!";
117
+
118
+ return `@type(${typeArgs}) public ${prop.name}${definiteAssertion}: ${langType}${(initializer) ? ` = ${initializer}` : ""};`
119
+ }
120
+
121
+
122
+ function generateInterface(structure: Interface, namespace: string, allClasses: Class[]) {
123
+ return `${getCommentHeader()}
124
+
125
+ export interface ${structure.name} {
126
+ ${structure.properties.map(prop => ` ${prop.name}: ${prop.type};`).join("\n")}
127
+ }
128
+ `;
129
+ }
@@ -0,0 +1,299 @@
1
+ import * as ts from "typescript";
2
+ import * as path from "path";
3
+ import { readFileSync } from "fs";
4
+ import { IStructure, Class, Interface, Property, Context, Enum } from "./types";
5
+
6
+ let currentStructure: IStructure;
7
+ let currentProperty: Property;
8
+
9
+ let globalContext: Context;
10
+
11
+ function defineProperty(property: Property, initializer: any) {
12
+ if (ts.isIdentifier(initializer)) {
13
+ property.type = "ref";
14
+ property.childType = initializer.text;
15
+
16
+ } else if (initializer.kind == ts.SyntaxKind.ObjectLiteralExpression) {
17
+ property.type = initializer.properties[0].name.text;
18
+ property.childType = initializer.properties[0].initializer.text;
19
+
20
+ } else if (initializer.kind == ts.SyntaxKind.ArrayLiteralExpression) {
21
+ property.type = "array";
22
+ property.childType = initializer.elements[0].text;
23
+
24
+ } else {
25
+ property.type = initializer.text;
26
+ }
27
+ }
28
+
29
+ function inspectNode(node: ts.Node, context: Context, decoratorName: string) {
30
+ switch (node.kind) {
31
+ case ts.SyntaxKind.ImportClause:
32
+ const specifier = (node.parent as any).moduleSpecifier;
33
+ if (specifier && (specifier.text as string).startsWith('.')) {
34
+ const currentDir = path.dirname(node.getSourceFile().fileName);
35
+ const pathToImport = path.resolve(currentDir, specifier.text);
36
+ parseFiles([pathToImport], decoratorName, globalContext);
37
+ }
38
+ break;
39
+
40
+ case ts.SyntaxKind.ClassDeclaration:
41
+ currentStructure = new Class();
42
+
43
+ const heritageClauses = (node as ts.ClassLikeDeclarationBase).heritageClauses;
44
+ if (heritageClauses && heritageClauses.length > 0) {
45
+ (currentStructure as Class).extends = heritageClauses[0].types[0].expression.getText();
46
+ }
47
+
48
+ context.addStructure(currentStructure);
49
+ break;
50
+
51
+ case ts.SyntaxKind.InterfaceDeclaration:
52
+ //
53
+ // Only generate Interfaces if it has "Message" on its name.
54
+ // Example: MyMessage
55
+ //
56
+ const interfaceName = (node as ts.TypeParameterDeclaration).name.escapedText.toString();
57
+ if (interfaceName.indexOf("Message") !== -1) {
58
+ currentStructure = new Interface();
59
+ currentStructure.name = interfaceName;
60
+
61
+ context.addStructure(currentStructure);
62
+ }
63
+ break;
64
+
65
+ case ts.SyntaxKind.EnumDeclaration:
66
+ const enumName = (
67
+ node as ts.EnumDeclaration
68
+ ).name.escapedText.toString();
69
+ currentStructure = new Enum();
70
+ currentStructure.name = enumName;
71
+ context.addStructure(currentStructure);
72
+ break;
73
+
74
+ case ts.SyntaxKind.ExtendsKeyword:
75
+ // console.log(node.getText());
76
+ break;
77
+
78
+ case ts.SyntaxKind.PropertySignature:
79
+ if (currentStructure instanceof Interface) {
80
+ const interfaceDeclaration = node.parent;
81
+
82
+ if (
83
+ currentStructure.name !== (interfaceDeclaration as ts.TypeParameterDeclaration).name.escapedText.toString()
84
+ ) {
85
+ // skip if property if for a another interface than the one we're interested in.
86
+ break;
87
+ }
88
+
89
+ // define a property of an interface
90
+ const property = new Property();
91
+ property.name = (node as any).name.escapedText.toString();
92
+ property.type = (node as any).type.getText();
93
+ currentStructure.addProperty(property);
94
+ }
95
+ break;
96
+
97
+ case ts.SyntaxKind.Identifier:
98
+ if (
99
+ node.getText() === "deprecated" &&
100
+ node.parent.kind !== ts.SyntaxKind.ImportSpecifier
101
+ ) {
102
+ currentProperty = new Property();
103
+ currentProperty.deprecated = true;
104
+ break;
105
+ }
106
+
107
+ if (node.getText() === decoratorName) {
108
+ const prop: any = node.parent?.parent?.parent;
109
+ const propDecorator = getDecorators(prop);
110
+ const hasExpression = prop?.expression?.arguments;
111
+ const hasDecorator = (propDecorator?.length > 0);
112
+
113
+ /**
114
+ * neither a `@type()` decorator or `type()` call. skip.
115
+ */
116
+ if (!hasDecorator && !hasExpression) {
117
+ break;
118
+ }
119
+
120
+ // using as decorator
121
+ if (propDecorator) {
122
+ /**
123
+ * Calling `@type()` as decorator
124
+ */
125
+ const typeDecorator: any = propDecorator.find((decorator => {
126
+ return (decorator.expression as any).expression.escapedText === decoratorName;
127
+ })).expression;
128
+
129
+ const property = currentProperty || new Property();
130
+ property.name = prop.name.escapedText;
131
+ currentStructure.addProperty(property);
132
+
133
+ const typeArgument = typeDecorator.arguments[0];
134
+ defineProperty(property, typeArgument);
135
+
136
+ } else if (
137
+ prop.expression.arguments?.[1] &&
138
+ prop.expression.expression.arguments?.[0]
139
+ ) {
140
+ /**
141
+ * Calling `type()` as a regular method
142
+ */
143
+ const property = currentProperty || new Property();
144
+ property.name = prop.expression.arguments[1].text;
145
+ currentStructure.addProperty(property);
146
+
147
+ const typeArgument = prop.expression.expression.arguments[0];
148
+ defineProperty(property, typeArgument);
149
+ }
150
+
151
+
152
+ } else if (
153
+ node.getText() === "defineTypes" &&
154
+ (
155
+ node.parent.kind === ts.SyntaxKind.CallExpression ||
156
+ node.parent.kind === ts.SyntaxKind.PropertyAccessExpression
157
+ )
158
+ ) {
159
+ /**
160
+ * JavaScript source file (`.js`)
161
+ * Using `defineTypes()`
162
+ */
163
+ const callExpression = (node.parent.kind === ts.SyntaxKind.PropertyAccessExpression)
164
+ ? node.parent.parent as ts.CallExpression
165
+ : node.parent as ts.CallExpression;
166
+
167
+ if (callExpression.kind !== ts.SyntaxKind.CallExpression) {
168
+ break;
169
+ }
170
+
171
+ const className = callExpression.arguments[0].getText()
172
+ currentStructure.name = className;
173
+
174
+ const types = callExpression.arguments[1] as any;
175
+ for (let i=0; i<types.properties.length; i++) {
176
+ const prop = types.properties[i];
177
+
178
+ const property = currentProperty || new Property();
179
+ property.name = prop.name.escapedText;
180
+ currentStructure.addProperty(property);
181
+
182
+ defineProperty(property, prop.initializer);
183
+ }
184
+
185
+ }
186
+
187
+ if (node.parent.kind === ts.SyntaxKind.ClassDeclaration) {
188
+ currentStructure.name = node.getText();
189
+ }
190
+
191
+ currentProperty = undefined;
192
+
193
+ break;
194
+
195
+ case ts.SyntaxKind.EnumMember:
196
+ if (currentStructure instanceof Enum) {
197
+ const initializer = (node as any).initializer?.getText();
198
+ const name = node.getFirstToken().getText();
199
+ const property = currentProperty || new Property();
200
+ property.name = name;
201
+ if (initializer !== undefined) {
202
+ property.type = initializer;
203
+ }
204
+ currentStructure.addProperty(property);
205
+ currentProperty = undefined;
206
+ }
207
+ break;
208
+ }
209
+
210
+ ts.forEachChild(node, (n) => inspectNode(n, context, decoratorName));
211
+ }
212
+
213
+ let parsedFiles: { [filename: string]: boolean };
214
+
215
+ export function parseFiles(
216
+ fileNames: string[],
217
+ decoratorName: string = "type",
218
+ context: Context = new Context()
219
+ ) {
220
+ /**
221
+ * Re-set globalContext for each test case
222
+ */
223
+ if (globalContext !== context) {
224
+ parsedFiles = {};
225
+ globalContext = context;
226
+ }
227
+
228
+ fileNames.forEach((fileName) => {
229
+ let sourceFile: ts.Node;
230
+ let sourceFileName: string;
231
+
232
+ const fileNameAlternatives = [];
233
+
234
+ if (
235
+ !fileName.endsWith(".ts") &&
236
+ !fileName.endsWith(".js") &&
237
+ !fileName.endsWith(".mjs")
238
+ ) {
239
+ fileNameAlternatives.push(`${fileName}.ts`);
240
+ fileNameAlternatives.push(`${fileName}/index.ts`);
241
+
242
+ } else {
243
+ fileNameAlternatives.push(fileName);
244
+ }
245
+
246
+ for (let i = 0; i < fileNameAlternatives.length; i++) {
247
+ try {
248
+ sourceFileName = path.resolve(fileNameAlternatives[i]);
249
+
250
+ if (parsedFiles[sourceFileName]) {
251
+ break;
252
+ }
253
+
254
+ sourceFile = ts.createSourceFile(
255
+ sourceFileName,
256
+ readFileSync(sourceFileName).toString(),
257
+ ts.ScriptTarget.Latest,
258
+ true
259
+ );
260
+
261
+ parsedFiles[sourceFileName] = true;
262
+
263
+ break;
264
+ } catch (e) {
265
+ // console.log(`${fileNameAlternatives[i]} => ${e.message}`);
266
+ }
267
+ }
268
+
269
+ if (sourceFile) {
270
+ inspectNode(sourceFile, context, decoratorName);
271
+ }
272
+ });
273
+
274
+ return context.getStructures();
275
+ }
276
+
277
+ /**
278
+ * TypeScript 4.8+ has introduced a change on how to access decorators.
279
+ * - https://github.com/microsoft/TypeScript/pull/49089
280
+ * - https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/#decorators-are-placed-on-modifiers-on-typescripts-syntax-trees
281
+ */
282
+ export function getDecorators(node: ts.Node | null | undefined,): undefined | ts.Decorator[] {
283
+ if (node == undefined) { return undefined; }
284
+
285
+ // TypeScript 4.7 and below
286
+ // @ts-ignore
287
+ if (node.decorators) { return node.decorators; }
288
+
289
+ // TypeScript 4.8 and above
290
+ // @ts-ignore
291
+ if (ts.canHaveDecorators && ts.canHaveDecorators(node)) {
292
+ // @ts-ignore
293
+ const decorators = ts.getDecorators(node);
294
+ return decorators ? Array.from(decorators) : undefined;
295
+ }
296
+
297
+ // @ts-ignore
298
+ return node.modifiers?.filter(ts.isDecorator);
299
+ }
@@ -0,0 +1,177 @@
1
+ import * as fs from "fs";
2
+
3
+ const VERSION = JSON.parse(fs.readFileSync(__dirname + "/../../package.json").toString()).version;
4
+ const COMMENT_HEADER = `
5
+ THIS FILE HAS BEEN GENERATED AUTOMATICALLY
6
+ DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING
7
+
8
+ GENERATED USING @colyseus/schema ${VERSION}
9
+ `;
10
+
11
+ export function getCommentHeader(singleLineComment: string = "//") {
12
+ return `${COMMENT_HEADER.split("\n").map(line => `${singleLineComment} ${line}`).join("\n")}`;
13
+ }
14
+
15
+ export class Context {
16
+ classes: Class[] = [];
17
+ interfaces: Interface[] = [];
18
+ enums: Enum[] = [];
19
+
20
+ getStructures() {
21
+ return {
22
+ classes: this.classes.filter(klass => {
23
+ if (this.isSchemaClass(klass)) {
24
+ return true;
25
+
26
+ } else {
27
+ let parentClass = klass;
28
+ while (parentClass = this.getParentClass(parentClass)) {
29
+ if (this.isSchemaClass(parentClass)) {
30
+ return true;
31
+ }
32
+ }
33
+ }
34
+ return false;
35
+ }),
36
+ interfaces: this.interfaces,
37
+ enums: this.enums,
38
+ };
39
+ }
40
+
41
+ addStructure(structure: IStructure) {
42
+ structure.context = this;
43
+
44
+ if (structure instanceof Class) {
45
+ this.classes.push(structure);
46
+ } else if (structure instanceof Interface) {
47
+ this.interfaces.push(structure);
48
+ } else if (structure instanceof Enum) {
49
+ this.enums.push(structure);
50
+ }
51
+ }
52
+
53
+ private getParentClass(klass: Class) {
54
+ return this.classes.find(c => c.name === klass.extends);
55
+ }
56
+
57
+ private isSchemaClass(klass: Class) {
58
+ let isSchema: boolean = false;
59
+
60
+ let currentClass = klass;
61
+ while (!isSchema && currentClass) {
62
+ //
63
+ // TODO: ideally we should check for actual @colyseus/schema module
64
+ // reference rather than arbitrary strings.
65
+ //
66
+ isSchema = (
67
+ currentClass.extends === "Schema" ||
68
+ currentClass.extends === "schema.Schema" ||
69
+ currentClass.extends === "Schema.Schema"
70
+ );
71
+
72
+ //
73
+ // When extending from `schema.Schema`, it is required to
74
+ // normalize as "Schema" for code generation.
75
+ //
76
+ if (currentClass === klass && isSchema) {
77
+ klass.extends = "Schema";
78
+ }
79
+
80
+ currentClass = this.getParentClass(currentClass);
81
+ }
82
+
83
+ return isSchema;
84
+ }
85
+ }
86
+
87
+ export interface IStructure {
88
+ context: Context;
89
+ name: string;
90
+ properties: Property[];
91
+ addProperty(property: Property);
92
+ }
93
+
94
+ export class Interface implements IStructure {
95
+ context: Context;
96
+ name: string;
97
+ properties: Property[] = [];
98
+
99
+ addProperty(property: Property) {
100
+ if (property.type.indexOf("[]") >= 0) {
101
+ // is array!
102
+ property.childType = property.type.match(/([^\[]+)/i)[1];
103
+ property.type = "array";
104
+ this.properties.push(property);
105
+
106
+ } else {
107
+ this.properties.push(property);
108
+ }
109
+ }
110
+ }
111
+
112
+ export class Class implements IStructure {
113
+ context: Context;
114
+ name: string;
115
+ properties: Property[] = [];
116
+ extends: string;
117
+
118
+ addProperty(property: Property) {
119
+ property.index = this.properties.length;
120
+ this.properties.push(property);
121
+ }
122
+
123
+ postProcessing() {
124
+ /**
125
+ * Ensure the proprierties `index` are correct using inheritance
126
+ */
127
+ let parentKlass: Class = this;
128
+
129
+ while (
130
+ parentKlass &&
131
+ (parentKlass = this.context.classes.find(k => k.name === parentKlass.extends))
132
+ ) {
133
+ this.properties.forEach(prop => {
134
+ prop.index += parentKlass.properties.length;
135
+ });
136
+ }
137
+ }
138
+ }
139
+
140
+ export class Enum implements IStructure {
141
+ context: Context;
142
+ name: string;
143
+ properties: Property[] = [];
144
+
145
+ addProperty(property: Property) {
146
+ this.properties.push(property);
147
+ }
148
+ }
149
+
150
+ export class Property {
151
+ index: number;
152
+ name: string;
153
+ type: string;
154
+ childType: string;
155
+ deprecated?: boolean;
156
+ }
157
+
158
+ export interface File {
159
+ name: string
160
+ content: string;
161
+ }
162
+
163
+ export function getInheritanceTree(klass: Class, allClasses: Class[], includeSelf: boolean = true) {
164
+ let currentClass = klass;
165
+ let inheritanceTree: Class[] = [];
166
+
167
+ if (includeSelf) {
168
+ inheritanceTree.push(currentClass);
169
+ }
170
+
171
+ while (currentClass.extends !== "Schema") {
172
+ currentClass = allClasses.find(klass => klass.name == currentClass.extends);
173
+ inheritanceTree.push(currentClass);
174
+ }
175
+
176
+ return inheritanceTree;
177
+ }