@colyseus/schema 4.0.6 → 4.0.8
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/bin/schema-codegen +1 -1
- package/build/codegen/cli.cjs +1555 -0
- package/build/codegen/cli.cjs.map +1 -0
- package/build/encoder/ChangeTree.d.ts +0 -1
- package/build/index.cjs.map +1 -1
- package/build/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/codegen/api.ts +12 -11
- package/src/encoder/ChangeTree.ts +2 -1
|
@@ -0,0 +1,1555 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var ts = require('typescript');
|
|
7
|
+
|
|
8
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
9
|
+
function _interopNamespaceDefault(e) {
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
|
|
27
|
+
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
28
|
+
var ts__namespace = /*#__PURE__*/_interopNamespaceDefault(ts);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @author Ethan Davis
|
|
32
|
+
* https://github.com/ethanent/gar
|
|
33
|
+
*/
|
|
34
|
+
var argv = (sargs) => {
|
|
35
|
+
let props = {};
|
|
36
|
+
let lones = [];
|
|
37
|
+
const convertIfApplicable = (value) => (isNaN(value) ? (value.toString().toLowerCase() === 'true' ? true : (value.toString().toLowerCase() === 'false' ? false : value)) : Number(value));
|
|
38
|
+
const removeStartHyphens = (value) => value.replace(/^\-+/g, '');
|
|
39
|
+
for (let i = 0; i < sargs.length; i++) {
|
|
40
|
+
const equalsIndex = sargs[i].indexOf('=');
|
|
41
|
+
const isNextRefProp = sargs[i].charAt(0) === '-' && sargs.length - 1 >= i + 1 && sargs[i + 1].indexOf('=') === -1 && sargs[i + 1].charAt(0) !== '-';
|
|
42
|
+
const argName = equalsIndex === -1 ? removeStartHyphens(sargs[i]) : removeStartHyphens(sargs[i].slice(0, equalsIndex));
|
|
43
|
+
if (equalsIndex !== -1) {
|
|
44
|
+
props[argName] = convertIfApplicable(sargs[i].slice(equalsIndex + 1));
|
|
45
|
+
}
|
|
46
|
+
else if (isNextRefProp) {
|
|
47
|
+
props[argName] = convertIfApplicable(sargs[i + 1]);
|
|
48
|
+
i++;
|
|
49
|
+
}
|
|
50
|
+
else if (sargs[i].charAt(0) === '-') {
|
|
51
|
+
if (sargs[i].charAt(1) === '-') {
|
|
52
|
+
props[argName] = true;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
for (let b = 0; b < argName.length; b++) {
|
|
56
|
+
props[argName.charAt(b)] = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
lones.push(convertIfApplicable(argName));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return Object.assign(props, {
|
|
65
|
+
'_': lones
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (typeof (__dirname) === "undefined") {
|
|
70
|
+
global.__dirname = path__namespace.dirname(new URL((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))).pathname);
|
|
71
|
+
}
|
|
72
|
+
const VERSION = JSON.parse(fs__namespace.readFileSync(__dirname + "/../../package.json").toString()).version;
|
|
73
|
+
const COMMENT_HEADER = `
|
|
74
|
+
THIS FILE HAS BEEN GENERATED AUTOMATICALLY
|
|
75
|
+
DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING
|
|
76
|
+
|
|
77
|
+
GENERATED USING @colyseus/schema ${VERSION}
|
|
78
|
+
`;
|
|
79
|
+
function getCommentHeader(singleLineComment = "//") {
|
|
80
|
+
return `${COMMENT_HEADER.split("\n").map(line => `${singleLineComment} ${line}`).join("\n")}`;
|
|
81
|
+
}
|
|
82
|
+
class Context {
|
|
83
|
+
classes = [];
|
|
84
|
+
interfaces = [];
|
|
85
|
+
enums = [];
|
|
86
|
+
getStructures() {
|
|
87
|
+
return {
|
|
88
|
+
classes: this.classes.filter(klass => {
|
|
89
|
+
if (this.isSchemaClass(klass)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
let parentClass = klass;
|
|
94
|
+
while (parentClass = this.getParentClass(parentClass)) {
|
|
95
|
+
if (this.isSchemaClass(parentClass)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}),
|
|
102
|
+
interfaces: this.interfaces,
|
|
103
|
+
enums: this.enums,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
addStructure(structure) {
|
|
107
|
+
if (structure.context === this) {
|
|
108
|
+
return;
|
|
109
|
+
} // skip if already added.
|
|
110
|
+
structure.context = this;
|
|
111
|
+
if (structure instanceof Class) {
|
|
112
|
+
this.classes.push(structure);
|
|
113
|
+
}
|
|
114
|
+
else if (structure instanceof Interface) {
|
|
115
|
+
this.interfaces.push(structure);
|
|
116
|
+
}
|
|
117
|
+
else if (structure instanceof Enum) {
|
|
118
|
+
this.enums.push(structure);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
getParentClass(klass) {
|
|
122
|
+
return this.classes.find(c => c.name === klass.extends);
|
|
123
|
+
}
|
|
124
|
+
isSchemaClass(klass) {
|
|
125
|
+
let isSchema = false;
|
|
126
|
+
let currentClass = klass;
|
|
127
|
+
while (!isSchema && currentClass) {
|
|
128
|
+
//
|
|
129
|
+
// TODO: ideally we should check for actual @colyseus/schema module
|
|
130
|
+
// reference rather than arbitrary strings.
|
|
131
|
+
//
|
|
132
|
+
isSchema = (currentClass.extends === "Schema" ||
|
|
133
|
+
currentClass.extends === "schema.Schema" ||
|
|
134
|
+
currentClass.extends === "Schema.Schema");
|
|
135
|
+
//
|
|
136
|
+
// When extending from `schema.Schema`, it is required to
|
|
137
|
+
// normalize as "Schema" for code generation.
|
|
138
|
+
//
|
|
139
|
+
if (currentClass === klass && isSchema) {
|
|
140
|
+
klass.extends = "Schema";
|
|
141
|
+
}
|
|
142
|
+
currentClass = this.getParentClass(currentClass);
|
|
143
|
+
}
|
|
144
|
+
return isSchema;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
class Interface {
|
|
148
|
+
context;
|
|
149
|
+
name;
|
|
150
|
+
properties = [];
|
|
151
|
+
addProperty(property) {
|
|
152
|
+
if (property.type.indexOf("[]") >= 0) {
|
|
153
|
+
// is array!
|
|
154
|
+
property.childType = property.type.match(/([^\[]+)/i)[1];
|
|
155
|
+
property.type = "array";
|
|
156
|
+
this.properties.push(property);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
this.properties.push(property);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
class Class {
|
|
164
|
+
context;
|
|
165
|
+
name;
|
|
166
|
+
properties = [];
|
|
167
|
+
extends;
|
|
168
|
+
addProperty(property) {
|
|
169
|
+
property.index = this.properties.length;
|
|
170
|
+
this.properties.push(property);
|
|
171
|
+
}
|
|
172
|
+
postProcessing() {
|
|
173
|
+
/**
|
|
174
|
+
* Ensure the proprierties `index` are correct using inheritance
|
|
175
|
+
*/
|
|
176
|
+
let parentKlass = this;
|
|
177
|
+
while (parentKlass &&
|
|
178
|
+
(parentKlass = this.context.classes.find(k => k.name === parentKlass.extends))) {
|
|
179
|
+
this.properties.forEach(prop => {
|
|
180
|
+
prop.index += parentKlass.properties.length;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
class Enum {
|
|
186
|
+
context;
|
|
187
|
+
name;
|
|
188
|
+
properties = [];
|
|
189
|
+
addProperty(property) {
|
|
190
|
+
this.properties.push(property);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
class Property {
|
|
194
|
+
index;
|
|
195
|
+
name;
|
|
196
|
+
type;
|
|
197
|
+
childType;
|
|
198
|
+
deprecated;
|
|
199
|
+
}
|
|
200
|
+
function getInheritanceTree(klass, allClasses, includeSelf = true) {
|
|
201
|
+
let currentClass = klass;
|
|
202
|
+
let inheritanceTree = [];
|
|
203
|
+
if (includeSelf) {
|
|
204
|
+
inheritanceTree.push(currentClass);
|
|
205
|
+
}
|
|
206
|
+
while (currentClass.extends !== "Schema") {
|
|
207
|
+
currentClass = allClasses.find(klass => klass.name == currentClass.extends);
|
|
208
|
+
inheritanceTree.push(currentClass);
|
|
209
|
+
}
|
|
210
|
+
return inheritanceTree;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let currentStructure;
|
|
214
|
+
let currentProperty;
|
|
215
|
+
let globalContext;
|
|
216
|
+
function defineProperty(property, initializer) {
|
|
217
|
+
if (ts__namespace.isIdentifier(initializer)) {
|
|
218
|
+
property.type = "ref";
|
|
219
|
+
property.childType = initializer.text;
|
|
220
|
+
}
|
|
221
|
+
else if (initializer.kind == ts__namespace.SyntaxKind.ObjectLiteralExpression) {
|
|
222
|
+
property.type = initializer.properties[0].name.text;
|
|
223
|
+
property.childType = initializer.properties[0].initializer.text;
|
|
224
|
+
}
|
|
225
|
+
else if (initializer.kind == ts__namespace.SyntaxKind.ArrayLiteralExpression) {
|
|
226
|
+
property.type = "array";
|
|
227
|
+
property.childType = initializer.elements[0].text;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
property.type = initializer.text;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function inspectNode(node, context, decoratorName) {
|
|
234
|
+
switch (node.kind) {
|
|
235
|
+
case ts__namespace.SyntaxKind.ImportClause:
|
|
236
|
+
const specifier = node.parent.moduleSpecifier;
|
|
237
|
+
if (specifier && specifier.text.startsWith('.')) {
|
|
238
|
+
const currentDir = path__namespace.dirname(node.getSourceFile().fileName);
|
|
239
|
+
const pathToImport = path__namespace.resolve(currentDir, specifier.text);
|
|
240
|
+
parseFiles([pathToImport], decoratorName, globalContext);
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
case ts__namespace.SyntaxKind.ClassDeclaration:
|
|
244
|
+
currentStructure = new Class();
|
|
245
|
+
const heritageClauses = node.heritageClauses;
|
|
246
|
+
if (heritageClauses && heritageClauses.length > 0) {
|
|
247
|
+
currentStructure.extends = heritageClauses[0].types[0].expression.getText();
|
|
248
|
+
}
|
|
249
|
+
context.addStructure(currentStructure);
|
|
250
|
+
break;
|
|
251
|
+
case ts__namespace.SyntaxKind.InterfaceDeclaration:
|
|
252
|
+
//
|
|
253
|
+
// Only generate Interfaces if it has "Message" on its name.
|
|
254
|
+
// Example: MyMessage
|
|
255
|
+
//
|
|
256
|
+
const interfaceName = node.name.escapedText.toString();
|
|
257
|
+
if (interfaceName.indexOf("Message") !== -1) {
|
|
258
|
+
currentStructure = new Interface();
|
|
259
|
+
currentStructure.name = interfaceName;
|
|
260
|
+
context.addStructure(currentStructure);
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case ts__namespace.SyntaxKind.EnumDeclaration:
|
|
264
|
+
const enumName = node.name.escapedText.toString();
|
|
265
|
+
currentStructure = new Enum();
|
|
266
|
+
currentStructure.name = enumName;
|
|
267
|
+
context.addStructure(currentStructure);
|
|
268
|
+
break;
|
|
269
|
+
case ts__namespace.SyntaxKind.ExtendsKeyword:
|
|
270
|
+
// console.log(node.getText());
|
|
271
|
+
break;
|
|
272
|
+
case ts__namespace.SyntaxKind.PropertySignature:
|
|
273
|
+
if (currentStructure instanceof Interface) {
|
|
274
|
+
const interfaceDeclaration = node.parent;
|
|
275
|
+
if (currentStructure.name !== interfaceDeclaration.name.escapedText.toString()) {
|
|
276
|
+
// skip if property if for a another interface than the one we're interested in.
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
// define a property of an interface
|
|
280
|
+
const property = new Property();
|
|
281
|
+
property.name = node.name.escapedText.toString();
|
|
282
|
+
property.type = node.type.getText();
|
|
283
|
+
currentStructure.addProperty(property);
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
case ts__namespace.SyntaxKind.Identifier:
|
|
287
|
+
if (node.getText() === "deprecated" &&
|
|
288
|
+
node.parent.kind !== ts__namespace.SyntaxKind.ImportSpecifier) {
|
|
289
|
+
currentProperty = new Property();
|
|
290
|
+
currentProperty.deprecated = true;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
if (node.getText() === decoratorName) {
|
|
294
|
+
const prop = node.parent?.parent?.parent;
|
|
295
|
+
const propDecorator = getDecorators(prop);
|
|
296
|
+
const hasExpression = prop?.expression?.arguments;
|
|
297
|
+
const hasDecorator = (propDecorator?.length > 0);
|
|
298
|
+
/**
|
|
299
|
+
* neither a `@type()` decorator or `type()` call. skip.
|
|
300
|
+
*/
|
|
301
|
+
if (!hasDecorator && !hasExpression) {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
// using as decorator
|
|
305
|
+
if (propDecorator) {
|
|
306
|
+
/**
|
|
307
|
+
* Calling `@type()` as decorator
|
|
308
|
+
*/
|
|
309
|
+
const typeDecorator = propDecorator.find((decorator => {
|
|
310
|
+
return decorator.expression.expression.escapedText === decoratorName;
|
|
311
|
+
})).expression;
|
|
312
|
+
const property = currentProperty || new Property();
|
|
313
|
+
property.name = prop.name.escapedText;
|
|
314
|
+
currentStructure.addProperty(property);
|
|
315
|
+
const typeArgument = typeDecorator.arguments[0];
|
|
316
|
+
defineProperty(property, typeArgument);
|
|
317
|
+
}
|
|
318
|
+
else if (prop.expression.arguments?.[1] &&
|
|
319
|
+
prop.expression.expression.arguments?.[0]) {
|
|
320
|
+
/**
|
|
321
|
+
* Calling `type()` as a regular method
|
|
322
|
+
*/
|
|
323
|
+
const property = currentProperty || new Property();
|
|
324
|
+
property.name = prop.expression.arguments[1].text;
|
|
325
|
+
currentStructure.addProperty(property);
|
|
326
|
+
const typeArgument = prop.expression.expression.arguments[0];
|
|
327
|
+
defineProperty(property, typeArgument);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else if (node.getText() === "setFields" &&
|
|
331
|
+
(node.parent.kind === ts__namespace.SyntaxKind.CallExpression ||
|
|
332
|
+
node.parent.kind === ts__namespace.SyntaxKind.PropertyAccessExpression)) {
|
|
333
|
+
/**
|
|
334
|
+
* Metadata.setFields(klassName, { ... })
|
|
335
|
+
*/
|
|
336
|
+
const callExpression = (node.parent.kind === ts__namespace.SyntaxKind.PropertyAccessExpression)
|
|
337
|
+
? node.parent.parent
|
|
338
|
+
: node.parent;
|
|
339
|
+
/**
|
|
340
|
+
* Skip if @codegen-ignore comment is found before the call expression
|
|
341
|
+
* TODO: currently, if @codegen-ignore is on the file, it will skip all the setFields calls.
|
|
342
|
+
*/
|
|
343
|
+
const sourceFile = node.getSourceFile();
|
|
344
|
+
const fullText = sourceFile.getFullText();
|
|
345
|
+
const nodeStart = callExpression.getFullStart();
|
|
346
|
+
const textBeforeNode = fullText.substring(0, nodeStart);
|
|
347
|
+
if (textBeforeNode.includes('@codegen-ignore')) {
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
if (callExpression.kind !== ts__namespace.SyntaxKind.CallExpression) {
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
const classNameNode = callExpression.arguments[0];
|
|
354
|
+
const className = ts__namespace.isClassExpression(classNameNode)
|
|
355
|
+
? classNameNode.name?.escapedText.toString()
|
|
356
|
+
: classNameNode.getText();
|
|
357
|
+
// skip if no className is provided
|
|
358
|
+
if (!className) {
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
if (currentStructure?.name !== className) {
|
|
362
|
+
currentStructure = new Class();
|
|
363
|
+
}
|
|
364
|
+
context.addStructure(currentStructure);
|
|
365
|
+
currentStructure.extends = "Schema"; // force extends to Schema
|
|
366
|
+
currentStructure.name = className;
|
|
367
|
+
const types = callExpression.arguments[1];
|
|
368
|
+
for (let i = 0; i < types.properties.length; i++) {
|
|
369
|
+
const prop = types.properties[i];
|
|
370
|
+
const property = currentProperty || new Property();
|
|
371
|
+
property.name = prop.name.escapedText;
|
|
372
|
+
currentStructure.addProperty(property);
|
|
373
|
+
defineProperty(property, prop.initializer);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else if (node.getText() === "defineTypes" &&
|
|
377
|
+
(node.parent.kind === ts__namespace.SyntaxKind.CallExpression ||
|
|
378
|
+
node.parent.kind === ts__namespace.SyntaxKind.PropertyAccessExpression)) {
|
|
379
|
+
/**
|
|
380
|
+
* JavaScript source file (`.js`)
|
|
381
|
+
* Using `defineTypes()`
|
|
382
|
+
*/
|
|
383
|
+
const callExpression = (node.parent.kind === ts__namespace.SyntaxKind.PropertyAccessExpression)
|
|
384
|
+
? node.parent.parent
|
|
385
|
+
: node.parent;
|
|
386
|
+
if (callExpression.kind !== ts__namespace.SyntaxKind.CallExpression) {
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
const className = callExpression.arguments[0].getText();
|
|
390
|
+
currentStructure.name = className;
|
|
391
|
+
const types = callExpression.arguments[1];
|
|
392
|
+
for (let i = 0; i < types.properties.length; i++) {
|
|
393
|
+
const prop = types.properties[i];
|
|
394
|
+
const property = currentProperty || new Property();
|
|
395
|
+
property.name = prop.name.escapedText;
|
|
396
|
+
currentStructure.addProperty(property);
|
|
397
|
+
defineProperty(property, prop.initializer);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (node.parent.kind === ts__namespace.SyntaxKind.ClassDeclaration) {
|
|
401
|
+
currentStructure.name = node.getText();
|
|
402
|
+
}
|
|
403
|
+
currentProperty = undefined;
|
|
404
|
+
break;
|
|
405
|
+
case ts__namespace.SyntaxKind.CallExpression:
|
|
406
|
+
/**
|
|
407
|
+
* Defining schema via `schema.schema({ ... })`
|
|
408
|
+
* - schema.schema({})
|
|
409
|
+
* - schema({})
|
|
410
|
+
* - ClassName.extends({})
|
|
411
|
+
*/
|
|
412
|
+
if (((node.expression?.getText() === "schema.schema" ||
|
|
413
|
+
node.expression?.getText() === "schema") ||
|
|
414
|
+
(node.expression?.getText().indexOf(".extends") !== -1)) &&
|
|
415
|
+
node.arguments[0].kind === ts__namespace.SyntaxKind.ObjectLiteralExpression) {
|
|
416
|
+
const callExpression = node;
|
|
417
|
+
let className = callExpression.arguments[1]?.getText();
|
|
418
|
+
if (!className && callExpression.parent.kind === ts__namespace.SyntaxKind.VariableDeclaration) {
|
|
419
|
+
className = callExpression.parent.name?.getText();
|
|
420
|
+
}
|
|
421
|
+
// skip if no className is provided
|
|
422
|
+
if (!className) {
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
if (currentStructure?.name !== className) {
|
|
426
|
+
currentStructure = new Class();
|
|
427
|
+
context.addStructure(currentStructure);
|
|
428
|
+
}
|
|
429
|
+
if (node.expression?.getText().indexOf(".extends") !== -1) {
|
|
430
|
+
// if it's using `.extends({})`
|
|
431
|
+
const extendsClass = node.expression?.expression?.escapedText;
|
|
432
|
+
// skip if no extendsClass is provided
|
|
433
|
+
if (!extendsClass) {
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
currentStructure.extends = extendsClass;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
// if it's using `schema({})`
|
|
440
|
+
currentStructure.extends = "Schema"; // force extends to Schema
|
|
441
|
+
}
|
|
442
|
+
currentStructure.name = className;
|
|
443
|
+
const types = callExpression.arguments[0];
|
|
444
|
+
for (let i = 0; i < types.properties.length; i++) {
|
|
445
|
+
const prop = types.properties[i];
|
|
446
|
+
const property = currentProperty || new Property();
|
|
447
|
+
property.name = prop.name.escapedText;
|
|
448
|
+
currentStructure.addProperty(property);
|
|
449
|
+
defineProperty(property, prop.initializer);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
break;
|
|
453
|
+
case ts__namespace.SyntaxKind.EnumMember:
|
|
454
|
+
if (currentStructure instanceof Enum) {
|
|
455
|
+
const initializer = node.initializer?.text;
|
|
456
|
+
const name = node.getFirstToken().getText();
|
|
457
|
+
const property = currentProperty || new Property();
|
|
458
|
+
property.name = name;
|
|
459
|
+
if (initializer !== undefined) {
|
|
460
|
+
property.type = initializer;
|
|
461
|
+
}
|
|
462
|
+
currentStructure.addProperty(property);
|
|
463
|
+
currentProperty = undefined;
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
ts__namespace.forEachChild(node, (n) => inspectNode(n, context, decoratorName));
|
|
468
|
+
}
|
|
469
|
+
let parsedFiles;
|
|
470
|
+
function parseFiles(fileNames, decoratorName = "type", context = new Context()) {
|
|
471
|
+
/**
|
|
472
|
+
* Re-set globalContext for each test case
|
|
473
|
+
*/
|
|
474
|
+
if (globalContext !== context) {
|
|
475
|
+
parsedFiles = {};
|
|
476
|
+
globalContext = context;
|
|
477
|
+
}
|
|
478
|
+
fileNames.forEach((fileName) => {
|
|
479
|
+
let sourceFile;
|
|
480
|
+
let sourceFileName;
|
|
481
|
+
const fileNameAlternatives = [];
|
|
482
|
+
if (!fileName.endsWith(".ts") &&
|
|
483
|
+
!fileName.endsWith(".js") &&
|
|
484
|
+
!fileName.endsWith(".mjs")) {
|
|
485
|
+
fileNameAlternatives.push(`${fileName}.ts`);
|
|
486
|
+
fileNameAlternatives.push(`${fileName}/index.ts`);
|
|
487
|
+
}
|
|
488
|
+
else if (fileName.endsWith(".js")) {
|
|
489
|
+
// Handle .js extensions by also trying .ts (ESM imports often use .js extension)
|
|
490
|
+
fileNameAlternatives.push(fileName);
|
|
491
|
+
fileNameAlternatives.push(fileName.replace(/\.js$/, ".ts"));
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
fileNameAlternatives.push(fileName);
|
|
495
|
+
}
|
|
496
|
+
for (let i = 0; i < fileNameAlternatives.length; i++) {
|
|
497
|
+
try {
|
|
498
|
+
sourceFileName = path__namespace.resolve(fileNameAlternatives[i]);
|
|
499
|
+
if (parsedFiles[sourceFileName]) {
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
sourceFile = ts__namespace.createSourceFile(sourceFileName, fs.readFileSync(sourceFileName).toString(), ts__namespace.ScriptTarget.Latest, true);
|
|
503
|
+
parsedFiles[sourceFileName] = true;
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
catch (e) {
|
|
507
|
+
// console.log(`${fileNameAlternatives[i]} => ${e.message}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (sourceFile) {
|
|
511
|
+
inspectNode(sourceFile, context, decoratorName);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return context.getStructures();
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* TypeScript 4.8+ has introduced a change on how to access decorators.
|
|
518
|
+
* - https://github.com/microsoft/TypeScript/pull/49089
|
|
519
|
+
* - https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/#decorators-are-placed-on-modifiers-on-typescripts-syntax-trees
|
|
520
|
+
*/
|
|
521
|
+
function getDecorators(node) {
|
|
522
|
+
if (node == undefined) {
|
|
523
|
+
return undefined;
|
|
524
|
+
}
|
|
525
|
+
// TypeScript 4.7 and below
|
|
526
|
+
// @ts-ignore
|
|
527
|
+
if (node.decorators) {
|
|
528
|
+
return node.decorators;
|
|
529
|
+
}
|
|
530
|
+
// TypeScript 4.8 and above
|
|
531
|
+
// @ts-ignore
|
|
532
|
+
if (ts__namespace.canHaveDecorators && ts__namespace.canHaveDecorators(node)) {
|
|
533
|
+
// @ts-ignore
|
|
534
|
+
const decorators = ts__namespace.getDecorators(node);
|
|
535
|
+
return decorators ? Array.from(decorators) : undefined;
|
|
536
|
+
}
|
|
537
|
+
// @ts-ignore
|
|
538
|
+
return node.modifiers?.filter(ts__namespace.isDecorator);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const typeMaps$6 = {
|
|
542
|
+
"string": "string",
|
|
543
|
+
"number": "float",
|
|
544
|
+
"boolean": "bool",
|
|
545
|
+
"int8": "sbyte",
|
|
546
|
+
"uint8": "byte",
|
|
547
|
+
"int16": "short",
|
|
548
|
+
"uint16": "ushort",
|
|
549
|
+
"int32": "int",
|
|
550
|
+
"uint32": "uint",
|
|
551
|
+
"int64": "long",
|
|
552
|
+
"uint64": "ulong",
|
|
553
|
+
"float32": "float",
|
|
554
|
+
"float64": "double",
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* C# Code Generator
|
|
558
|
+
*/
|
|
559
|
+
const capitalize$1 = (s) => {
|
|
560
|
+
if (typeof s !== 'string')
|
|
561
|
+
return '';
|
|
562
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
563
|
+
};
|
|
564
|
+
function generate$7(context, options) {
|
|
565
|
+
// enrich typeMaps with enums
|
|
566
|
+
context.enums.forEach((structure) => {
|
|
567
|
+
typeMaps$6[structure.name] = structure.name;
|
|
568
|
+
});
|
|
569
|
+
return [
|
|
570
|
+
...context.classes.map(structure => ({
|
|
571
|
+
name: `${structure.name}.cs`,
|
|
572
|
+
content: generateClass$6(structure, options.namespace)
|
|
573
|
+
})),
|
|
574
|
+
...context.interfaces.map(structure => ({
|
|
575
|
+
name: `${structure.name}.cs`,
|
|
576
|
+
content: generateInterface$1(structure, options.namespace),
|
|
577
|
+
})),
|
|
578
|
+
...context.enums.filter(structure => structure.name !== 'OPERATION').map((structure) => ({
|
|
579
|
+
name: `${structure.name}.cs`,
|
|
580
|
+
content: generateEnum(structure, options.namespace),
|
|
581
|
+
})),
|
|
582
|
+
];
|
|
583
|
+
}
|
|
584
|
+
function generateClass$6(klass, namespace) {
|
|
585
|
+
const indent = (namespace) ? "\t" : "";
|
|
586
|
+
return `${getCommentHeader()}
|
|
587
|
+
|
|
588
|
+
using Colyseus.Schema;
|
|
589
|
+
#if UNITY_5_3_OR_NEWER
|
|
590
|
+
using UnityEngine.Scripting;
|
|
591
|
+
#endif
|
|
592
|
+
${namespace ? `\nnamespace ${namespace} {` : ""}
|
|
593
|
+
${indent}public partial class ${klass.name} : ${klass.extends} {
|
|
594
|
+
#if UNITY_5_3_OR_NEWER
|
|
595
|
+
[Preserve]
|
|
596
|
+
#endif
|
|
597
|
+
public ${klass.name}() { }
|
|
598
|
+
${klass.properties.map((prop) => generateProperty$4(prop, indent)).join("\n\n")}
|
|
599
|
+
${indent}}
|
|
600
|
+
${namespace ? "}" : ""}
|
|
601
|
+
`;
|
|
602
|
+
}
|
|
603
|
+
function generateEnum(_enum, namespace) {
|
|
604
|
+
const indent = namespace ? "\t" : "";
|
|
605
|
+
return `${getCommentHeader()}
|
|
606
|
+
${namespace ? `\nnamespace ${namespace} {` : ""}
|
|
607
|
+
${indent}public struct ${_enum.name} {
|
|
608
|
+
|
|
609
|
+
${_enum.properties
|
|
610
|
+
.map((prop) => {
|
|
611
|
+
let dataType = "int";
|
|
612
|
+
let value;
|
|
613
|
+
if (prop.type) {
|
|
614
|
+
if (isNaN(Number(prop.type))) {
|
|
615
|
+
value = `"${prop.type}"`;
|
|
616
|
+
dataType = "string";
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
value = Number(prop.type);
|
|
620
|
+
dataType = Number.isInteger(value) ? 'int' : 'float';
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
value = _enum.properties.indexOf(prop);
|
|
625
|
+
}
|
|
626
|
+
return `${indent}\tpublic const ${dataType} ${prop.name} = ${value};`;
|
|
627
|
+
})
|
|
628
|
+
.join("\n")}
|
|
629
|
+
${indent}}
|
|
630
|
+
${namespace ? "}" : ""}`;
|
|
631
|
+
}
|
|
632
|
+
function generateProperty$4(prop, indent = "") {
|
|
633
|
+
let typeArgs = `"${prop.type}"`;
|
|
634
|
+
let property = "public";
|
|
635
|
+
let langType;
|
|
636
|
+
let initializer = "";
|
|
637
|
+
if (prop.childType) {
|
|
638
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
639
|
+
langType = getType(prop);
|
|
640
|
+
typeArgs += `, typeof(${langType})`;
|
|
641
|
+
if (!isUpcaseFirst) {
|
|
642
|
+
typeArgs += `, "${prop.childType}"`;
|
|
643
|
+
}
|
|
644
|
+
initializer = `null`;
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
langType = getType(prop);
|
|
648
|
+
initializer = `default(${langType})`;
|
|
649
|
+
}
|
|
650
|
+
property += ` ${langType} ${prop.name}`;
|
|
651
|
+
let ret = (prop.deprecated) ? `\t\t[System.Obsolete("field '${prop.name}' is deprecated.", true)]\n` : '';
|
|
652
|
+
return ret + `\t${indent}[Type(${prop.index}, ${typeArgs})]
|
|
653
|
+
\t${indent}${property} = ${initializer};`;
|
|
654
|
+
}
|
|
655
|
+
function generateInterface$1(struct, namespace) {
|
|
656
|
+
const indent = (namespace) ? "\t" : "";
|
|
657
|
+
return `${getCommentHeader()}
|
|
658
|
+
|
|
659
|
+
using Colyseus.Schema;
|
|
660
|
+
${namespace ? `\nnamespace ${namespace} {` : ""}
|
|
661
|
+
${indent}public class ${struct.name} {
|
|
662
|
+
${struct.properties.map(prop => `\t${indent}public ${getType(prop)} ${prop.name};`).join("\n")}
|
|
663
|
+
${indent}}
|
|
664
|
+
${namespace ? "}" : ""}
|
|
665
|
+
`;
|
|
666
|
+
}
|
|
667
|
+
function getChildType(prop) {
|
|
668
|
+
return typeMaps$6[prop.childType];
|
|
669
|
+
}
|
|
670
|
+
function getType(prop) {
|
|
671
|
+
if (prop.childType) {
|
|
672
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
673
|
+
let type;
|
|
674
|
+
if (prop.type === "ref") {
|
|
675
|
+
type = (isUpcaseFirst)
|
|
676
|
+
? prop.childType
|
|
677
|
+
: getChildType(prop);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
const containerClass = capitalize$1(prop.type);
|
|
681
|
+
type = (isUpcaseFirst)
|
|
682
|
+
? `${containerClass}Schema<${prop.childType}>`
|
|
683
|
+
: `${containerClass}Schema<${getChildType(prop)}>`;
|
|
684
|
+
}
|
|
685
|
+
return type;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
return (prop.type === "array")
|
|
689
|
+
? `${typeMaps$6[prop.childType] || prop.childType}[]`
|
|
690
|
+
: typeMaps$6[prop.type];
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const typeMaps$5 = {
|
|
695
|
+
"string": "string",
|
|
696
|
+
"number": "varint_t",
|
|
697
|
+
"boolean": "bool",
|
|
698
|
+
"int8": "int8_t",
|
|
699
|
+
"uint8": "uint8_t",
|
|
700
|
+
"int16": "int16_t",
|
|
701
|
+
"uint16": "uint16_t",
|
|
702
|
+
"int32": "int32_t",
|
|
703
|
+
"uint32": "uint32_t",
|
|
704
|
+
"int64": "int64_t",
|
|
705
|
+
"uint64": "uint64_t",
|
|
706
|
+
"float32": "float32_t",
|
|
707
|
+
"float64": "float64_t",
|
|
708
|
+
};
|
|
709
|
+
const typeInitializer$2 = {
|
|
710
|
+
"string": '""',
|
|
711
|
+
"number": "0",
|
|
712
|
+
"boolean": "false",
|
|
713
|
+
"int8": "0",
|
|
714
|
+
"uint8": "0",
|
|
715
|
+
"int16": "0",
|
|
716
|
+
"uint16": "0",
|
|
717
|
+
"int32": "0",
|
|
718
|
+
"uint32": "0",
|
|
719
|
+
"int64": "0",
|
|
720
|
+
"uint64": "0",
|
|
721
|
+
"float32": "0",
|
|
722
|
+
"float64": "0",
|
|
723
|
+
};
|
|
724
|
+
/**
|
|
725
|
+
* C++ Code Generator
|
|
726
|
+
*/
|
|
727
|
+
const capitalize = (s) => {
|
|
728
|
+
if (typeof s !== 'string')
|
|
729
|
+
return '';
|
|
730
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
731
|
+
};
|
|
732
|
+
const distinct$3 = (value, index, self) => self.indexOf(value) === index;
|
|
733
|
+
function generate$6(context, options) {
|
|
734
|
+
return context.classes.map(klass => ({
|
|
735
|
+
name: klass.name + ".hpp",
|
|
736
|
+
content: generateClass$5(klass, options.namespace, context.classes)
|
|
737
|
+
}));
|
|
738
|
+
}
|
|
739
|
+
function generateClass$5(klass, namespace, allClasses) {
|
|
740
|
+
const propertiesPerType = {};
|
|
741
|
+
const allRefs = [];
|
|
742
|
+
klass.properties.forEach(property => {
|
|
743
|
+
let type = property.type;
|
|
744
|
+
if (!propertiesPerType[type]) {
|
|
745
|
+
propertiesPerType[type] = [];
|
|
746
|
+
}
|
|
747
|
+
propertiesPerType[type].push(property);
|
|
748
|
+
// keep all refs list
|
|
749
|
+
if ((type === "ref" || type === "array" || type === "map")) {
|
|
750
|
+
allRefs.push(property);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
const allProperties = getAllProperties(klass, allClasses);
|
|
754
|
+
const createInstanceMethod = (allRefs.length === 0) ? "" :
|
|
755
|
+
`\tinline Schema* createInstance(std::type_index type) {
|
|
756
|
+
\t\t${generateFieldIfElseChain(allRefs, (property) => `type == typeid(${property.childType})`, (property) => `return new ${property.childType}();`, (property) => typeMaps$5[property.childType] === undefined)}
|
|
757
|
+
\t\treturn ${klass.extends}::createInstance(type);
|
|
758
|
+
\t}`;
|
|
759
|
+
return `${getCommentHeader()}
|
|
760
|
+
#ifndef __SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__
|
|
761
|
+
#define __SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__ 1
|
|
762
|
+
|
|
763
|
+
#include "schema.h"
|
|
764
|
+
#include <typeinfo>
|
|
765
|
+
#include <typeindex>
|
|
766
|
+
|
|
767
|
+
${allRefs.
|
|
768
|
+
filter(ref => ref.childType && typeMaps$5[ref.childType] === undefined).
|
|
769
|
+
map(ref => ref.childType).
|
|
770
|
+
concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
|
|
771
|
+
filter(distinct$3).
|
|
772
|
+
map(childType => `#include "${childType}.hpp"`).
|
|
773
|
+
join("\n")}
|
|
774
|
+
|
|
775
|
+
using namespace colyseus::schema;
|
|
776
|
+
|
|
777
|
+
${namespace ? `namespace ${namespace} {` : ""}
|
|
778
|
+
class ${klass.name} : public ${klass.extends} {
|
|
779
|
+
public:
|
|
780
|
+
${klass.properties.map(prop => generateProperty$3(prop)).join("\n")}
|
|
781
|
+
|
|
782
|
+
\t${klass.name}() {
|
|
783
|
+
\t\tthis->_indexes = ${generateAllIndexes(allProperties)};
|
|
784
|
+
\t\tthis->_types = ${generateAllTypes(allProperties)};
|
|
785
|
+
\t\tthis->_childPrimitiveTypes = ${generateAllChildPrimitiveTypes(allProperties)};
|
|
786
|
+
\t\tthis->_childSchemaTypes = ${generateAllChildSchemaTypes(allProperties)};
|
|
787
|
+
\t}
|
|
788
|
+
|
|
789
|
+
\tvirtual ~${klass.name}() {
|
|
790
|
+
\t\t${generateDestructors(allProperties).join("\n\t\t")}
|
|
791
|
+
\t}
|
|
792
|
+
|
|
793
|
+
protected:
|
|
794
|
+
${Object.keys(propertiesPerType).map(type => generateGettersAndSetters(klass, type, propertiesPerType[type])).
|
|
795
|
+
join("\n")}
|
|
796
|
+
|
|
797
|
+
${createInstanceMethod}
|
|
798
|
+
};
|
|
799
|
+
${namespace ? "}" : ""}
|
|
800
|
+
|
|
801
|
+
#endif
|
|
802
|
+
`;
|
|
803
|
+
}
|
|
804
|
+
function generateProperty$3(prop) {
|
|
805
|
+
let property = "";
|
|
806
|
+
let langType;
|
|
807
|
+
let initializer = "";
|
|
808
|
+
let isPropPointer = "";
|
|
809
|
+
if (prop.childType) {
|
|
810
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
811
|
+
if (prop.type === "ref") {
|
|
812
|
+
langType = `${prop.childType}`;
|
|
813
|
+
initializer = `new ${prop.childType}()`;
|
|
814
|
+
}
|
|
815
|
+
else if (prop.type === "array") {
|
|
816
|
+
langType = (isUpcaseFirst)
|
|
817
|
+
? `ArraySchema<${prop.childType}*>`
|
|
818
|
+
: `ArraySchema<${typeMaps$5[prop.childType]}>`;
|
|
819
|
+
initializer = `new ${langType}()`;
|
|
820
|
+
}
|
|
821
|
+
else if (prop.type === "map") {
|
|
822
|
+
langType = (isUpcaseFirst)
|
|
823
|
+
? `MapSchema<${prop.childType}*>`
|
|
824
|
+
: `MapSchema<${typeMaps$5[prop.childType]}>`;
|
|
825
|
+
initializer = `new ${langType}()`;
|
|
826
|
+
}
|
|
827
|
+
isPropPointer = "*";
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
langType = typeMaps$5[prop.type];
|
|
831
|
+
initializer = typeInitializer$2[prop.type];
|
|
832
|
+
}
|
|
833
|
+
property += ` ${langType} ${isPropPointer}${prop.name}`;
|
|
834
|
+
return `\t${property} = ${initializer};`;
|
|
835
|
+
}
|
|
836
|
+
function generateGettersAndSetters(klass, type, properties) {
|
|
837
|
+
let langType = typeMaps$5[type];
|
|
838
|
+
let typeCast = "";
|
|
839
|
+
const getMethodName = `get${capitalize(type)}`;
|
|
840
|
+
const setMethodName = `set${capitalize(type)}`;
|
|
841
|
+
if (type === "ref") {
|
|
842
|
+
langType = "Schema*";
|
|
843
|
+
}
|
|
844
|
+
else if (type === "array") {
|
|
845
|
+
langType = `ArraySchema<char*> *`;
|
|
846
|
+
typeCast = `(ArraySchema<char*> *)`;
|
|
847
|
+
}
|
|
848
|
+
else if (type === "map") {
|
|
849
|
+
langType = `MapSchema<char*> *`;
|
|
850
|
+
typeCast = `(MapSchema<char*> *)`;
|
|
851
|
+
}
|
|
852
|
+
return `\tinline ${langType} ${getMethodName}(const string &field)
|
|
853
|
+
\t{
|
|
854
|
+
\t\t${generateFieldIfElseChain(properties, (property) => `field == "${property.name}"`, (property) => `return ${typeCast}this->${property.name};`)}
|
|
855
|
+
\t\treturn ${klass.extends}::${getMethodName}(field);
|
|
856
|
+
\t}
|
|
857
|
+
|
|
858
|
+
\tinline void ${setMethodName}(const string &field, ${langType} value)
|
|
859
|
+
\t{
|
|
860
|
+
\t\t${generateFieldIfElseChain(properties, (property) => `field == "${property.name}"`, (property) => {
|
|
861
|
+
const isSchemaType = (typeMaps$5[property.childType] === undefined);
|
|
862
|
+
if (type === "ref") {
|
|
863
|
+
langType = `${property.childType}*`;
|
|
864
|
+
typeCast = (isSchemaType)
|
|
865
|
+
? `(${property.childType}*)`
|
|
866
|
+
: `/* bug? */`;
|
|
867
|
+
}
|
|
868
|
+
else if (type === "array") {
|
|
869
|
+
typeCast = (isSchemaType)
|
|
870
|
+
? `(ArraySchema<${property.childType}*> *)`
|
|
871
|
+
: `(ArraySchema<${typeMaps$5[property.childType]}> *)`;
|
|
872
|
+
}
|
|
873
|
+
else if (type === "map") {
|
|
874
|
+
typeCast = (isSchemaType)
|
|
875
|
+
? `(MapSchema<${property.childType}*> *)`
|
|
876
|
+
: `(MapSchema<${typeMaps$5[property.childType]}> *)`;
|
|
877
|
+
}
|
|
878
|
+
return `this->${property.name} = ${typeCast}value;\n\t\t\treturn;`;
|
|
879
|
+
})}
|
|
880
|
+
\t\treturn ${klass.extends}::${setMethodName}(field, value);
|
|
881
|
+
\t}`;
|
|
882
|
+
}
|
|
883
|
+
function generateFieldIfElseChain(properties, ifCallback, callback, filter = (_) => true) {
|
|
884
|
+
let chain = "";
|
|
885
|
+
const uniqueChecks = [];
|
|
886
|
+
properties.filter(filter).forEach((property, i) => {
|
|
887
|
+
const check = ifCallback(property);
|
|
888
|
+
if (uniqueChecks.indexOf(check) === -1) {
|
|
889
|
+
uniqueChecks.push(check);
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (i === 0) {
|
|
895
|
+
chain += "if ";
|
|
896
|
+
}
|
|
897
|
+
else {
|
|
898
|
+
chain += " else if ";
|
|
899
|
+
}
|
|
900
|
+
chain += `(${check})
|
|
901
|
+
\t\t{
|
|
902
|
+
\t\t\t${callback(property)}\n
|
|
903
|
+
\t\t}`;
|
|
904
|
+
});
|
|
905
|
+
return chain;
|
|
906
|
+
}
|
|
907
|
+
function generateAllIndexes(properties) {
|
|
908
|
+
return `{${properties.map((property, i) => `{${i}, "${property.name}"}`).join(", ")}}`;
|
|
909
|
+
}
|
|
910
|
+
function generateAllTypes(properties) {
|
|
911
|
+
return `{${properties.map((property, i) => `{${i}, "${property.type}"}`).join(", ")}}`;
|
|
912
|
+
}
|
|
913
|
+
function generateAllChildSchemaTypes(properties) {
|
|
914
|
+
return `{${properties.map((property, i) => {
|
|
915
|
+
if (property.childType && typeMaps$5[property.childType] === undefined) {
|
|
916
|
+
return `{${i}, typeid(${property.childType})}`;
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
}).filter(r => r !== null).join(", ")}}`;
|
|
922
|
+
}
|
|
923
|
+
function generateAllChildPrimitiveTypes(properties) {
|
|
924
|
+
return `{${properties.map((property, i) => {
|
|
925
|
+
if (typeMaps$5[property.childType] !== undefined) {
|
|
926
|
+
return `{${i}, "${property.childType}"}`;
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
}).filter(r => r !== null).join(", ")}}`;
|
|
932
|
+
}
|
|
933
|
+
function generateDestructors(properties) {
|
|
934
|
+
return properties.map((property, i) => {
|
|
935
|
+
if (property.childType) {
|
|
936
|
+
return `delete this->${property.name};`;
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
}).filter(r => r !== null);
|
|
942
|
+
}
|
|
943
|
+
function getAllProperties(klass, allClasses) {
|
|
944
|
+
let properties = [];
|
|
945
|
+
getInheritanceTree(klass, allClasses).reverse().forEach((klass) => {
|
|
946
|
+
properties = properties.concat(klass.properties);
|
|
947
|
+
});
|
|
948
|
+
return properties;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const typeMaps$4 = {
|
|
952
|
+
"string": "String",
|
|
953
|
+
"number": "Dynamic",
|
|
954
|
+
"boolean": "Bool",
|
|
955
|
+
"int8": "Int",
|
|
956
|
+
"uint8": "UInt",
|
|
957
|
+
"int16": "Int",
|
|
958
|
+
"uint16": "UInt",
|
|
959
|
+
"int32": "Int",
|
|
960
|
+
"uint32": "UInt",
|
|
961
|
+
"int64": "Int",
|
|
962
|
+
"uint64": "UInt",
|
|
963
|
+
"float32": "Float",
|
|
964
|
+
"float64": "Float",
|
|
965
|
+
};
|
|
966
|
+
const typeInitializer$1 = {
|
|
967
|
+
"string": '""',
|
|
968
|
+
"number": "0",
|
|
969
|
+
"boolean": "false",
|
|
970
|
+
"int8": "0",
|
|
971
|
+
"uint8": "0",
|
|
972
|
+
"int16": "0",
|
|
973
|
+
"uint16": "0",
|
|
974
|
+
"int32": "0",
|
|
975
|
+
"uint32": "0",
|
|
976
|
+
"int64": "0",
|
|
977
|
+
"uint64": "0",
|
|
978
|
+
"float32": "0",
|
|
979
|
+
"float64": "0",
|
|
980
|
+
};
|
|
981
|
+
function generate$5(context, options) {
|
|
982
|
+
return context.classes.map(klass => ({
|
|
983
|
+
name: klass.name + ".hx",
|
|
984
|
+
content: generateClass$4(klass, options.namespace, context.classes)
|
|
985
|
+
}));
|
|
986
|
+
}
|
|
987
|
+
function generateClass$4(klass, namespace, allClasses) {
|
|
988
|
+
return `${getCommentHeader()}
|
|
989
|
+
|
|
990
|
+
${namespace ? `package ${namespace};` : ""}
|
|
991
|
+
import io.colyseus.serializer.schema.Schema;
|
|
992
|
+
import io.colyseus.serializer.schema.types.*;
|
|
993
|
+
|
|
994
|
+
class ${klass.name} extends ${klass.extends} {
|
|
995
|
+
${klass.properties.map(prop => generateProperty$2(prop)).join("\n")}
|
|
996
|
+
}
|
|
997
|
+
`;
|
|
998
|
+
}
|
|
999
|
+
function generateProperty$2(prop) {
|
|
1000
|
+
let langType;
|
|
1001
|
+
let initializer = "";
|
|
1002
|
+
let typeArgs = `"${prop.type}"`;
|
|
1003
|
+
if (prop.childType) {
|
|
1004
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
1005
|
+
if (isUpcaseFirst) {
|
|
1006
|
+
typeArgs += `, ${prop.childType}`;
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
typeArgs += `, "${prop.childType}"`;
|
|
1010
|
+
}
|
|
1011
|
+
if (prop.type === "ref") {
|
|
1012
|
+
langType = `${prop.childType}`;
|
|
1013
|
+
initializer = `new ${prop.childType}()`;
|
|
1014
|
+
}
|
|
1015
|
+
else if (prop.type === "array") {
|
|
1016
|
+
langType = (isUpcaseFirst)
|
|
1017
|
+
? `ArraySchema<${prop.childType}>`
|
|
1018
|
+
: `ArraySchema<${typeMaps$4[prop.childType]}>`;
|
|
1019
|
+
initializer = `new ${langType}()`;
|
|
1020
|
+
}
|
|
1021
|
+
else if (prop.type === "map") {
|
|
1022
|
+
langType = (isUpcaseFirst)
|
|
1023
|
+
? `MapSchema<${prop.childType}>`
|
|
1024
|
+
: `MapSchema<${typeMaps$4[prop.childType]}>`;
|
|
1025
|
+
initializer = `new ${langType}()`;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
else {
|
|
1029
|
+
langType = typeMaps$4[prop.type];
|
|
1030
|
+
initializer = typeInitializer$1[prop.type];
|
|
1031
|
+
}
|
|
1032
|
+
// TODO: remove initializer. The callbacks at the Haxe decoder side have a
|
|
1033
|
+
// "FIXME" comment about this on Decoder.hx
|
|
1034
|
+
return `\t@:type(${typeArgs})\n\tpublic var ${prop.name}: ${langType} = ${initializer};\n`;
|
|
1035
|
+
// return `\t@:type(${typeArgs})\n\tpublic var ${prop.name}: ${langType};\n`
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const typeMaps$3 = {
|
|
1039
|
+
"string": "string",
|
|
1040
|
+
"number": "number",
|
|
1041
|
+
"boolean": "boolean",
|
|
1042
|
+
"int8": "number",
|
|
1043
|
+
"uint8": "number",
|
|
1044
|
+
"int16": "number",
|
|
1045
|
+
"uint16": "number",
|
|
1046
|
+
"int32": "number",
|
|
1047
|
+
"uint32": "number",
|
|
1048
|
+
"int64": "number",
|
|
1049
|
+
"uint64": "number",
|
|
1050
|
+
"float32": "number",
|
|
1051
|
+
"float64": "number",
|
|
1052
|
+
};
|
|
1053
|
+
const distinct$2 = (value, index, self) => self.indexOf(value) === index;
|
|
1054
|
+
function generate$4(context, options) {
|
|
1055
|
+
return [
|
|
1056
|
+
...context.classes.map(structure => ({
|
|
1057
|
+
name: structure.name + ".ts",
|
|
1058
|
+
content: generateClass$3(structure, options.namespace, context.classes)
|
|
1059
|
+
})),
|
|
1060
|
+
...context.interfaces.map(structure => ({
|
|
1061
|
+
name: structure.name + ".ts",
|
|
1062
|
+
content: generateInterface(structure, options.namespace, context.classes),
|
|
1063
|
+
}))
|
|
1064
|
+
];
|
|
1065
|
+
}
|
|
1066
|
+
function generateClass$3(klass, namespace, allClasses) {
|
|
1067
|
+
const allRefs = [];
|
|
1068
|
+
klass.properties.forEach(property => {
|
|
1069
|
+
let type = property.type;
|
|
1070
|
+
// keep all refs list
|
|
1071
|
+
if ((type === "ref" || type === "array" || type === "map" || type === "set")) {
|
|
1072
|
+
allRefs.push(property);
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
return `${getCommentHeader()}
|
|
1076
|
+
|
|
1077
|
+
import { Schema, type, ArraySchema, MapSchema, SetSchema, DataChange } from '@colyseus/schema';
|
|
1078
|
+
${allRefs.
|
|
1079
|
+
filter(ref => ref.childType && typeMaps$3[ref.childType] === undefined).
|
|
1080
|
+
map(ref => ref.childType).
|
|
1081
|
+
concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
|
|
1082
|
+
filter(distinct$2).
|
|
1083
|
+
map(childType => `import { ${childType} } from './${childType}'`).
|
|
1084
|
+
join("\n")}
|
|
1085
|
+
|
|
1086
|
+
export class ${klass.name} extends ${klass.extends} {
|
|
1087
|
+
${klass.properties.map(prop => ` ${generateProperty$1(prop)}`).join("\n")}
|
|
1088
|
+
}
|
|
1089
|
+
`;
|
|
1090
|
+
}
|
|
1091
|
+
function generateProperty$1(prop) {
|
|
1092
|
+
let langType;
|
|
1093
|
+
let initializer = "";
|
|
1094
|
+
let typeArgs;
|
|
1095
|
+
if (prop.childType) {
|
|
1096
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
1097
|
+
if (isUpcaseFirst) {
|
|
1098
|
+
typeArgs += `, ${prop.childType}`;
|
|
1099
|
+
}
|
|
1100
|
+
else {
|
|
1101
|
+
typeArgs += `, "${prop.childType}"`;
|
|
1102
|
+
}
|
|
1103
|
+
if (prop.type === "ref") {
|
|
1104
|
+
langType = `${prop.childType}`;
|
|
1105
|
+
initializer = `new ${prop.childType}()`;
|
|
1106
|
+
typeArgs = `${prop.childType}`;
|
|
1107
|
+
}
|
|
1108
|
+
else if (prop.type === "array") {
|
|
1109
|
+
langType = (isUpcaseFirst)
|
|
1110
|
+
? `ArraySchema<${prop.childType}>`
|
|
1111
|
+
: `ArraySchema<${typeMaps$3[prop.childType]}>`;
|
|
1112
|
+
initializer = `new ${langType}()`;
|
|
1113
|
+
typeArgs = (isUpcaseFirst)
|
|
1114
|
+
? `[ ${prop.childType} ]`
|
|
1115
|
+
: `[ "${prop.childType}" ]`;
|
|
1116
|
+
}
|
|
1117
|
+
else if (prop.type === "map") {
|
|
1118
|
+
langType = (isUpcaseFirst)
|
|
1119
|
+
? `MapSchema<${prop.childType}>`
|
|
1120
|
+
: `MapSchema<${typeMaps$3[prop.childType]}>`;
|
|
1121
|
+
initializer = `new ${langType}()`;
|
|
1122
|
+
typeArgs = (isUpcaseFirst)
|
|
1123
|
+
? `{ map: ${prop.childType} }`
|
|
1124
|
+
: `{ map: "${prop.childType}" }`;
|
|
1125
|
+
}
|
|
1126
|
+
else if (prop.type === "set") {
|
|
1127
|
+
langType = (isUpcaseFirst)
|
|
1128
|
+
? `SetSchema<${prop.childType}>`
|
|
1129
|
+
: `SetSchema<${typeMaps$3[prop.childType]}>`;
|
|
1130
|
+
initializer = `new ${langType}()`;
|
|
1131
|
+
typeArgs = (isUpcaseFirst)
|
|
1132
|
+
? `{ set: ${prop.childType} }`
|
|
1133
|
+
: `{ set: "${prop.childType}" }`;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
else {
|
|
1137
|
+
langType = typeMaps$3[prop.type];
|
|
1138
|
+
typeArgs = `"${prop.type}"`;
|
|
1139
|
+
}
|
|
1140
|
+
// TS1263: "Declarations with initializers cannot also have definite assignment assertions"
|
|
1141
|
+
const definiteAssertion = initializer ? "" : "!";
|
|
1142
|
+
return `@type(${typeArgs}) public ${prop.name}${definiteAssertion}: ${langType}${(initializer) ? ` = ${initializer}` : ""};`;
|
|
1143
|
+
}
|
|
1144
|
+
function generateInterface(structure, namespace, allClasses) {
|
|
1145
|
+
return `${getCommentHeader()}
|
|
1146
|
+
|
|
1147
|
+
export interface ${structure.name} {
|
|
1148
|
+
${structure.properties.map(prop => ` ${prop.name}: ${prop.type};`).join("\n")}
|
|
1149
|
+
}
|
|
1150
|
+
`;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
const typeMaps$2 = {
|
|
1154
|
+
"string": "string",
|
|
1155
|
+
"number": "number",
|
|
1156
|
+
"boolean": "boolean",
|
|
1157
|
+
"int8": "number",
|
|
1158
|
+
"uint8": "number",
|
|
1159
|
+
"int16": "number",
|
|
1160
|
+
"uint16": "number",
|
|
1161
|
+
"int32": "number",
|
|
1162
|
+
"uint32": "number",
|
|
1163
|
+
"int64": "number",
|
|
1164
|
+
"uint64": "number",
|
|
1165
|
+
"float32": "number",
|
|
1166
|
+
"float64": "number",
|
|
1167
|
+
};
|
|
1168
|
+
const distinct$1 = (value, index, self) => self.indexOf(value) === index;
|
|
1169
|
+
function generate$3(context, options) {
|
|
1170
|
+
return context.classes.map(klass => ({
|
|
1171
|
+
name: klass.name + ".js",
|
|
1172
|
+
content: generateClass$2(klass, options.namespace, context.classes)
|
|
1173
|
+
}));
|
|
1174
|
+
}
|
|
1175
|
+
function generateClass$2(klass, namespace, allClasses) {
|
|
1176
|
+
const allRefs = [];
|
|
1177
|
+
klass.properties.forEach(property => {
|
|
1178
|
+
let type = property.type;
|
|
1179
|
+
// keep all refs list
|
|
1180
|
+
if ((type === "ref" || type === "array" || type === "map")) {
|
|
1181
|
+
allRefs.push(property);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
return `${getCommentHeader()}
|
|
1185
|
+
|
|
1186
|
+
const schema = require("@colyseus/schema");
|
|
1187
|
+
const Schema = schema.Schema;
|
|
1188
|
+
const type = schema.type;
|
|
1189
|
+
${allRefs.
|
|
1190
|
+
filter(ref => ref.childType && typeMaps$2[ref.childType] === undefined).
|
|
1191
|
+
map(ref => ref.childType).
|
|
1192
|
+
concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
|
|
1193
|
+
filter(distinct$1).
|
|
1194
|
+
map(childType => `const ${childType} = require("./${childType}");`).
|
|
1195
|
+
join("\n")}
|
|
1196
|
+
|
|
1197
|
+
class ${klass.name} extends ${klass.extends} {
|
|
1198
|
+
constructor () {
|
|
1199
|
+
super();
|
|
1200
|
+
${klass.properties.
|
|
1201
|
+
filter(prop => prop.childType !== undefined).
|
|
1202
|
+
map(prop => " " + generatePropertyInitializer(prop)).join("\n")}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
${klass.properties.map(prop => generatePropertyDeclaration$1(klass.name, prop)).join("\n")}
|
|
1206
|
+
|
|
1207
|
+
export default ${klass.name};
|
|
1208
|
+
`;
|
|
1209
|
+
}
|
|
1210
|
+
function generatePropertyDeclaration$1(className, prop) {
|
|
1211
|
+
let typeArgs;
|
|
1212
|
+
if (prop.childType) {
|
|
1213
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
1214
|
+
if (isUpcaseFirst) {
|
|
1215
|
+
typeArgs += `, ${prop.childType}`;
|
|
1216
|
+
}
|
|
1217
|
+
else {
|
|
1218
|
+
typeArgs += `, "${prop.childType}"`;
|
|
1219
|
+
}
|
|
1220
|
+
if (prop.type === "ref") {
|
|
1221
|
+
typeArgs = `${prop.childType}`;
|
|
1222
|
+
}
|
|
1223
|
+
else if (prop.type === "array") {
|
|
1224
|
+
typeArgs = (isUpcaseFirst)
|
|
1225
|
+
? `[ ${prop.childType} ]`
|
|
1226
|
+
: `[ "${prop.childType}" ]`;
|
|
1227
|
+
}
|
|
1228
|
+
else if (prop.type === "map") {
|
|
1229
|
+
typeArgs = (isUpcaseFirst)
|
|
1230
|
+
? `{ map: ${prop.childType} }`
|
|
1231
|
+
: `{ map: "${prop.childType}" }`;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
else {
|
|
1235
|
+
typeArgs = `"${prop.type}"`;
|
|
1236
|
+
}
|
|
1237
|
+
return `type(${typeArgs})(${className}.prototype, "${prop.name}");`;
|
|
1238
|
+
}
|
|
1239
|
+
function generatePropertyInitializer(prop) {
|
|
1240
|
+
let initializer = "";
|
|
1241
|
+
if (prop.type === "ref") {
|
|
1242
|
+
initializer = `new ${prop.childType}()`;
|
|
1243
|
+
}
|
|
1244
|
+
else if (prop.type === "array") {
|
|
1245
|
+
initializer = `new schema.ArraySchema()`;
|
|
1246
|
+
}
|
|
1247
|
+
else if (prop.type === "map") {
|
|
1248
|
+
initializer = `new schema.MapSchema()`;
|
|
1249
|
+
}
|
|
1250
|
+
return `this.${prop.name} = ${initializer}`;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
const typeMaps$1 = {
|
|
1254
|
+
"string": "String",
|
|
1255
|
+
"number": "float",
|
|
1256
|
+
"boolean": "boolean",
|
|
1257
|
+
"int8": "byte",
|
|
1258
|
+
"uint8": "short",
|
|
1259
|
+
"int16": "short",
|
|
1260
|
+
"uint16": "int",
|
|
1261
|
+
"int32": "int",
|
|
1262
|
+
"uint32": "long",
|
|
1263
|
+
"int64": "long",
|
|
1264
|
+
"uint64": "long",
|
|
1265
|
+
"float32": "float",
|
|
1266
|
+
"float64": "double",
|
|
1267
|
+
};
|
|
1268
|
+
const typeInitializer = {
|
|
1269
|
+
"string": '""',
|
|
1270
|
+
"number": "0",
|
|
1271
|
+
"boolean": "false",
|
|
1272
|
+
"int8": "0",
|
|
1273
|
+
"uint8": "0",
|
|
1274
|
+
"int16": "0",
|
|
1275
|
+
"uint16": "0",
|
|
1276
|
+
"int32": "0",
|
|
1277
|
+
"uint32": "0",
|
|
1278
|
+
"int64": "0",
|
|
1279
|
+
"uint64": "0",
|
|
1280
|
+
"float32": "0",
|
|
1281
|
+
"float64": "0",
|
|
1282
|
+
};
|
|
1283
|
+
/**
|
|
1284
|
+
* C# Code Generator
|
|
1285
|
+
*/
|
|
1286
|
+
function generate$2(context, options) {
|
|
1287
|
+
return context.classes.map(klass => ({
|
|
1288
|
+
name: klass.name + ".java",
|
|
1289
|
+
content: generateClass$1(klass, options.namespace)
|
|
1290
|
+
}));
|
|
1291
|
+
}
|
|
1292
|
+
function generateClass$1(klass, namespace) {
|
|
1293
|
+
const indent = (namespace) ? "\t" : "";
|
|
1294
|
+
return `${getCommentHeader()}
|
|
1295
|
+
${namespace ? `\npackage ${namespace};` : ""}
|
|
1296
|
+
|
|
1297
|
+
import io.colyseus.serializer.schema.Schema;
|
|
1298
|
+
import io.colyseus.serializer.schema.annotations.SchemaClass;
|
|
1299
|
+
import io.colyseus.serializer.schema.annotations.SchemaField;
|
|
1300
|
+
|
|
1301
|
+
@SchemaClass
|
|
1302
|
+
${indent}public class ${klass.name} extends ${klass.extends} {
|
|
1303
|
+
${klass.properties.map(prop => generateProperty(prop, indent)).join("\n\n")}
|
|
1304
|
+
${indent}}
|
|
1305
|
+
${namespace ? "}" : ""}
|
|
1306
|
+
`;
|
|
1307
|
+
}
|
|
1308
|
+
function generateProperty(prop, indent = "") {
|
|
1309
|
+
let typeArgs = `${prop.index}/${prop.type}`;
|
|
1310
|
+
let property = "public";
|
|
1311
|
+
let langType;
|
|
1312
|
+
let ctorArgs = "";
|
|
1313
|
+
let initializer = "";
|
|
1314
|
+
if (prop.childType) {
|
|
1315
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
1316
|
+
if (prop.type !== "ref" && isUpcaseFirst) {
|
|
1317
|
+
ctorArgs = `${prop.childType}.class`;
|
|
1318
|
+
}
|
|
1319
|
+
if (prop.type === "ref") {
|
|
1320
|
+
langType = (isUpcaseFirst)
|
|
1321
|
+
? prop.childType
|
|
1322
|
+
: typeMaps$1[prop.childType];
|
|
1323
|
+
initializer = `new ${langType}${(prop.type !== "ref" && isUpcaseFirst) ? "<>" : ""}(${ctorArgs})`;
|
|
1324
|
+
}
|
|
1325
|
+
else if (prop.type === "array") {
|
|
1326
|
+
langType = (isUpcaseFirst)
|
|
1327
|
+
? `ArraySchema<${prop.childType}>`
|
|
1328
|
+
: `ArraySchema`;
|
|
1329
|
+
initializer = `new ArraySchema${(isUpcaseFirst) ? "<>" : ""}(${ctorArgs})`;
|
|
1330
|
+
}
|
|
1331
|
+
else if (prop.type === "map") {
|
|
1332
|
+
langType = (isUpcaseFirst)
|
|
1333
|
+
? `MapSchema<${prop.childType}>`
|
|
1334
|
+
: `MapSchema`;
|
|
1335
|
+
initializer = `new MapSchema${(isUpcaseFirst) ? "<>" : ""}(${ctorArgs})`;
|
|
1336
|
+
}
|
|
1337
|
+
if (prop.type !== "ref") {
|
|
1338
|
+
typeArgs += (isUpcaseFirst)
|
|
1339
|
+
? `/ref`
|
|
1340
|
+
: `/${prop.childType}`;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
else {
|
|
1344
|
+
langType = typeMaps$1[prop.type];
|
|
1345
|
+
initializer = typeInitializer[prop.type];
|
|
1346
|
+
}
|
|
1347
|
+
property += ` ${langType} ${prop.name}`;
|
|
1348
|
+
return `\t@SchemaField("${typeArgs}")\t${indent}
|
|
1349
|
+
\t${indent}${property} = ${initializer};`;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
TODO:
|
|
1354
|
+
- Support inheritance
|
|
1355
|
+
- Support importing Schema dependencies
|
|
1356
|
+
*/
|
|
1357
|
+
const typeMaps = {
|
|
1358
|
+
"string": "string",
|
|
1359
|
+
"number": "number",
|
|
1360
|
+
"boolean": "boolean",
|
|
1361
|
+
"int8": "number",
|
|
1362
|
+
"uint8": "number",
|
|
1363
|
+
"int16": "number",
|
|
1364
|
+
"uint16": "number",
|
|
1365
|
+
"int32": "number",
|
|
1366
|
+
"uint32": "number",
|
|
1367
|
+
"int64": "number",
|
|
1368
|
+
"uint64": "number",
|
|
1369
|
+
"float32": "number",
|
|
1370
|
+
"float64": "number",
|
|
1371
|
+
};
|
|
1372
|
+
const distinct = (value, index, self) => self.indexOf(value) === index;
|
|
1373
|
+
function generate$1(context, options) {
|
|
1374
|
+
return context.classes.map(klass => ({
|
|
1375
|
+
name: klass.name + ".lua",
|
|
1376
|
+
content: generateClass(klass, options.namespace, context.classes)
|
|
1377
|
+
}));
|
|
1378
|
+
}
|
|
1379
|
+
function generateClass(klass, namespace, allClasses) {
|
|
1380
|
+
const allRefs = [];
|
|
1381
|
+
klass.properties.forEach(property => {
|
|
1382
|
+
let type = property.type;
|
|
1383
|
+
// keep all refs list
|
|
1384
|
+
if ((type === "ref" || type === "array" || type === "map")) {
|
|
1385
|
+
allRefs.push(property);
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
// Inheritance support
|
|
1389
|
+
const inherits = (klass.extends !== "Schema")
|
|
1390
|
+
? `, ${klass.extends}`
|
|
1391
|
+
: "";
|
|
1392
|
+
return `${getCommentHeader().replace(/\/\//mg, "--")}
|
|
1393
|
+
|
|
1394
|
+
local schema = require 'colyseus.serializer.schema.schema'
|
|
1395
|
+
${allRefs.
|
|
1396
|
+
filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
|
|
1397
|
+
map(ref => ref.childType).
|
|
1398
|
+
concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
|
|
1399
|
+
filter(distinct).
|
|
1400
|
+
map(childType => `local ${childType} = require '${(namespace ? `${namespace}.` : '')}${childType}'`).
|
|
1401
|
+
join("\n")}
|
|
1402
|
+
|
|
1403
|
+
---@class ${klass.name}: ${klass.extends}
|
|
1404
|
+
${klass.properties.map(prop => `---@field ${prop.name} ${getLUATypeAnnotation(prop)}`).join("\n")}
|
|
1405
|
+
local ${klass.name} = schema.define({
|
|
1406
|
+
${klass.properties.map(prop => generatePropertyDeclaration(prop)).join(",\n")},
|
|
1407
|
+
["_fields_by_index"] = { ${klass.properties.map(prop => `"${prop.name}"`).join(", ")} },
|
|
1408
|
+
}${inherits})
|
|
1409
|
+
|
|
1410
|
+
return ${klass.name}
|
|
1411
|
+
`;
|
|
1412
|
+
}
|
|
1413
|
+
function generatePropertyDeclaration(prop) {
|
|
1414
|
+
let typeArgs;
|
|
1415
|
+
if (prop.childType) {
|
|
1416
|
+
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
|
|
1417
|
+
if (isUpcaseFirst) {
|
|
1418
|
+
typeArgs += `${prop.childType}`;
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
typeArgs += `"${prop.childType}"`;
|
|
1422
|
+
}
|
|
1423
|
+
if (prop.type === "ref") {
|
|
1424
|
+
typeArgs = (isUpcaseFirst)
|
|
1425
|
+
? `${prop.childType}`
|
|
1426
|
+
: `"${prop.childType}"`;
|
|
1427
|
+
}
|
|
1428
|
+
else {
|
|
1429
|
+
typeArgs = (isUpcaseFirst)
|
|
1430
|
+
? `{ ${prop.type} = ${prop.childType} }`
|
|
1431
|
+
: `{ ${prop.type} = "${prop.childType}" }`;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
else {
|
|
1435
|
+
typeArgs = `"${prop.type}"`;
|
|
1436
|
+
}
|
|
1437
|
+
return ` ["${prop.name}"] = ${typeArgs}`;
|
|
1438
|
+
}
|
|
1439
|
+
function getLUATypeAnnotation(prop) {
|
|
1440
|
+
if (prop.type === "ref") {
|
|
1441
|
+
return prop.childType;
|
|
1442
|
+
}
|
|
1443
|
+
else if (prop.type === "array") {
|
|
1444
|
+
return "ArraySchema";
|
|
1445
|
+
}
|
|
1446
|
+
else if (prop.type === "map") {
|
|
1447
|
+
return "MapSchema";
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1450
|
+
return typeMaps[prop.type];
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
const generators = { csharp: generate$7, cpp: generate$6, haxe: generate$5, ts: generate$4, js: generate$3, java: generate$2, lua: generate$1, };
|
|
1455
|
+
function generate(targetId, options) {
|
|
1456
|
+
const generator = generators[targetId];
|
|
1457
|
+
if (!generator) {
|
|
1458
|
+
throw new Error("You must provide a valid generator as argument, such as: --csharp, --haxe or --cpp");
|
|
1459
|
+
}
|
|
1460
|
+
if (!fs__namespace.existsSync(options.output)) {
|
|
1461
|
+
console.log("Creating", options.output, "directory");
|
|
1462
|
+
fs__namespace.mkdirSync(options.output);
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Default `@type()` decorator name
|
|
1466
|
+
*/
|
|
1467
|
+
if (!options.decorator) {
|
|
1468
|
+
options.decorator = "type";
|
|
1469
|
+
}
|
|
1470
|
+
// resolve wildcard files
|
|
1471
|
+
options.files = options.files.reduce((acc, cur) => {
|
|
1472
|
+
if (cur.endsWith("*")) {
|
|
1473
|
+
acc.push(...recursiveFiles(cur.slice(0, -1)).filter(filename => /\.(js|ts|mjs)$/.test(filename)));
|
|
1474
|
+
}
|
|
1475
|
+
else {
|
|
1476
|
+
acc.push(cur);
|
|
1477
|
+
}
|
|
1478
|
+
return acc;
|
|
1479
|
+
}, []);
|
|
1480
|
+
const structures = parseFiles(options.files, options.decorator);
|
|
1481
|
+
// Post-process classes before generating
|
|
1482
|
+
structures.classes.forEach(klass => klass.postProcessing());
|
|
1483
|
+
const files = generator(structures, options);
|
|
1484
|
+
files.forEach((file) => {
|
|
1485
|
+
const outputPath = path__namespace.resolve(options.output, file.name);
|
|
1486
|
+
fs__namespace.writeFileSync(outputPath, file.content);
|
|
1487
|
+
console.log("generated:", file.name);
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
function recursiveFiles(dir) {
|
|
1491
|
+
const files = fs__namespace.readdirSync(dir, { withFileTypes: true });
|
|
1492
|
+
let collect = [];
|
|
1493
|
+
files.forEach(file => {
|
|
1494
|
+
const filename = path__namespace.resolve(dir, file.name);
|
|
1495
|
+
file.isDirectory() ? collect.push(...recursiveFiles(filename)) : collect.push(filename);
|
|
1496
|
+
});
|
|
1497
|
+
return collect;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
const supportedTargets = {
|
|
1501
|
+
csharp: 'generate for C#/Unity',
|
|
1502
|
+
cpp: 'generate for C++',
|
|
1503
|
+
haxe: 'generate for Haxe',
|
|
1504
|
+
ts: 'generate for TypeScript',
|
|
1505
|
+
js: 'generate for JavaScript',
|
|
1506
|
+
java: 'generate for Java',
|
|
1507
|
+
lua: 'generate for LUA',
|
|
1508
|
+
};
|
|
1509
|
+
function displayHelp() {
|
|
1510
|
+
console.log(`\nschema-codegen [path/to/Schema.ts]
|
|
1511
|
+
|
|
1512
|
+
Usage (C#/Unity)
|
|
1513
|
+
schema-codegen src/Schema.ts --output client-side/ --csharp --namespace MyGame.Schema
|
|
1514
|
+
|
|
1515
|
+
Valid options:
|
|
1516
|
+
--output: the output directory for generated client-side schema files
|
|
1517
|
+
${Object.
|
|
1518
|
+
keys(supportedTargets).
|
|
1519
|
+
map((targetId) => (` --${targetId}: ${supportedTargets[targetId]}`)).
|
|
1520
|
+
join("\n")}
|
|
1521
|
+
|
|
1522
|
+
Optional:
|
|
1523
|
+
--namespace: generate namespace on output code
|
|
1524
|
+
--decorator: custom name for @type decorator to scan for`);
|
|
1525
|
+
process.exit();
|
|
1526
|
+
}
|
|
1527
|
+
const args = argv(process.argv.slice(2));
|
|
1528
|
+
if (args.help) {
|
|
1529
|
+
displayHelp();
|
|
1530
|
+
}
|
|
1531
|
+
let targetId;
|
|
1532
|
+
for (let target in supportedTargets) {
|
|
1533
|
+
if (args[target]) {
|
|
1534
|
+
targetId = target;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (!args.output) {
|
|
1538
|
+
console.error("You must provide a valid --output directory.");
|
|
1539
|
+
displayHelp();
|
|
1540
|
+
}
|
|
1541
|
+
try {
|
|
1542
|
+
args.files = args._;
|
|
1543
|
+
generate(targetId, {
|
|
1544
|
+
files: args._,
|
|
1545
|
+
decorator: args.decorator,
|
|
1546
|
+
output: args.output,
|
|
1547
|
+
namespace: args.namespace
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
catch (e) {
|
|
1551
|
+
console.error(e.message);
|
|
1552
|
+
console.error(e.stack);
|
|
1553
|
+
displayHelp();
|
|
1554
|
+
}
|
|
1555
|
+
//# sourceMappingURL=cli.cjs.map
|