@colyseus/schema 1.0.34 → 1.0.38

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 (76) hide show
  1. package/README.md +0 -4
  2. package/build/cjs/index.js +35 -33
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +113 -83
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +37 -35
  7. package/lib/Reflection.js +11 -11
  8. package/lib/Reflection.js.map +1 -1
  9. package/lib/Schema.js +15 -13
  10. package/lib/Schema.js.map +1 -1
  11. package/lib/annotations.js +17 -13
  12. package/lib/annotations.js.map +1 -1
  13. package/lib/changes/ChangeTree.js +2 -2
  14. package/lib/changes/ChangeTree.js.map +1 -1
  15. package/lib/codegen/api.js +1 -1
  16. package/lib/codegen/api.js.map +1 -1
  17. package/lib/codegen/argv.d.ts +1 -1
  18. package/lib/codegen/cli.js +5 -5
  19. package/lib/codegen/cli.js.map +1 -1
  20. package/lib/codegen/languages/cpp.js +38 -38
  21. package/lib/codegen/languages/cpp.js.map +1 -1
  22. package/lib/codegen/languages/csharp.js +25 -21
  23. package/lib/codegen/languages/csharp.js.map +1 -1
  24. package/lib/codegen/languages/haxe.js +13 -13
  25. package/lib/codegen/languages/haxe.js.map +1 -1
  26. package/lib/codegen/languages/java.js +11 -11
  27. package/lib/codegen/languages/java.js.map +1 -1
  28. package/lib/codegen/languages/js.js +16 -16
  29. package/lib/codegen/languages/js.js.map +1 -1
  30. package/lib/codegen/languages/lua.js +12 -12
  31. package/lib/codegen/languages/lua.js.map +1 -1
  32. package/lib/codegen/languages/ts.js +37 -33
  33. package/lib/codegen/languages/ts.js.map +1 -1
  34. package/lib/codegen/parser.js +3 -3
  35. package/lib/codegen/parser.js.map +1 -1
  36. package/lib/codegen/types.js +2 -2
  37. package/lib/codegen/types.js.map +1 -1
  38. package/lib/events/EventEmitter.js +10 -6
  39. package/lib/events/EventEmitter.js.map +1 -1
  40. package/lib/index.js +4 -4
  41. package/lib/index.js.map +1 -1
  42. package/lib/types/ArraySchema.js +12 -8
  43. package/lib/types/ArraySchema.js.map +1 -1
  44. package/lib/types/MapSchema.d.ts +19 -19
  45. package/lib/types/MapSchema.js +2 -2
  46. package/lib/types/MapSchema.js.map +1 -1
  47. package/package.json +2 -1
  48. package/src/Reflection.ts +159 -0
  49. package/src/Schema.ts +1085 -0
  50. package/src/annotations.ts +357 -0
  51. package/src/changes/ChangeTree.ts +373 -0
  52. package/src/codegen/api.ts +46 -0
  53. package/src/codegen/argv.ts +40 -0
  54. package/src/codegen/cli.ts +65 -0
  55. package/src/codegen/languages/cpp.ts +297 -0
  56. package/src/codegen/languages/csharp.ts +119 -0
  57. package/src/codegen/languages/haxe.ts +110 -0
  58. package/src/codegen/languages/java.ts +115 -0
  59. package/src/codegen/languages/js.ts +115 -0
  60. package/src/codegen/languages/lua.ts +125 -0
  61. package/src/codegen/languages/ts.ts +129 -0
  62. package/src/codegen/parser.ts +251 -0
  63. package/src/codegen/types.ts +164 -0
  64. package/src/encoding/decode.ts +278 -0
  65. package/src/encoding/encode.ts +283 -0
  66. package/src/events/EventEmitter.ts +32 -0
  67. package/src/filters/index.ts +23 -0
  68. package/src/index.ts +59 -0
  69. package/src/spec.ts +49 -0
  70. package/src/types/ArraySchema.ts +608 -0
  71. package/src/types/CollectionSchema.ts +188 -0
  72. package/src/types/HelperTypes.ts +34 -0
  73. package/src/types/MapSchema.ts +255 -0
  74. package/src/types/SetSchema.ts +197 -0
  75. package/src/types/index.ts +19 -0
  76. package/src/utils.ts +28 -0
@@ -0,0 +1,115 @@
1
+ import { Class, Property, File, getCommentHeader, getInheritanceTree, Context } 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 context.classes.map(klass => ({
24
+ name: klass.name + ".js",
25
+ content: generateClass(klass, options.namespace, context.classes)
26
+ }));
27
+ }
28
+
29
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
30
+ const allRefs: Property[] = [];
31
+ klass.properties.forEach(property => {
32
+ let type = property.type;
33
+
34
+ // keep all refs list
35
+ if ((type === "ref" || type === "array" || type === "map")) {
36
+ allRefs.push(property);
37
+ }
38
+ });
39
+
40
+ return `${getCommentHeader()}
41
+
42
+ const schema = require("@colyseus/schema");
43
+ const Schema = schema.Schema;
44
+ const type = schema.type;
45
+ ${allRefs.
46
+ filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
47
+ map(ref => ref.childType).
48
+ concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
49
+ filter(distinct).
50
+ map(childType => `const ${childType} = require("./${childType}");`).
51
+ join("\n")}
52
+
53
+ class ${klass.name} extends ${klass.extends} {
54
+ constructor () {
55
+ super();
56
+ ${klass.properties.
57
+ filter(prop => prop.childType !== undefined).
58
+ map(prop => " " + generatePropertyInitializer(prop)).join("\n")}
59
+ }
60
+ }
61
+ ${klass.properties.map(prop => generatePropertyDeclaration(klass.name, prop)).join("\n")}
62
+
63
+ export default ${klass.name};
64
+ `;
65
+ }
66
+
67
+ function generatePropertyDeclaration(className: string, prop: Property) {
68
+ let typeArgs: string;
69
+
70
+ if (prop.childType) {
71
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
72
+
73
+ if (isUpcaseFirst) {
74
+ typeArgs += `, ${prop.childType}`;
75
+
76
+ } else {
77
+ typeArgs += `, "${prop.childType}"`;
78
+ }
79
+
80
+ if(prop.type === "ref") {
81
+ typeArgs = `${prop.childType}`;
82
+
83
+ } else if(prop.type === "array") {
84
+ typeArgs = (isUpcaseFirst)
85
+ ? `[ ${prop.childType} ]`
86
+ : `[ "${prop.childType}" ]`;
87
+
88
+ } else if(prop.type === "map") {
89
+ typeArgs = (isUpcaseFirst)
90
+ ? `{ map: ${prop.childType} }`
91
+ : `{ map: "${prop.childType}" }`;
92
+ }
93
+
94
+ } else {
95
+ typeArgs = `"${prop.type}"`;
96
+ }
97
+
98
+ return `type(${typeArgs})(${className}.prototype, "${prop.name}");`;
99
+ }
100
+
101
+ function generatePropertyInitializer(prop: Property) {
102
+ let initializer = "";
103
+
104
+ if(prop.type === "ref") {
105
+ initializer = `new ${prop.childType}()`;
106
+
107
+ } else if(prop.type === "array") {
108
+ initializer = `new schema.ArraySchema()`;
109
+
110
+ } else if(prop.type === "map") {
111
+ initializer = `new schema.MapSchema()`;
112
+ }
113
+
114
+ return `this.${prop.name} = ${initializer}`;
115
+ }
@@ -0,0 +1,125 @@
1
+ import { Class, Property, File, getCommentHeader, getInheritanceTree, Context } from "../types";
2
+ import { GenerateOptions } from "../api";
3
+
4
+ /**
5
+ TODO:
6
+ - Support inheritance
7
+ - Support importing Schema dependencies
8
+ */
9
+
10
+ const typeMaps = {
11
+ "string": "string",
12
+ "number": "number",
13
+ "boolean": "boolean",
14
+ "int8": "number",
15
+ "uint8": "number",
16
+ "int16": "number",
17
+ "uint16": "number",
18
+ "int32": "number",
19
+ "uint32": "number",
20
+ "int64": "number",
21
+ "uint64": "number",
22
+ "float32": "number",
23
+ "float64": "number",
24
+ }
25
+
26
+ const distinct = (value, index, self) => self.indexOf(value) === index;
27
+
28
+ export function generate (context: Context, options: GenerateOptions): File[] {
29
+ return context.classes.map(klass => ({
30
+ name: klass.name + ".lua",
31
+ content: generateClass(klass, options.namespace, context.classes)
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")) {
42
+ allRefs.push(property);
43
+ }
44
+ });
45
+
46
+ // TOOD: inheritance
47
+
48
+ return `${getCommentHeader().replace(/\/\//mg, "--")}
49
+
50
+ local schema = require 'colyseus.serialization.schema.schema'
51
+ ${allRefs.
52
+ filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
53
+ map(ref => ref.childType).
54
+ concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
55
+ filter(distinct).
56
+ map(childType => `local ${childType} = require '${(namespace ? `${namespace}.` : '')}${childType}'`).
57
+ join("\n")}
58
+
59
+ local ${klass.name} = schema.define({
60
+ ${klass.properties.map(prop => generatePropertyDeclaration(prop)).join(",\n")},
61
+ ["_fields_by_index"] = { ${klass.properties.map(prop => `"${prop.name}"`).join(", ")} },
62
+ })
63
+
64
+ return ${klass.name}
65
+ `;
66
+
67
+ // ["on_change"] = function(changes)
68
+ // -- on change logic here
69
+ // end,
70
+
71
+ // ["on_add"] = function()
72
+ // -- on add logic here
73
+ // end,
74
+
75
+ // ["on_remove"] = function()
76
+ // -- on remove logic here
77
+ // end,
78
+ }
79
+
80
+ function generatePropertyDeclaration(prop: Property) {
81
+ let typeArgs: string;
82
+
83
+ if (prop.childType) {
84
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
85
+
86
+ if (isUpcaseFirst) {
87
+ typeArgs += `${prop.childType}`;
88
+
89
+ } else {
90
+ typeArgs += `"${prop.childType}"`;
91
+ }
92
+
93
+ if(prop.type === "ref") {
94
+ typeArgs = (isUpcaseFirst)
95
+ ? `${prop.childType}`
96
+ : `"${prop.childType}"`;
97
+
98
+ } else {
99
+ typeArgs = (isUpcaseFirst)
100
+ ? `{ ${prop.type} = ${prop.childType} }`
101
+ : `{ ${prop.type} = "${prop.childType}" }`;
102
+ }
103
+
104
+ } else {
105
+ typeArgs = `"${prop.type}"`;
106
+ }
107
+
108
+ return ` ["${prop.name}"] = ${typeArgs}`;
109
+ }
110
+
111
+ // function generatePropertyInitializer(prop: Property) {
112
+ // let initializer = "";
113
+
114
+ // if(prop.type === "ref") {
115
+ // initializer = `new ${prop.childType}()`;
116
+
117
+ // } else if(prop.type === "array") {
118
+ // initializer = `new schema.ArraySchema()`;
119
+
120
+ // } else if(prop.type === "map") {
121
+ // initializer = `new schema.MapSchema()`;
122
+ // }
123
+
124
+ // return `this.${prop.name} = ${initializer}`;
125
+ // }
@@ -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,251 @@
1
+ import * as ts from "typescript";
2
+ import * as path from "path";
3
+ import { readFileSync } from "fs";
4
+ import { IStructure, Class, Interface, Property, Context } 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.ExtendsKeyword:
66
+ // console.log(node.getText());
67
+ break;
68
+
69
+ case ts.SyntaxKind.PropertySignature:
70
+ if (currentStructure instanceof Interface) {
71
+ const interfaceDeclaration = node.parent;
72
+
73
+ if (
74
+ currentStructure.name !== (interfaceDeclaration as ts.TypeParameterDeclaration).name.escapedText.toString()
75
+ ) {
76
+ // skip if property if for a another interface than the one we're interested in.
77
+ break;
78
+ }
79
+
80
+ // define a property of an interface
81
+ const property = new Property();
82
+ property.name = (node as any).name.escapedText.toString();
83
+ property.type = (node as any).type.getText();
84
+ currentStructure.addProperty(property);
85
+ }
86
+ break;
87
+
88
+ case ts.SyntaxKind.Identifier:
89
+ if (
90
+ node.getText() === "deprecated" &&
91
+ node.parent.kind !== ts.SyntaxKind.ImportSpecifier
92
+ ) {
93
+ currentProperty = new Property();
94
+ currentProperty.deprecated = true;
95
+ break;
96
+ }
97
+
98
+ if (node.getText() === decoratorName) {
99
+ const prop: any = node.parent?.parent?.parent;
100
+ const propDecorator = prop?.decorators;
101
+ const hasExpression = prop?.expression?.arguments;
102
+
103
+ /**
104
+ * neither a `@type()` decorator or `type()` call. skip.
105
+ */
106
+ if (!propDecorator && !hasExpression) {
107
+ break;
108
+ }
109
+
110
+ // using as decorator
111
+ if (propDecorator) {
112
+ /**
113
+ * Calling `@type()` as decorator
114
+ */
115
+ const typeDecorator: any = propDecorator.find((decorator => {
116
+ return (decorator.expression as any).expression.escapedText === decoratorName;
117
+ })).expression;
118
+
119
+ const property = currentProperty || new Property();
120
+ property.name = prop.name.escapedText;
121
+ currentStructure.addProperty(property);
122
+
123
+ const typeArgument = typeDecorator.arguments[0];
124
+ defineProperty(property, typeArgument);
125
+
126
+ } else if (
127
+ prop.expression.arguments?.[1] &&
128
+ prop.expression.expression.arguments?.[0]
129
+ ) {
130
+ /**
131
+ * Calling `type()` as a regular method
132
+ */
133
+ const property = currentProperty || new Property();
134
+ property.name = prop.expression.arguments[1].text;
135
+ currentStructure.addProperty(property);
136
+
137
+ const typeArgument = prop.expression.expression.arguments[0];
138
+ defineProperty(property, typeArgument);
139
+ }
140
+
141
+
142
+ } else if (
143
+ node.getText() === "defineTypes" &&
144
+ (
145
+ node.parent.kind === ts.SyntaxKind.CallExpression ||
146
+ node.parent.kind === ts.SyntaxKind.PropertyAccessExpression
147
+ )
148
+ ) {
149
+ /**
150
+ * JavaScript source file (`.js`)
151
+ * Using `defineTypes()`
152
+ */
153
+ const callExpression = (node.parent.kind === ts.SyntaxKind.PropertyAccessExpression)
154
+ ? node.parent.parent as ts.CallExpression
155
+ : node.parent as ts.CallExpression;
156
+
157
+ if (callExpression.kind !== ts.SyntaxKind.CallExpression) {
158
+ break;
159
+ }
160
+
161
+ const className = callExpression.arguments[0].getText()
162
+ currentStructure.name = className;
163
+
164
+ const types = callExpression.arguments[1] as any;
165
+ for (let i=0; i<types.properties.length; i++) {
166
+ const prop = types.properties[i];
167
+
168
+ const property = currentProperty || new Property();
169
+ property.name = prop.name.escapedText;
170
+ currentStructure.addProperty(property);
171
+
172
+ defineProperty(property, prop.initializer);
173
+ }
174
+
175
+ }
176
+
177
+ if (node.parent.kind === ts.SyntaxKind.ClassDeclaration) {
178
+ currentStructure.name = node.getText();
179
+ }
180
+
181
+ currentProperty = undefined;
182
+
183
+ break;
184
+ }
185
+
186
+ ts.forEachChild(node, (n) => inspectNode(n, context, decoratorName));
187
+ }
188
+
189
+ let parsedFiles: { [filename: string]: boolean };
190
+
191
+ export function parseFiles(
192
+ fileNames: string[],
193
+ decoratorName: string = "type",
194
+ context: Context = new Context()
195
+ ) {
196
+ /**
197
+ * Re-set globalContext for each test case
198
+ */
199
+ if (globalContext !== context) {
200
+ parsedFiles = {};
201
+ globalContext = context;
202
+ }
203
+
204
+ fileNames.forEach((fileName) => {
205
+ let sourceFile: ts.Node;
206
+ let sourceFileName: string;
207
+
208
+ const fileNameAlternatives = [];
209
+
210
+ if (
211
+ !fileName.endsWith(".ts") &&
212
+ !fileName.endsWith(".js") &&
213
+ !fileName.endsWith(".mjs")
214
+ ) {
215
+ fileNameAlternatives.push(`${fileName}.ts`);
216
+ fileNameAlternatives.push(`${fileName}/index.ts`);
217
+
218
+ } else {
219
+ fileNameAlternatives.push(fileName);
220
+ }
221
+
222
+ for (let i = 0; i < fileNameAlternatives.length; i++) {
223
+ try {
224
+ sourceFileName = path.resolve(fileNameAlternatives[i]);
225
+
226
+ if (parsedFiles[sourceFileName]) {
227
+ break;
228
+ }
229
+
230
+ sourceFile = ts.createSourceFile(
231
+ sourceFileName,
232
+ readFileSync(sourceFileName).toString(),
233
+ ts.ScriptTarget.Latest,
234
+ true
235
+ );
236
+
237
+ parsedFiles[sourceFileName] = true;
238
+
239
+ break;
240
+ } catch (e) {
241
+ // console.log(`${fileNameAlternatives[i]} => ${e.message}`);
242
+ }
243
+ }
244
+
245
+ if (sourceFile) {
246
+ inspectNode(sourceFile, context, decoratorName);
247
+ }
248
+ });
249
+
250
+ return context.getStructures();
251
+ }