@needle-tools/needle-component-compiler 1.11.2 → 1.12.0-da2e0be

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.
@@ -1,784 +1,788 @@
1
- "use strict";
2
- exports.__esModule = true;
3
- exports.run = exports.compile = void 0;
4
- var fs_1 = require("fs");
5
- var ts = require("typescript");
6
- var fs = require("fs");
7
- var path = require("path");
8
- var types = require("./types");
9
- var dict = types.dict;
10
- // add either of these two comments above a class to enforce code gen or disable it for the next class
11
- var exportNextClassCommand = "@generate-component";
12
- var dontExportNextClassCommand = "@dont-generate-component";
13
- // add above field to add [SerializeField] attribute
14
- var serializeCommand = "@serializeField";
15
- var dontSerializeCommand = "@nonSerialized";
16
- // https://regex101.com/r/ltpcKT/2
17
- var typePattern = new RegExp("@type ?(?<type>.+)");
18
- var ifdefPattern = new RegExp("@ifdef ?(?<ifdef>.+)");
19
- var CODEGEN_MARKER_START = "// NEEDLE_CODEGEN_START";
20
- var CODEGEN_MARKER_END = "// NEEDLE_CODEGEN_END";
21
- var allowDebugLogs = true;
22
- // will be set to true when e.g. a comment for export is found
23
- var exportNextClass = false;
24
- var dontExportNextClass = false;
25
- var serializeField = false;
26
- var dontSerialize = false;
27
- var typesFileContent = undefined;
28
- // const exportDir = "../dist";
29
- var commentStarts = [];
30
- var contexts = [];
31
- var lastTypeFound = null;
32
- var ifdefSections = [];
33
- function resetAllState() {
34
- exportNextClass = false;
35
- dontExportNextClass = false;
36
- serializeField = false;
37
- dontSerialize = false;
38
- typesFileContent = undefined;
39
- commentStarts.length = 0;
40
- contexts.length = 0;
41
- lastTypeFound = null;
42
- ifdefSections.length = 0;
43
- }
44
- function resetExportNextClass() {
45
- dontExportNextClass = false;
46
- exportNextClass = false;
47
- }
48
- function tryGetKnownType(str) {
49
- if (typesFileContent === undefined) {
50
- typesFileContent = null;
51
- var filePath = path.dirname(__dirname) + "/src/types.json";
52
- if (fs.existsSync(filePath)) {
53
- if (allowDebugLogs)
54
- console.log("Reading types file");
55
- var content = fs.readFileSync(filePath, "utf8");
56
- typesFileContent = JSON.parse(content);
57
- }
58
- }
59
- if (typesFileContent) {
60
- var fullType = typesFileContent[str];
61
- if (fullType && allowDebugLogs)
62
- console.log(fullType);
63
- return fullType;
64
- }
65
- return null;
66
- }
67
- // https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
68
- var ExportContext = /** @class */ (function () {
69
- function ExportContext(outputDir, fileName) {
70
- this.classEnd = -1;
71
- this.indentLevel = 0;
72
- this.emitMethodContextMenu = null;
73
- this.emitTooltip = null;
74
- this.outputDir = outputDir;
75
- this.fileName = fileName;
76
- this.reset();
77
- }
78
- ExportContext.prototype.onBeforeField = function (name) {
79
- if (this.emitTooltip) {
80
- this.appendLine("[UnityEngine.Tooltip(\"" + this.emitTooltip.trim().replace("\"", "'") + "\")]");
81
- this.emitTooltip = undefined;
82
- }
83
- };
84
- ExportContext.prototype.onBeforeMethod = function (name) {
85
- if (this.emitMethodContextMenu !== undefined) {
86
- var contextMenuText = this.emitMethodContextMenu === null ? name : this.emitMethodContextMenu;
87
- this.appendLine("[UnityEngine.ContextMenu(\"" + contextMenuText + "\")]");
88
- this.emitMethodContextMenu = undefined;
89
- }
90
- };
91
- ExportContext.prototype.append = function (text) {
92
- for (var i = 0; i < this.indentLevel; i++)
93
- text = "\t" + text;
94
- this.textBuffer += text;
95
- this.emitMethodContextMenu = undefined;
96
- };
97
- ExportContext.prototype.appendLine = function (text) {
98
- this.append(text + "\n");
99
- };
100
- ExportContext.prototype.flush = function () {
101
- if (this.textBuffer.length <= 0) {
102
- return;
103
- }
104
- this.textBuffer = CODEGEN_MARKER_START + "\n" + this.textBuffer + "\n" + CODEGEN_MARKER_END;
105
- var code = this.textBuffer;
106
- if (this.outputDir !== null) {
107
- if (!fs.existsSync(this.outputDir))
108
- fs.mkdirSync(this.outputDir);
109
- var dir = this.outputDir + "/";
110
- var path_1 = dir + this.fileName;
111
- code = this.replaceGeneratedCodeSection(path_1, code);
112
- if (allowDebugLogs)
113
- console.log("Write to " + path_1);
114
- fs.writeFileSync(path_1, code);
115
- }
116
- else {
117
- console.log("No output dir specified");
118
- }
119
- this.reset();
120
- return code;
121
- };
122
- ExportContext.prototype.reset = function () {
123
- this.textBuffer = "";
124
- this.classEnd = -1;
125
- };
126
- ExportContext.prototype.replaceGeneratedCodeSection = function (path, code) {
127
- if (fs.existsSync(path)) {
128
- var existing = fs.readFileSync(path, "utf8");
129
- var regex = new RegExp("(?<before>.*?)\/\/ ?NEEDLE_CODEGEN_START.+\/\/ ?NEEDLE_CODEGEN_END(?<after>.*)", "s");
130
- var matches = regex.exec(existing);
131
- if (matches === null || matches === void 0 ? void 0 : matches.groups) {
132
- if (allowDebugLogs)
133
- console.log("Found codegen sections");
134
- var before_1 = matches.groups.before;
135
- var after_1 = matches.groups.after;
136
- return before_1 + code + after_1;
137
- }
138
- }
139
- return code;
140
- };
141
- return ExportContext;
142
- }());
143
- function compile(code, fileName, outputDir, debugLogs) {
144
- if (debugLogs === void 0) { debugLogs = true; }
145
- resetAllState();
146
- allowDebugLogs = debugLogs;
147
- // Parse a file
148
- var sourceFile = ts.createSourceFile(fileName, code, ts.ScriptTarget.ES2015, true);
149
- var prog = ts.createProgram([fileName], {});
150
- // delint it
151
- return run(prog, outputDir, sourceFile);
152
- }
153
- exports.compile = compile;
154
- function run(program, outputDir, sourceFile) {
155
- if (outputDir !== null && !fs.existsSync(outputDir)) {
156
- console.error("Output directory does not exist: \"" + outputDir + "\"");
157
- return;
158
- }
159
- var results = [];
160
- traverseFile(sourceFile);
161
- function traverseFile(node) {
162
- visit(node);
163
- ts.forEachChild(node, traverseFile);
164
- }
165
- return results;
166
- function visit(node) {
167
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
168
- var context = contexts.length > 0 ? contexts[contexts.length - 1] : null;
169
- if (context) {
170
- if ((context === null || context === void 0 ? void 0 : context.classEnd) > 0 && node.pos >= (context === null || context === void 0 ? void 0 : context.classEnd)) {
171
- while (context.indentLevel > 0) {
172
- context.indentLevel -= 1;
173
- context.append("}\n");
174
- }
175
- var code = context.flush();
176
- results.push(code);
177
- context = null;
178
- contexts.pop();
179
- }
180
- }
181
- if (allowDebugLogs)
182
- console.log("\t", ts.SyntaxKind[node.kind]);
183
- var commentRanges = ts.getLeadingCommentRanges(sourceFile.getFullText(), node.getFullStart());
184
- if (commentRanges === null || commentRanges === void 0 ? void 0 : commentRanges.length) {
185
- for (var _i = 0, commentRanges_1 = commentRanges; _i < commentRanges_1.length; _i++) {
186
- var r = commentRanges_1[_i];
187
- // avoid emitting comments multiple times
188
- if (commentStarts.includes(r.pos))
189
- continue;
190
- commentStarts.push(r.pos);
191
- var comment = node.getSourceFile().getFullText().slice(r.pos, r.end);
192
- console.log(comment);
193
- if (context) {
194
- // https://regex101.com/r/ud4oev/1
195
- var emitContextMenu = comment.match("(\/{2,}|\/\*) {0,}@contextmenu {1,}(?<text>[a-zA-Z 0-9]+)?");
196
- if (emitContextMenu) {
197
- context.emitMethodContextMenu = (_b = (_a = emitContextMenu.groups) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : null;
198
- }
199
- // https://regex101.com/r/Sa6Q8T/3
200
- var emitTooltip = comment.match("\/{2,} {0,}(@tooltip) *?(?<text>.+)");
201
- if (emitTooltip) {
202
- console.log((_c = emitTooltip.groups) === null || _c === void 0 ? void 0 : _c.text);
203
- context.emitTooltip = (_e = (_d = emitTooltip.groups) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : null;
204
- }
205
- else if (comment.includes(serializeCommand))
206
- serializeField = true;
207
- else if (comment.includes(dontSerializeCommand))
208
- dontSerialize = true;
209
- }
210
- if (comment.includes(exportNextClassCommand))
211
- exportNextClass = true;
212
- if (comment.includes(dontExportNextClassCommand))
213
- dontExportNextClass = true;
214
- var typeMatch = typePattern.exec(comment);
215
- if (typeMatch && typeMatch.groups) {
216
- // for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
217
- // so we remove them
218
- var type = typeMatch.groups["type"];
219
- type = type.replace(/\(/, "").replace(/\)/, "");
220
- if (allowDebugLogs)
221
- console.log("Found type: ", type);
222
- lastTypeFound = type;
223
- }
224
- var ifdefMatch = ifdefPattern.exec(comment);
225
- if (ifdefMatch && ifdefMatch.groups) {
226
- var ifdef = ifdefMatch.groups["ifdef"];
227
- if (ifdef)
228
- ifdefSections.push(ifdef);
229
- }
230
- }
231
- }
232
- var skip = dontSerialize;
233
- switch (node.kind) {
234
- // Namespace
235
- // case ts.SyntaxKind.ModuleDeclaration:
236
- // const mod = node as ts.ModuleDeclaration;
237
- // console.log(ts.SyntaxKind[mod.getChildAt(1).kind])
238
- // const type = mod.getChildAt(1) as ts.Identifier;
239
- // console.log("MODULE", type.text)
240
- // break;
241
- // case ts.SyntaxKind.Identifier:
242
- // break;
243
- // case ts.SyntaxKind.ClassDeclaration:
244
- // case ts.SyntaxKind.SingleLineCommentTrivia:
245
- // console.log("comment: " + node.getText())
246
- // break;
247
- case ts.SyntaxKind.Decorator:
248
- break;
249
- case ts.SyntaxKind.MethodDeclaration:
250
- lastTypeFound = null;
251
- serializeField = false;
252
- dontSerialize = false;
253
- resetExportNextClass();
254
- if (!context)
255
- break;
256
- // TODO: always emit at least OnEnable method per class so generated components have toggle in editor
257
- var meth = node;
258
- // const isCoroutine = func.asteriskToken;
259
- if (!skip && meth.name) {
260
- var pub_1 = shouldEmitMethod(meth);
261
- if (!pub_1)
262
- return;
263
- var paramsStr = "";
264
- for (var _k = 0, _l = meth.parameters; _k < _l.length; _k++) {
265
- var param = _l[_k];
266
- if (!param || !param.name)
267
- continue;
268
- if (paramsStr.length > 0)
269
- paramsStr += ", ";
270
- var type = tryResolveTypeRecursive(param);
271
- if (type === undefined)
272
- type = "object";
273
- var paramName = "";
274
- var paramNameKind = param.name.kind;
275
- switch (paramNameKind) {
276
- case ts.SyntaxKind.ObjectBindingPattern:
277
- paramName = "obj";
278
- break;
279
- default:
280
- paramName = param.name.getText();
281
- break;
282
- }
283
- paramsStr += type + " @" + paramName;
284
- }
285
- var methodName = meth.name.getText();
286
- switch (methodName) {
287
- case "onEnable":
288
- methodName = "OnEnable";
289
- break;
290
- case "onDisable":
291
- methodName = "OnDisable";
292
- break;
293
- }
294
- context.onBeforeMethod(methodName);
295
- // let visibility = pub ? "public" : "private";
296
- context.append("public void " + methodName + "(" + paramsStr + "){}\n");
297
- }
298
- break;
299
- case ts.SyntaxKind.SetAccessor:
300
- case ts.SyntaxKind.PropertyDeclaration:
301
- resetExportNextClass();
302
- if (!context)
303
- break;
304
- if (allowDebugLogs)
305
- console.log("Found variable", ts.SyntaxKind[node.kind], "\n", node.getText());
306
- var vardec = node;
307
- var varName = "@" + vardec.name.getText();
308
- var pub = shouldEmitMethod(vardec);
309
- var visibility = pub ? "public" : "private";
310
- var isAccessible = pub;
311
- dontSerialize = false;
312
- if (serializeField) {
313
- if (allowDebugLogs)
314
- console.log("[SerializeField]");
315
- context.appendLine("[UnityEngine.SerializeField]");
316
- isAccessible = true;
317
- }
318
- else if (skip)
319
- isAccessible = false;
320
- if (!isAccessible) {
321
- if (allowDebugLogs)
322
- console.log("Skip because not public or serializeable");
323
- break;
324
- }
325
- var name_1 = vardec.name.getText();
326
- if (allowDebugLogs)
327
- console.log("Variable:", name_1);
328
- if (name_1.startsWith("\"@") || name_1.startsWith("\"$") || name_1.startsWith("$"))
329
- break;
330
- var typeString = lastTypeFound !== null && lastTypeFound !== void 0 ? lastTypeFound : tryResolveTypeRecursive(node);
331
- var postFix = "";
332
- var typeName = (_f = vardec.type) === null || _f === void 0 ? void 0 : _f.getText();
333
- var shouldCommentTheLine = typeString === undefined;
334
- if (typeString === undefined) {
335
- postFix = " → Could not resolve C# type";
336
- }
337
- var assignment = "";
338
- if (typeString !== undefined) {
339
- for (var _m = 0, _o = node.getChildren(); _m < _o.length; _m++) {
340
- var ch = _o[_m];
341
- switch (ch.kind) {
342
- default:
343
- // console.log("Unknown assignment:", ts.SyntaxKind[ch.kind]);
344
- break;
345
- case ts.SyntaxKind.NewExpression:
346
- assignment = " = " + getTypeForAssignment(ch, typeString);
347
- break;
348
- case ts.SyntaxKind.FalseKeyword:
349
- case ts.SyntaxKind.TrueKeyword:
350
- assignment = " = " + ch.getText();
351
- break;
352
- case ts.SyntaxKind.StringLiteral:
353
- var str = ch;
354
- assignment = " = \"" + str.text + "\"";
355
- break;
356
- case ts.SyntaxKind.FirstLiteralToken:
357
- var lit = ch;
358
- assignment = " = " + lit.text;
359
- if (ts.isNumericLiteral(lit))
360
- assignment += "f";
361
- break;
362
- case ts.SyntaxKind.ArrayLiteralExpression:
363
- var arr = ch;
364
- assignment = " = new " + typeString;
365
- // if (arr.elements.length > 0) {
366
- assignment += "{" + arr.elements.map(function (e) { return " " + getTypeForAssignment(e); }) + " }";
367
- // }
368
- break;
369
- }
370
- }
371
- }
372
- var requireEndIf = false;
373
- if (ifdefSections.length > 0) {
374
- requireEndIf = true;
375
- context.appendLine("#ifdef " + ifdefSections.pop());
376
- }
377
- if (typeString === undefined)
378
- typeString = typeName;
379
- if (typeString === "[]") {
380
- if (allowDebugLogs)
381
- console.log("Unknown array type for \"" + varName + " " + typeName + "\" - your type is probably not known (did you just create it this session?) and you might need to regenerate the Typemap in Unity. Go to \"Needle Engine/Internal/Generate Type Map for component compiler");
382
- // typeString = "object[]";
383
- // assignment = " = new object[0]";
384
- shouldCommentTheLine = true;
385
- }
386
- context.onBeforeField(varName);
387
- if (allowDebugLogs)
388
- console.log("EMIT member: \"" + typeString + "\" " + varName, assignment, "Last type found:", lastTypeFound);
389
- var prefix = shouldCommentTheLine ? "// " : "";
390
- var isUnityEditorType = typeString === null || typeString === void 0 ? void 0 : typeString.includes("UnityEditor");
391
- if (isUnityEditorType) {
392
- context.appendLine("#if UNITY_EDITOR");
393
- }
394
- context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";" + postFix + "\n");
395
- if (isUnityEditorType) {
396
- context.appendLine("#endif");
397
- }
398
- lastTypeFound = null;
399
- if (requireEndIf) {
400
- context.appendLine("#endif");
401
- }
402
- break;
403
- case ts.SyntaxKind.ClassDeclaration:
404
- serializeField = false;
405
- var dec = node;
406
- // a class must inherit a component
407
- var inheritsComponent = testInheritsComponent(node);
408
- if (!dontExportNextClass && (lastTypeFound || exportNextClass || inheritsComponent)) {
409
- resetExportNextClass();
410
- var name_2 = (_g = dec.name) === null || _g === void 0 ? void 0 : _g.escapedText;
411
- if (allowDebugLogs)
412
- console.log("Found class: ", name_2);
413
- var namespace = (_h = tryParseNamespace(node)) !== null && _h !== void 0 ? _h : "Needle.Typescript.GeneratedComponents";
414
- if (allowDebugLogs)
415
- console.log("NAMESPACE", namespace);
416
- var newContext = new ExportContext(outputDir, name_2 + ".cs");
417
- newContext.appendLine("// auto generated code - do not edit directly");
418
- newContext.appendLine("");
419
- newContext.appendLine("#pragma warning disable");
420
- newContext.appendLine("");
421
- newContext.appendLine("namespace " + namespace);
422
- newContext.appendLine("{");
423
- newContext.indentLevel += 1;
424
- // newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
425
- var typeName_1 = "UnityEngine.MonoBehaviour";
426
- if (typeof inheritsComponent === "string")
427
- typeName_1 = inheritsComponent;
428
- if (lastTypeFound)
429
- typeName_1 = lastTypeFound;
430
- if (allowDebugLogs)
431
- console.log(name_2 + " inherits " + typeName_1);
432
- var modifiers = "";
433
- if (dec.modifiers) {
434
- for (var _p = 0, _q = dec.modifiers; _p < _q.length; _p++) {
435
- var mod = _q[_p];
436
- switch (mod.getText()) {
437
- case "abstract":
438
- modifiers += " abstract";
439
- if (allowDebugLogs)
440
- console.log(name_2 + " is abstract");
441
- break;
442
- }
443
- }
444
- }
445
- modifiers += " partial";
446
- newContext.appendLine("public " + modifiers.trim() + " class " + name_2 + " : " + typeName_1);
447
- newContext.appendLine("{");
448
- newContext.indentLevel += 1;
449
- newContext.classEnd = dec.end;
450
- contexts.push(newContext);
451
- }
452
- else {
453
- if (allowDebugLogs)
454
- console.log("Class type is unknown and will not generate a component: ", (_j = dec.name) === null || _j === void 0 ? void 0 : _j.escapedText);
455
- }
456
- lastTypeFound = null;
457
- break;
458
- }
459
- function testInheritsComponent(node) {
460
- switch (node.kind) {
461
- case ts.SyntaxKind.ClassDeclaration:
462
- var dec = node;
463
- if (dec.heritageClauses) {
464
- for (var _i = 0, _a = dec.heritageClauses; _i < _a.length; _i++) {
465
- var h = _a[_i];
466
- if (h.types.length <= 0)
467
- continue;
468
- for (var _b = 0, _c = h.types; _b < _c.length; _b++) {
469
- var type = _c[_b];
470
- // const symbol = program.getTypeChecker().getSymbolAtLocation(type.expression);
471
- // console.log(symbol);
472
- var text = type.expression.getText();
473
- if (text === "Component")
474
- return true;
475
- if (text === "Behaviour")
476
- return true;
477
- var known = tryGetKnownType(text);
478
- if (known)
479
- return known;
480
- }
481
- }
482
- }
483
- return false;
484
- }
485
- return false;
486
- }
487
- function getTypeForAssignment(node, typeString) {
488
- // console.log("-------------------\nAssign", ts.SyntaxKind[node.kind]);
489
- switch (node.kind) {
490
- case ts.SyntaxKind.FirstLiteralToken:
491
- return node.getText();
492
- case ts.SyntaxKind.NewExpression:
493
- var type = undefined;
494
- var args = undefined;
495
- for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
496
- var ch = _a[_i];
497
- var text = ch.getText();
498
- console.log("child", ts.SyntaxKind[ch.kind], text);
499
- switch (ch.kind) {
500
- case ts.SyntaxKind.Identifier:
501
- case ts.SyntaxKind.PropertyAccessExpression:
502
- type = tryGetTypeFromText(text);
503
- break;
504
- case ts.SyntaxKind.SyntaxList:
505
- for (var _b = 0, _c = ch.getChildren(); _b < _c.length; _b++) {
506
- var arg = _c[_b];
507
- if (args === undefined)
508
- args = "";
509
- var res = getTypeForAssignment(arg, typeString);
510
- // handle floats being assigned with "f" suffix
511
- if (Number.parseFloat(res) >= 0) {
512
- args += res + "f";
513
- }
514
- else {
515
- args += res;
516
- if (res === ",")
517
- args += " ";
518
- }
519
- }
520
- break;
521
- }
522
- }
523
- if (!args)
524
- args = "";
525
- if (type || typeString) {
526
- if (typeString) {
527
- console.log("Override type", type, typeString);
528
- type = typeString;
529
- }
530
- return "new " + type + "(" + args + ")";
531
- }
532
- // const expType = node.getChildren().find(c => c.kind === ts.SyntaxKind.Identifier);
533
- break;
534
- }
535
- var str = node.getText();
536
- if (allowDebugLogs)
537
- console.log("Unknown assignment:", str, ts.SyntaxKind[node.kind]);
538
- return str;
539
- }
540
- function shouldEmitMethod(node) {
541
- if (node.kind === ts.SyntaxKind.PublicKeyword) {
542
- return true;
543
- }
544
- else if (node.kind === ts.SyntaxKind.PrivateKeyword || node.kind === ts.SyntaxKind.ProtectedKeyword) {
545
- return false;
546
- }
547
- // check if its static
548
- else if (node.kind === ts.SyntaxKind.StaticKeyword) {
549
- return false;
550
- }
551
- // check if its abstract
552
- else if (node.kind === ts.SyntaxKind.AbstractKeyword) {
553
- return false;
554
- }
555
- for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
556
- var ch = _a[_i];
557
- if (!shouldEmitMethod(ch))
558
- return false;
559
- }
560
- return true;
561
- }
562
- function tryParseNamespace(node, namespace) {
563
- // console.log("TRAVERSE - " + ts.SyntaxKind[node.kind]);
564
- switch (node.kind) {
565
- case ts.SyntaxKind.ModuleDeclaration:
566
- for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
567
- var ch = _a[_i];
568
- // console.log("-- TRAVERSE - " + ts.SyntaxKind[ch.kind]);
569
- switch (ch.kind) {
570
- case ts.SyntaxKind.Identifier:
571
- var id = ch;
572
- if (id.text) {
573
- if (!namespace)
574
- namespace = "";
575
- namespace = id.text + (namespace ? "." : "") + namespace;
576
- }
577
- break;
578
- }
579
- }
580
- break;
581
- }
582
- if (node.parent) {
583
- return tryParseNamespace(node.parent, namespace);
584
- }
585
- return namespace;
586
- }
587
- function tryGetTypeFromText(typeName) {
588
- // if a type is imported via some namespace e.g. THREE.AnimationClip
589
- // we want to remove that namespace / import name
590
- var separatorIndex = typeName.lastIndexOf(".");
591
- if (separatorIndex > 0) {
592
- var newName = typeName.substring(separatorIndex + 1);
593
- if (allowDebugLogs)
594
- console.log("Remove import name from type: \"" + typeName + "\" → \"" + newName + "\"");
595
- typeName = newName;
596
- }
597
- var res = dict[typeName];
598
- if (res === undefined) {
599
- switch (typeName) {
600
- case "Array":
601
- break;
602
- default:
603
- var knownType = tryGetKnownType(typeName);
604
- res = knownType !== null && knownType !== void 0 ? knownType : undefined;
605
- break;
606
- }
607
- }
608
- // console.log(typeName, res);
609
- return res;
610
- }
611
- function tryResolveTypeRecursive(node) {
612
- if (!node)
613
- return undefined;
614
- // skip decorators (e.g. @serializable() may break array generation)
615
- if (node.kind === ts.SyntaxKind.Decorator)
616
- return undefined;
617
- var typeName = node === null || node === void 0 ? void 0 : node.getText();
618
- var varDec = node;
619
- if (varDec.type) {
620
- typeName = varDec.type.getText();
621
- }
622
- var res = undefined;
623
- // if it's a { x: number } inline object type:
624
- if (typeName.startsWith("{")) {
625
- // check if it has XYZW we assume it's a vector
626
- var hasX = typeName.includes("x");
627
- var hasY = typeName.includes("y");
628
- var hasZ = typeName.includes("z");
629
- var hasW = typeName.includes("w");
630
- if (hasX && hasY && hasZ && hasW) {
631
- return tryGetTypeFromText("Vector4");
632
- }
633
- else if (hasX && hasY && hasZ) {
634
- return tryGetTypeFromText("Vector3");
635
- }
636
- else if (hasX && hasY) {
637
- return tryGetTypeFromText("Vector2");
638
- }
639
- return "object";
640
- }
641
- // const kindName = ts.SyntaxKind[node.kind];
642
- // console.log("Unknown type: " + typeName);
643
- switch (node.kind) {
644
- case ts.SyntaxKind.ObjectBindingPattern:
645
- res = "object";
646
- break;
647
- // case ts.SyntaxKind.SyntaxList:
648
- // const list = node as ts.SyntaxList;
649
- // for (const ch of list._children) {
650
- // res = tryResolveTypeRecursive(ch);
651
- // }
652
- // break;
653
- case ts.SyntaxKind.UnionType:
654
- var union = node;
655
- for (var _i = 0, _a = union.types; _i < _a.length; _i++) {
656
- var t = _a[_i];
657
- res = tryResolveTypeRecursive(t);
658
- if (res !== undefined)
659
- return res;
660
- }
661
- break;
662
- case ts.SyntaxKind.ArrayType:
663
- res = "[]";
664
- break;
665
- case ts.SyntaxKind.TypeReference:
666
- var typeRef = node;
667
- var typeName_2 = typeRef.typeName.getText();
668
- if (allowDebugLogs)
669
- console.log("TypeReference:", typeName_2);
670
- switch (typeName_2) {
671
- case "Array":
672
- break;
673
- default:
674
- return tryGetTypeFromText(typeName_2);
675
- }
676
- // return res;
677
- break;
678
- case ts.SyntaxKind.BooleanKeyword:
679
- case ts.SyntaxKind.NumberKeyword:
680
- case ts.SyntaxKind.StringKeyword:
681
- case ts.SyntaxKind.ObjectKeyword:
682
- var keyword = node.getText();
683
- // the basic keywords are declared in the static dictionary
684
- // no need for a complex lookup
685
- res = dict[keyword];
686
- break;
687
- case ts.SyntaxKind.Identifier:
688
- var id = node;
689
- switch (id.text) {
690
- // if we have an array we dont want to use the System.Array as a type but just make it to the array syntax
691
- case "Array":
692
- res = "[]";
693
- break;
694
- default:
695
- // console.log(id.text);
696
- // res = tryGetTypeFromText(id.text);
697
- break;
698
- }
699
- break;
700
- }
701
- var isInGenericDeclaration = false;
702
- for (var _b = 0, _c = node.getChildren(); _b < _c.length; _b++) {
703
- var child = _c[_b];
704
- // Fix https://linear.app/needle/issue/NE-4423
705
- if (child.kind === ts.SyntaxKind.Block) {
706
- if (allowDebugLogs)
707
- console.log("Skip block");
708
- continue;
709
- }
710
- if (allowDebugLogs)
711
- console.log("Child type: " + ts.SyntaxKind[child.kind]);
712
- var isGenericStart = false;
713
- var isAssignment = false;
714
- switch (child.kind) {
715
- case ts.SyntaxKind.FirstAssignment:
716
- isAssignment = true;
717
- break;
718
- case ts.SyntaxKind.FirstBinaryOperator:
719
- // console.log("Generic start: " + child.getText());
720
- isInGenericDeclaration = true;
721
- isGenericStart = true;
722
- break;
723
- case ts.SyntaxKind.GreaterThanGreaterThanToken:
724
- isInGenericDeclaration = false;
725
- // console.log("Generic end: " + child.getText());
726
- break;
727
- }
728
- // if (isAssignment) break;
729
- var childResult = tryResolveTypeRecursive(child);
730
- if (childResult !== undefined) {
731
- if (res === undefined)
732
- res = "";
733
- if (allowDebugLogs)
734
- console.log("Child: " + ts.SyntaxKind[child.kind] + " → " + childResult);
735
- // if the thing is a generic return as generic result
736
- if (isInGenericDeclaration && !res.includes("[]")) {
737
- res = "<" + childResult + ">";
738
- }
739
- // we got a generic result, these need to be appended
740
- else if (childResult.startsWith("<")) {
741
- res += childResult;
742
- }
743
- // concat default
744
- else
745
- res = childResult + res;
746
- }
747
- }
748
- // if (ts.isTypeReferenceNode(node)) {
749
- // const typeRef = node as ts.TypeReferenceNode;
750
- // const typeName = typeRef.typeName.getText();
751
- // switch (typeName) {
752
- // case "Array":
753
- // res += "[]";
754
- // return res;
755
- // }
756
- // }
757
- return res;
758
- }
759
- }
760
- }
761
- exports.run = run;
762
- if (process) {
763
- if (process.argv.length < 4) {
764
- console.error("Missing args, call with: <output_dir> <input_files>");
765
- }
766
- else {
767
- var outputDir_1 = process.argv[2];
768
- var fileNames = process.argv.slice(3);
769
- fileNames.forEach(function (fileName) {
770
- try {
771
- if (!fs.existsSync(fileName)) {
772
- console.error("File not found: " + fileName);
773
- }
774
- else {
775
- var code = (0, fs_1.readFileSync)(fileName).toString();
776
- compile(code, fileName, outputDir_1);
777
- }
778
- }
779
- catch (e) {
780
- console.error(e);
781
- }
782
- });
783
- }
784
- }
1
+ "use strict";
2
+ exports.__esModule = true;
3
+ exports.run = exports.compile = void 0;
4
+ var fs_1 = require("fs");
5
+ var ts = require("typescript");
6
+ var fs = require("fs");
7
+ var path = require("path");
8
+ var types = require("./types");
9
+ var dict = types.dict;
10
+ // add either of these two comments above a class to enforce code gen or disable it for the next class
11
+ var exportNextClassCommand = "@generate-component";
12
+ var dontExportNextClassCommand = "@dont-generate-component";
13
+ // add above field to add [SerializeField] attribute
14
+ var serializeCommand = "@serializeField";
15
+ var dontSerializeCommand = "@nonSerialized";
16
+ // https://regex101.com/r/ltpcKT/2
17
+ var typePattern = new RegExp("@type ?(?<type>.+)");
18
+ var ifdefPattern = new RegExp("@ifdef ?(?<ifdef>.+)");
19
+ var CODEGEN_MARKER_START = "// NEEDLE_CODEGEN_START";
20
+ var CODEGEN_MARKER_END = "// NEEDLE_CODEGEN_END";
21
+ var allowDebugLogs = true;
22
+ // will be set to true when e.g. a comment for export is found
23
+ var exportNextClass = false;
24
+ var dontExportNextClass = false;
25
+ var serializeField = false;
26
+ var dontSerialize = false;
27
+ var typesFileContent = undefined;
28
+ // const exportDir = "../dist";
29
+ var commentStarts = [];
30
+ var contexts = [];
31
+ var lastTypeFound = null;
32
+ var ifdefSections = [];
33
+ function resetAllState() {
34
+ exportNextClass = false;
35
+ dontExportNextClass = false;
36
+ serializeField = false;
37
+ dontSerialize = false;
38
+ typesFileContent = undefined;
39
+ commentStarts.length = 0;
40
+ contexts.length = 0;
41
+ lastTypeFound = null;
42
+ ifdefSections.length = 0;
43
+ }
44
+ function resetExportNextClass() {
45
+ dontExportNextClass = false;
46
+ exportNextClass = false;
47
+ }
48
+ function tryGetKnownType(str) {
49
+ if (typesFileContent === undefined) {
50
+ typesFileContent = null;
51
+ var filePath = path.dirname(__dirname) + "/src/types.json";
52
+ if (fs.existsSync(filePath)) {
53
+ if (allowDebugLogs)
54
+ console.log("Reading types file");
55
+ var content = fs.readFileSync(filePath, "utf8");
56
+ typesFileContent = JSON.parse(content);
57
+ }
58
+ }
59
+ if (typesFileContent) {
60
+ var fullType = typesFileContent[str];
61
+ if (fullType && allowDebugLogs)
62
+ console.log(fullType);
63
+ return fullType;
64
+ }
65
+ return null;
66
+ }
67
+ // https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
68
+ var ExportContext = /** @class */ (function () {
69
+ function ExportContext(outputDir, fileName) {
70
+ this.classEnd = -1;
71
+ this.indentLevel = 0;
72
+ this.emitMethodContextMenu = null;
73
+ this.emitTooltip = null;
74
+ this.outputDir = outputDir;
75
+ this.fileName = fileName;
76
+ this.reset();
77
+ }
78
+ ExportContext.prototype.onBeforeField = function (name) {
79
+ if (this.emitTooltip) {
80
+ this.appendLine("[UnityEngine.Tooltip(\"" + this.emitTooltip.trim().replace("\"", "'") + "\")]");
81
+ this.emitTooltip = undefined;
82
+ }
83
+ };
84
+ ExportContext.prototype.onBeforeMethod = function (name) {
85
+ if (this.emitMethodContextMenu !== undefined) {
86
+ var contextMenuText = this.emitMethodContextMenu === null ? name : this.emitMethodContextMenu;
87
+ this.appendLine("[UnityEngine.ContextMenu(\"" + contextMenuText + "\")]");
88
+ this.emitMethodContextMenu = undefined;
89
+ }
90
+ };
91
+ ExportContext.prototype.append = function (text) {
92
+ for (var i = 0; i < this.indentLevel; i++)
93
+ text = "\t" + text;
94
+ this.textBuffer += text;
95
+ this.emitMethodContextMenu = undefined;
96
+ };
97
+ ExportContext.prototype.appendLine = function (text) {
98
+ this.append(text + "\n");
99
+ };
100
+ ExportContext.prototype.flush = function () {
101
+ if (this.textBuffer.length <= 0) {
102
+ return;
103
+ }
104
+ this.textBuffer = CODEGEN_MARKER_START + "\n" + this.textBuffer + "\n" + CODEGEN_MARKER_END;
105
+ var code = this.textBuffer;
106
+ if (this.outputDir !== null) {
107
+ if (!fs.existsSync(this.outputDir))
108
+ fs.mkdirSync(this.outputDir);
109
+ var dir = this.outputDir + "/";
110
+ var path_1 = dir + this.fileName;
111
+ code = this.replaceGeneratedCodeSection(path_1, code);
112
+ if (allowDebugLogs)
113
+ console.log("Write to " + path_1);
114
+ fs.writeFileSync(path_1, code);
115
+ }
116
+ else {
117
+ console.log("No output dir specified");
118
+ }
119
+ this.reset();
120
+ return code;
121
+ };
122
+ ExportContext.prototype.reset = function () {
123
+ this.textBuffer = "";
124
+ this.classEnd = -1;
125
+ };
126
+ ExportContext.prototype.replaceGeneratedCodeSection = function (path, code) {
127
+ if (fs.existsSync(path)) {
128
+ var existing = fs.readFileSync(path, "utf8");
129
+ var regex = new RegExp("(?<before>.*?)\/\/ ?NEEDLE_CODEGEN_START.+\/\/ ?NEEDLE_CODEGEN_END(?<after>.*)", "s");
130
+ var matches = regex.exec(existing);
131
+ if (matches === null || matches === void 0 ? void 0 : matches.groups) {
132
+ if (allowDebugLogs)
133
+ console.log("Found codegen sections");
134
+ var before_1 = matches.groups.before;
135
+ var after_1 = matches.groups.after;
136
+ return before_1 + code + after_1;
137
+ }
138
+ }
139
+ return code;
140
+ };
141
+ return ExportContext;
142
+ }());
143
+ function compile(code, fileName, outputDir, debugLogs) {
144
+ if (debugLogs === void 0) { debugLogs = true; }
145
+ resetAllState();
146
+ allowDebugLogs = debugLogs;
147
+ // Parse a file
148
+ var sourceFile = ts.createSourceFile(fileName, code, ts.ScriptTarget.ES2015, true);
149
+ var prog = ts.createProgram([fileName], {});
150
+ // delint it
151
+ return run(prog, outputDir, sourceFile);
152
+ }
153
+ exports.compile = compile;
154
+ function run(program, outputDir, sourceFile) {
155
+ if (outputDir !== null && !fs.existsSync(outputDir)) {
156
+ console.error("Output directory does not exist: \"" + outputDir + "\"");
157
+ return;
158
+ }
159
+ var results = [];
160
+ traverseFile(sourceFile);
161
+ function traverseFile(node) {
162
+ visit(node);
163
+ ts.forEachChild(node, traverseFile);
164
+ }
165
+ return results;
166
+ function visit(node) {
167
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
168
+ var context = contexts.length > 0 ? contexts[contexts.length - 1] : null;
169
+ if (context) {
170
+ if ((context === null || context === void 0 ? void 0 : context.classEnd) > 0 && node.pos >= (context === null || context === void 0 ? void 0 : context.classEnd)) {
171
+ while (context.indentLevel > 0) {
172
+ context.indentLevel -= 1;
173
+ context.append("}\n");
174
+ }
175
+ var code = context.flush();
176
+ results.push(code);
177
+ context = null;
178
+ contexts.pop();
179
+ }
180
+ }
181
+ if (allowDebugLogs)
182
+ console.log("\t", ts.SyntaxKind[node.kind]);
183
+ var commentRanges = ts.getLeadingCommentRanges(sourceFile.getFullText(), node.getFullStart());
184
+ if (commentRanges === null || commentRanges === void 0 ? void 0 : commentRanges.length) {
185
+ for (var _i = 0, commentRanges_1 = commentRanges; _i < commentRanges_1.length; _i++) {
186
+ var r = commentRanges_1[_i];
187
+ // avoid emitting comments multiple times
188
+ if (commentStarts.includes(r.pos))
189
+ continue;
190
+ commentStarts.push(r.pos);
191
+ var comment = node.getSourceFile().getFullText().slice(r.pos, r.end);
192
+ console.log(comment);
193
+ if (context) {
194
+ // https://regex101.com/r/ud4oev/1
195
+ var emitContextMenu = comment.match("(\/{2,}|\/\*) {0,}@contextmenu {1,}(?<text>[a-zA-Z 0-9]+)?");
196
+ if (emitContextMenu) {
197
+ context.emitMethodContextMenu = (_b = (_a = emitContextMenu.groups) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : null;
198
+ }
199
+ // https://regex101.com/r/Sa6Q8T/3
200
+ var emitTooltip = comment.match("\/{2,} {0,}(@tooltip) *?(?<text>.+)");
201
+ if (emitTooltip) {
202
+ console.log((_c = emitTooltip.groups) === null || _c === void 0 ? void 0 : _c.text);
203
+ context.emitTooltip = (_e = (_d = emitTooltip.groups) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : null;
204
+ }
205
+ else if (comment.includes(serializeCommand))
206
+ serializeField = true;
207
+ else if (comment.includes(dontSerializeCommand))
208
+ dontSerialize = true;
209
+ }
210
+ if (comment.includes(exportNextClassCommand))
211
+ exportNextClass = true;
212
+ if (comment.includes(dontExportNextClassCommand))
213
+ dontExportNextClass = true;
214
+ var typeMatch = typePattern.exec(comment);
215
+ if (typeMatch && typeMatch.groups) {
216
+ // for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
217
+ // so we remove them
218
+ var type = typeMatch.groups["type"];
219
+ type = type.replace(/\(/, "").replace(/\)/, "");
220
+ if (allowDebugLogs)
221
+ console.log("Found type: ", type);
222
+ lastTypeFound = type;
223
+ }
224
+ var ifdefMatch = ifdefPattern.exec(comment);
225
+ if (ifdefMatch && ifdefMatch.groups) {
226
+ var ifdef = ifdefMatch.groups["ifdef"];
227
+ if (ifdef)
228
+ ifdefSections.push(ifdef);
229
+ }
230
+ }
231
+ }
232
+ var skip = dontSerialize;
233
+ switch (node.kind) {
234
+ // Namespace
235
+ // case ts.SyntaxKind.ModuleDeclaration:
236
+ // const mod = node as ts.ModuleDeclaration;
237
+ // console.log(ts.SyntaxKind[mod.getChildAt(1).kind])
238
+ // const type = mod.getChildAt(1) as ts.Identifier;
239
+ // console.log("MODULE", type.text)
240
+ // break;
241
+ // case ts.SyntaxKind.Identifier:
242
+ // break;
243
+ // case ts.SyntaxKind.ClassDeclaration:
244
+ // case ts.SyntaxKind.SingleLineCommentTrivia:
245
+ // console.log("comment: " + node.getText())
246
+ // break;
247
+ case ts.SyntaxKind.Decorator:
248
+ break;
249
+ case ts.SyntaxKind.MethodDeclaration:
250
+ lastTypeFound = null;
251
+ serializeField = false;
252
+ dontSerialize = false;
253
+ resetExportNextClass();
254
+ if (!context)
255
+ break;
256
+ // TODO: always emit at least OnEnable method per class so generated components have toggle in editor
257
+ var meth = node;
258
+ // const isCoroutine = func.asteriskToken;
259
+ if (!skip && meth.name) {
260
+ var pub_1 = shouldEmitMethod(meth);
261
+ if (!pub_1)
262
+ return;
263
+ var paramsStr = "";
264
+ for (var _k = 0, _l = meth.parameters; _k < _l.length; _k++) {
265
+ var param = _l[_k];
266
+ if (!param || !param.name)
267
+ continue;
268
+ if (paramsStr.length > 0)
269
+ paramsStr += ", ";
270
+ var type = tryResolveTypeRecursive(param);
271
+ if (type === undefined)
272
+ type = "object";
273
+ var paramName = "";
274
+ var paramNameKind = param.name.kind;
275
+ switch (paramNameKind) {
276
+ case ts.SyntaxKind.ObjectBindingPattern:
277
+ paramName = "obj";
278
+ break;
279
+ default:
280
+ paramName = param.name.getText();
281
+ break;
282
+ }
283
+ paramsStr += type + " @" + paramName;
284
+ }
285
+ var methodName = meth.name.getText();
286
+ switch (methodName) {
287
+ case "onEnable":
288
+ methodName = "OnEnable";
289
+ break;
290
+ case "onDisable":
291
+ methodName = "OnDisable";
292
+ break;
293
+ }
294
+ context.onBeforeMethod(methodName);
295
+ // let visibility = pub ? "public" : "private";
296
+ context.append("public void " + methodName + "(" + paramsStr + "){}\n");
297
+ }
298
+ break;
299
+ case ts.SyntaxKind.SetAccessor:
300
+ case ts.SyntaxKind.PropertyDeclaration:
301
+ resetExportNextClass();
302
+ if (!context)
303
+ break;
304
+ if (allowDebugLogs)
305
+ console.log("Found variable", ts.SyntaxKind[node.kind], "\n", node.getText());
306
+ var vardec = node;
307
+ var varName = "@" + vardec.name.getText();
308
+ var pub = shouldEmitMethod(vardec);
309
+ var visibility = pub ? "public" : "private";
310
+ var isAccessible = pub;
311
+ dontSerialize = false;
312
+ if (serializeField) {
313
+ if (allowDebugLogs)
314
+ console.log("[SerializeField]");
315
+ context.appendLine("[UnityEngine.SerializeField]");
316
+ isAccessible = true;
317
+ }
318
+ else if (skip)
319
+ isAccessible = false;
320
+ if (!isAccessible) {
321
+ if (allowDebugLogs)
322
+ console.log("Skip because not public or serializeable");
323
+ break;
324
+ }
325
+ var name_1 = vardec.name.getText();
326
+ if (allowDebugLogs)
327
+ console.log("Variable:", name_1);
328
+ if (name_1.startsWith("\"@") || name_1.startsWith("\"$") || name_1.startsWith("$"))
329
+ break;
330
+ var typeString = lastTypeFound !== null && lastTypeFound !== void 0 ? lastTypeFound : tryResolveTypeRecursive(node);
331
+ var postFix = "";
332
+ var typeName = (_f = vardec.type) === null || _f === void 0 ? void 0 : _f.getText();
333
+ var shouldCommentTheLine = typeString === undefined;
334
+ if (typeString === undefined) {
335
+ postFix = " → Could not resolve C# type";
336
+ }
337
+ var assignment = "";
338
+ if (typeString !== undefined) {
339
+ for (var _m = 0, _o = node.getChildren(); _m < _o.length; _m++) {
340
+ var ch = _o[_m];
341
+ switch (ch.kind) {
342
+ default:
343
+ // console.log("Unknown assignment:", ts.SyntaxKind[ch.kind]);
344
+ break;
345
+ case ts.SyntaxKind.NewExpression:
346
+ assignment = " = " + getTypeForAssignment(ch, typeString);
347
+ break;
348
+ case ts.SyntaxKind.FalseKeyword:
349
+ case ts.SyntaxKind.TrueKeyword:
350
+ assignment = " = " + ch.getText();
351
+ break;
352
+ case ts.SyntaxKind.StringLiteral:
353
+ var str = ch;
354
+ assignment = " = \"" + str.text + "\"";
355
+ break;
356
+ case ts.SyntaxKind.FirstLiteralToken:
357
+ var lit = ch;
358
+ assignment = " = " + lit.text;
359
+ if (ts.isNumericLiteral(lit))
360
+ assignment += "f";
361
+ break;
362
+ case ts.SyntaxKind.ArrayLiteralExpression:
363
+ var arr = ch;
364
+ assignment = " = new " + typeString;
365
+ // if (arr.elements.length > 0) {
366
+ assignment += "{" + arr.elements.map(function (e) { return " " + getTypeForAssignment(e); }) + " }";
367
+ // }
368
+ break;
369
+ }
370
+ }
371
+ }
372
+ var requireEndIf = false;
373
+ if (ifdefSections.length > 0) {
374
+ requireEndIf = true;
375
+ context.appendLine("#ifdef " + ifdefSections.pop());
376
+ }
377
+ if (typeString === undefined)
378
+ typeString = typeName;
379
+ if (typeString === "[]") {
380
+ if (allowDebugLogs)
381
+ console.log("Unknown array type for \"" + varName + " " + typeName + "\" - your type is probably not known (did you just create it this session?) and you might need to regenerate the Typemap in Unity. Go to \"Needle Engine/Internal/Generate Type Map for component compiler");
382
+ // typeString = "object[]";
383
+ // assignment = " = new object[0]";
384
+ shouldCommentTheLine = true;
385
+ }
386
+ context.onBeforeField(varName);
387
+ if (allowDebugLogs)
388
+ console.log("EMIT member: \"" + typeString + "\" " + varName, assignment, "Last type found:", lastTypeFound);
389
+ var prefix = shouldCommentTheLine ? "// " : "";
390
+ var isUnityEditorType = typeString === null || typeString === void 0 ? void 0 : typeString.includes("UnityEditor");
391
+ if (isUnityEditorType) {
392
+ context.appendLine("#if UNITY_EDITOR");
393
+ }
394
+ context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";" + postFix + "\n");
395
+ if (isUnityEditorType) {
396
+ context.appendLine("#endif");
397
+ }
398
+ lastTypeFound = null;
399
+ if (requireEndIf) {
400
+ context.appendLine("#endif");
401
+ }
402
+ break;
403
+ case ts.SyntaxKind.ClassDeclaration:
404
+ serializeField = false;
405
+ var dec = node;
406
+ // a class must inherit a component
407
+ var inheritsComponent = testInheritsComponent(node);
408
+ if (!dontExportNextClass && (lastTypeFound || exportNextClass || inheritsComponent)) {
409
+ resetExportNextClass();
410
+ var name_2 = (_g = dec.name) === null || _g === void 0 ? void 0 : _g.escapedText;
411
+ if (allowDebugLogs)
412
+ console.log("Found class: ", name_2);
413
+ var namespace = (_h = tryParseNamespace(node)) !== null && _h !== void 0 ? _h : "Needle.Typescript.GeneratedComponents";
414
+ if (allowDebugLogs)
415
+ console.log("NAMESPACE", namespace);
416
+ var newContext = new ExportContext(outputDir, name_2 + ".cs");
417
+ newContext.appendLine("// auto generated code - do not edit directly");
418
+ newContext.appendLine("");
419
+ newContext.appendLine("#pragma warning disable");
420
+ newContext.appendLine("");
421
+ newContext.appendLine("namespace " + namespace);
422
+ newContext.appendLine("{");
423
+ newContext.indentLevel += 1;
424
+ // newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
425
+ var typeName_1 = "UnityEngine.MonoBehaviour";
426
+ if (typeof inheritsComponent === "string")
427
+ typeName_1 = inheritsComponent;
428
+ if (lastTypeFound)
429
+ typeName_1 = lastTypeFound;
430
+ if (allowDebugLogs)
431
+ console.log(name_2 + " inherits " + typeName_1);
432
+ var modifiers = "";
433
+ if (dec.modifiers) {
434
+ for (var _p = 0, _q = dec.modifiers; _p < _q.length; _p++) {
435
+ var mod = _q[_p];
436
+ switch (mod.getText()) {
437
+ case "abstract":
438
+ modifiers += " abstract";
439
+ if (allowDebugLogs)
440
+ console.log(name_2 + " is abstract");
441
+ break;
442
+ }
443
+ }
444
+ }
445
+ modifiers += " partial";
446
+ // If it's a custom class and decorated with @type object then we want to make it serializable
447
+ if (typeName_1 === "object") {
448
+ newContext.appendLine("[System.Serializable]");
449
+ }
450
+ newContext.appendLine("public " + modifiers.trim() + " class " + name_2 + " : " + typeName_1);
451
+ newContext.appendLine("{");
452
+ newContext.indentLevel += 1;
453
+ newContext.classEnd = dec.end;
454
+ contexts.push(newContext);
455
+ }
456
+ else {
457
+ if (allowDebugLogs)
458
+ console.log("Class type is unknown and will not generate a component: ", (_j = dec.name) === null || _j === void 0 ? void 0 : _j.escapedText);
459
+ }
460
+ lastTypeFound = null;
461
+ break;
462
+ }
463
+ function testInheritsComponent(node) {
464
+ switch (node.kind) {
465
+ case ts.SyntaxKind.ClassDeclaration:
466
+ var dec = node;
467
+ if (dec.heritageClauses) {
468
+ for (var _i = 0, _a = dec.heritageClauses; _i < _a.length; _i++) {
469
+ var h = _a[_i];
470
+ if (h.types.length <= 0)
471
+ continue;
472
+ for (var _b = 0, _c = h.types; _b < _c.length; _b++) {
473
+ var type = _c[_b];
474
+ // const symbol = program.getTypeChecker().getSymbolAtLocation(type.expression);
475
+ // console.log(symbol);
476
+ var text = type.expression.getText();
477
+ if (text === "Component")
478
+ return true;
479
+ if (text === "Behaviour")
480
+ return true;
481
+ var known = tryGetKnownType(text);
482
+ if (known)
483
+ return known;
484
+ }
485
+ }
486
+ }
487
+ return false;
488
+ }
489
+ return false;
490
+ }
491
+ function getTypeForAssignment(node, typeString) {
492
+ // console.log("-------------------\nAssign", ts.SyntaxKind[node.kind]);
493
+ switch (node.kind) {
494
+ case ts.SyntaxKind.FirstLiteralToken:
495
+ return node.getText();
496
+ case ts.SyntaxKind.NewExpression:
497
+ var type = undefined;
498
+ var args = undefined;
499
+ for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
500
+ var ch = _a[_i];
501
+ var text = ch.getText();
502
+ console.log("child", ts.SyntaxKind[ch.kind], text);
503
+ switch (ch.kind) {
504
+ case ts.SyntaxKind.Identifier:
505
+ case ts.SyntaxKind.PropertyAccessExpression:
506
+ type = tryGetTypeFromText(text);
507
+ break;
508
+ case ts.SyntaxKind.SyntaxList:
509
+ for (var _b = 0, _c = ch.getChildren(); _b < _c.length; _b++) {
510
+ var arg = _c[_b];
511
+ if (args === undefined)
512
+ args = "";
513
+ var res = getTypeForAssignment(arg, typeString);
514
+ // handle floats being assigned with "f" suffix
515
+ if (Number.parseFloat(res) >= 0) {
516
+ args += res + "f";
517
+ }
518
+ else {
519
+ args += res;
520
+ if (res === ",")
521
+ args += " ";
522
+ }
523
+ }
524
+ break;
525
+ }
526
+ }
527
+ if (!args)
528
+ args = "";
529
+ if (type || typeString) {
530
+ if (typeString) {
531
+ console.log("Override type", type, typeString);
532
+ type = typeString;
533
+ }
534
+ return "new " + type + "(" + args + ")";
535
+ }
536
+ // const expType = node.getChildren().find(c => c.kind === ts.SyntaxKind.Identifier);
537
+ break;
538
+ }
539
+ var str = node.getText();
540
+ if (allowDebugLogs)
541
+ console.log("Unknown assignment:", str, ts.SyntaxKind[node.kind]);
542
+ return str;
543
+ }
544
+ function shouldEmitMethod(node) {
545
+ if (node.kind === ts.SyntaxKind.PublicKeyword) {
546
+ return true;
547
+ }
548
+ else if (node.kind === ts.SyntaxKind.PrivateKeyword || node.kind === ts.SyntaxKind.ProtectedKeyword) {
549
+ return false;
550
+ }
551
+ // check if its static
552
+ else if (node.kind === ts.SyntaxKind.StaticKeyword) {
553
+ return false;
554
+ }
555
+ // check if its abstract
556
+ else if (node.kind === ts.SyntaxKind.AbstractKeyword) {
557
+ return false;
558
+ }
559
+ for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
560
+ var ch = _a[_i];
561
+ if (!shouldEmitMethod(ch))
562
+ return false;
563
+ }
564
+ return true;
565
+ }
566
+ function tryParseNamespace(node, namespace) {
567
+ // console.log("TRAVERSE - " + ts.SyntaxKind[node.kind]);
568
+ switch (node.kind) {
569
+ case ts.SyntaxKind.ModuleDeclaration:
570
+ for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
571
+ var ch = _a[_i];
572
+ // console.log("-- TRAVERSE - " + ts.SyntaxKind[ch.kind]);
573
+ switch (ch.kind) {
574
+ case ts.SyntaxKind.Identifier:
575
+ var id = ch;
576
+ if (id.text) {
577
+ if (!namespace)
578
+ namespace = "";
579
+ namespace = id.text + (namespace ? "." : "") + namespace;
580
+ }
581
+ break;
582
+ }
583
+ }
584
+ break;
585
+ }
586
+ if (node.parent) {
587
+ return tryParseNamespace(node.parent, namespace);
588
+ }
589
+ return namespace;
590
+ }
591
+ function tryGetTypeFromText(typeName) {
592
+ // if a type is imported via some namespace e.g. THREE.AnimationClip
593
+ // we want to remove that namespace / import name
594
+ var separatorIndex = typeName.lastIndexOf(".");
595
+ if (separatorIndex > 0) {
596
+ var newName = typeName.substring(separatorIndex + 1);
597
+ if (allowDebugLogs)
598
+ console.log("Remove import name from type: \"" + typeName + "\" → \"" + newName + "\"");
599
+ typeName = newName;
600
+ }
601
+ var res = dict[typeName];
602
+ if (res === undefined) {
603
+ switch (typeName) {
604
+ case "Array":
605
+ break;
606
+ default:
607
+ var knownType = tryGetKnownType(typeName);
608
+ res = knownType !== null && knownType !== void 0 ? knownType : undefined;
609
+ break;
610
+ }
611
+ }
612
+ // console.log(typeName, res);
613
+ return res;
614
+ }
615
+ function tryResolveTypeRecursive(node) {
616
+ if (!node)
617
+ return undefined;
618
+ // skip decorators (e.g. @serializable() may break array generation)
619
+ if (node.kind === ts.SyntaxKind.Decorator)
620
+ return undefined;
621
+ var typeName = node === null || node === void 0 ? void 0 : node.getText();
622
+ var varDec = node;
623
+ if (varDec.type) {
624
+ typeName = varDec.type.getText();
625
+ }
626
+ var res = undefined;
627
+ // if it's a { x: number } inline object type:
628
+ if (typeName.startsWith("{")) {
629
+ // check if it has XYZW we assume it's a vector
630
+ var hasX = typeName.includes("x");
631
+ var hasY = typeName.includes("y");
632
+ var hasZ = typeName.includes("z");
633
+ var hasW = typeName.includes("w");
634
+ if (hasX && hasY && hasZ && hasW) {
635
+ return tryGetTypeFromText("Vector4");
636
+ }
637
+ else if (hasX && hasY && hasZ) {
638
+ return tryGetTypeFromText("Vector3");
639
+ }
640
+ else if (hasX && hasY) {
641
+ return tryGetTypeFromText("Vector2");
642
+ }
643
+ return "object";
644
+ }
645
+ // const kindName = ts.SyntaxKind[node.kind];
646
+ // console.log("Unknown type: " + typeName);
647
+ switch (node.kind) {
648
+ case ts.SyntaxKind.ObjectBindingPattern:
649
+ res = "object";
650
+ break;
651
+ // case ts.SyntaxKind.SyntaxList:
652
+ // const list = node as ts.SyntaxList;
653
+ // for (const ch of list._children) {
654
+ // res = tryResolveTypeRecursive(ch);
655
+ // }
656
+ // break;
657
+ case ts.SyntaxKind.UnionType:
658
+ var union = node;
659
+ for (var _i = 0, _a = union.types; _i < _a.length; _i++) {
660
+ var t = _a[_i];
661
+ res = tryResolveTypeRecursive(t);
662
+ if (res !== undefined)
663
+ return res;
664
+ }
665
+ break;
666
+ case ts.SyntaxKind.ArrayType:
667
+ res = "[]";
668
+ break;
669
+ case ts.SyntaxKind.TypeReference:
670
+ var typeRef = node;
671
+ var typeName_2 = typeRef.typeName.getText();
672
+ if (allowDebugLogs)
673
+ console.log("TypeReference:", typeName_2);
674
+ switch (typeName_2) {
675
+ case "Array":
676
+ break;
677
+ default:
678
+ return tryGetTypeFromText(typeName_2);
679
+ }
680
+ // return res;
681
+ break;
682
+ case ts.SyntaxKind.BooleanKeyword:
683
+ case ts.SyntaxKind.NumberKeyword:
684
+ case ts.SyntaxKind.StringKeyword:
685
+ case ts.SyntaxKind.ObjectKeyword:
686
+ var keyword = node.getText();
687
+ // the basic keywords are declared in the static dictionary
688
+ // no need for a complex lookup
689
+ res = dict[keyword];
690
+ break;
691
+ case ts.SyntaxKind.Identifier:
692
+ var id = node;
693
+ switch (id.text) {
694
+ // if we have an array we dont want to use the System.Array as a type but just make it to the array syntax
695
+ case "Array":
696
+ res = "[]";
697
+ break;
698
+ default:
699
+ // console.log(id.text);
700
+ // res = tryGetTypeFromText(id.text);
701
+ break;
702
+ }
703
+ break;
704
+ }
705
+ var isInGenericDeclaration = false;
706
+ for (var _b = 0, _c = node.getChildren(); _b < _c.length; _b++) {
707
+ var child = _c[_b];
708
+ // Fix https://linear.app/needle/issue/NE-4423
709
+ if (child.kind === ts.SyntaxKind.Block) {
710
+ if (allowDebugLogs)
711
+ console.log("Skip block");
712
+ continue;
713
+ }
714
+ if (allowDebugLogs)
715
+ console.log("Child type: " + ts.SyntaxKind[child.kind]);
716
+ var isGenericStart = false;
717
+ var isAssignment = false;
718
+ switch (child.kind) {
719
+ case ts.SyntaxKind.FirstAssignment:
720
+ isAssignment = true;
721
+ break;
722
+ case ts.SyntaxKind.FirstBinaryOperator:
723
+ // console.log("Generic start: " + child.getText());
724
+ isInGenericDeclaration = true;
725
+ isGenericStart = true;
726
+ break;
727
+ case ts.SyntaxKind.GreaterThanGreaterThanToken:
728
+ isInGenericDeclaration = false;
729
+ // console.log("Generic end: " + child.getText());
730
+ break;
731
+ }
732
+ // if (isAssignment) break;
733
+ var childResult = tryResolveTypeRecursive(child);
734
+ if (childResult !== undefined) {
735
+ if (res === undefined)
736
+ res = "";
737
+ if (allowDebugLogs)
738
+ console.log("Child: " + ts.SyntaxKind[child.kind] + " → " + childResult);
739
+ // if the thing is a generic return as generic result
740
+ if (isInGenericDeclaration && !res.includes("[]")) {
741
+ res = "<" + childResult + ">";
742
+ }
743
+ // we got a generic result, these need to be appended
744
+ else if (childResult.startsWith("<")) {
745
+ res += childResult;
746
+ }
747
+ // concat default
748
+ else
749
+ res = childResult + res;
750
+ }
751
+ }
752
+ // if (ts.isTypeReferenceNode(node)) {
753
+ // const typeRef = node as ts.TypeReferenceNode;
754
+ // const typeName = typeRef.typeName.getText();
755
+ // switch (typeName) {
756
+ // case "Array":
757
+ // res += "[]";
758
+ // return res;
759
+ // }
760
+ // }
761
+ return res;
762
+ }
763
+ }
764
+ }
765
+ exports.run = run;
766
+ if (process) {
767
+ if (process.argv.length < 4) {
768
+ console.error("Missing args, call with: <output_dir> <input_files>");
769
+ }
770
+ else {
771
+ var outputDir_1 = process.argv[2];
772
+ var fileNames = process.argv.slice(3);
773
+ fileNames.forEach(function (fileName) {
774
+ try {
775
+ if (!fs.existsSync(fileName)) {
776
+ console.error("File not found: " + fileName);
777
+ }
778
+ else {
779
+ var code = (0, fs_1.readFileSync)(fileName).toString();
780
+ compile(code, fileName, outputDir_1);
781
+ }
782
+ }
783
+ catch (e) {
784
+ console.error(e);
785
+ }
786
+ });
787
+ }
788
+ }