@needle-tools/needle-component-compiler 3.0.0-alpha → 3.0.0-alpha.1

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/Changelog.md CHANGED
@@ -4,6 +4,11 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.0.0-alpha.1] - 2026-03-02
8
+ - Fix: destructured method parameters (e.g. `{xr}`) now emit as `object @obj` instead of invalid `object @{xr}`
9
+ - Fix: `Object3D` type now correctly maps to `UnityEngine.Transform`
10
+ - Add: `RectTransform` type mapping to `UnityEngine.RectTransform`
11
+
7
12
  ## [2.4.1-pre] - 2023-04-03
8
13
  # Blender compiler
9
14
  - Add: typenames
@@ -24,6 +29,46 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
24
29
  ## [2.1.0-pre] - 2022-11-12
25
30
  - Add blender scheme compiler for creating and updating schemes (or removing them when the component gets deleted)
26
31
 
32
+ ## [1.12.2] - 2025-06-06
33
+ - Fix: Private fields decorated with `@serializable()` should be annotated with `UnityEngine.SerializeField`
34
+ - Fix: Comment for `// @serializeField` should not affect subsequent fields
35
+
36
+ ## [1.12.0] - 2025-06-02
37
+ - Add: Support to serialize unknown classes when annotated with `// @type object`
38
+
39
+ ## [1.11.2] - 2023-12-16
40
+ - Fix: property setter emitting invalid C# code
41
+
42
+ ## [1.11.1] - 2023-12-02
43
+ - Fix: Use Unity AnimatorController type
44
+ - Fix: method inline anonymous type declaration (e.g. `myMethod(arg: {x:number})`)
45
+
46
+ ## [1.11.0] - 2023-11-18
47
+ - Add: support to wrap fields with `#if UNITY_EDITOR` if their namespace contains UnityEditor
48
+ - Add: more threejs types to generate Unity Material fields for
49
+
50
+ ## [1.10.3] - 2023-09-08
51
+ - Add: Needle Engine type `RGBAColor` is now automatically emitted as `UnityEngine.Color`
52
+
53
+ ## [1.10.2] - 2023-09-08
54
+ - Fix: `@type` was not properly applied for `new expression` cases (e.g. `new RGBAColor` should produce a `new Color` when decorated with `@type Color`)
55
+
56
+ ## [1.10.1] - 2023-08-02
57
+ - Add: use `@tooltip` to emit a UnityEngine.Tooltip
58
+ - Update Readme
59
+
60
+ ## [1.9.4] - 2023-05-24
61
+ - Change: `Object3D` now emits `GameObject` field instead of `Transform`
62
+ - Fix: ignore `static` methods
63
+ - Fix: ignore `abstract` methods
64
+
65
+ ## [1.9.3] - 2022-12-30
66
+ - Add debug logs for when no file or target directory was passed in. Also wrapping core with try catch
67
+
68
+ ## [1.9.2] - 2022-11-29
69
+ - Fix codegen deleting code added manually outside of codegen sections
70
+ - Fix codegen creating output directory if it does not exist yet
71
+
27
72
  ## [1.9.1] - 2022-11-05
28
73
  - Fix ``new Vector2(1, .5)`` generating invalid C# where number arguments were emitted as double instead of float
29
74
 
package/Readme.md CHANGED
@@ -1,19 +1,46 @@
1
- ## Typescript to Unity component
2
-
3
- Little helper package to transpile typescript files to Unity C# components.
4
- Please run ``npm install`` first before using.
5
-
6
- ### Usage
7
- ``node <path to>/component-compiler.js <output_directory> <path_to/my_script.ts>``
8
-
9
-
10
- ### Command decorators
11
- - ``@dont-generate-component`` add before class to skip generating a component
12
- - ``@generate-component`` to enforce generating a component (not required)
13
- - ``@serializeField`` field decorator, similar to ``[SerializeField]`` in Unity
14
- - ``@nonSerialized`` field or method decorator to skip generating c# code for a field or a method, similar to ``[NonSerialized]`` in Unity
15
- - ``@type MyNamespace.MyType`` decorator for fields or classes, specifiy C# type of field or class
16
- - ``@ifdef MY_IFDEF`` field decorator only at the moment
17
-
18
- ### Test
19
- - Run single test: `` npm run test:single -- -r ts-node/register "./test/blender/blender.from_typedef.test.ts"``
1
+ ## Needle Engine Component Compiler
2
+
3
+ Compiles TypeScript component definitions into **Unity C#** component stubs and **Blender** Python component schemas for Needle Engine.
4
+
5
+ ```
6
+ npm install @needle-tools/needle-component-compiler
7
+ ```
8
+
9
+ ### Supported TypeScript Features
10
+
11
+ - Classes extending `Behaviour`, `MonoBehaviour`, or implementing `IComponent`
12
+ - Public, protected, and private fields with type annotations
13
+ - Default values (primitives, `new` expressions, literals)
14
+ - Array types: `T[]`, `Array<T>`
15
+ - Union types (nullable types like `Object3D | null` resolve to the concrete type)
16
+ - Enums (with value initialization)
17
+ - Methods with parameters and return types
18
+ - Destructured parameters (compiled as `object`)
19
+
20
+ ### Comment Directives
21
+
22
+ Use `//` comments above classes, fields, or methods to control code generation:
23
+
24
+ | Directive | Target | Description |
25
+ |---|---|---|
26
+ | `@generate-component` | Class | Force generation (not required, classes are generated by default) |
27
+ | `@dont-generate-component` | Class | Skip generating this class entirely |
28
+ | `@type MyNamespace.MyType` | Class / Field | Override the resolved C# type or base class |
29
+ | `@abstract` | Class | Mark the generated class as `abstract` |
30
+ | `@serializeField` | Field | Emit `[UnityEngine.SerializeField]` attribute |
31
+ | `@nonSerialized` | Field / Method | Skip generating C# code for this member |
32
+ | `@tooltip "My text"` | Field | Emit `[UnityEngine.Tooltip("My text")]` |
33
+ | `@contextmenu "Label"` | Method | Emit `[UnityEngine.ContextMenu("Label")]` |
34
+ | `@ifdef MY_DEFINE` | Field | Wrap field in `#if MY_DEFINE` / `#endif` |
35
+
36
+ ### Codegen Fences
37
+
38
+ Generated C# files are wrapped in `// NEEDLE_CODEGEN_START` and `// NEEDLE_CODEGEN_END` markers. This allows you to add custom code outside the fences that will be preserved when the file is regenerated.
39
+
40
+ # Contact ✒️
41
+ <b>[🌵 Needle](https://needle.tools)</b> •
42
+ [Github](https://github.com/needle-tools) •
43
+ [Twitter](https://twitter.com/NeedleTools) •
44
+ [Discord](https://discord.needle.tools) •
45
+ [Forum](https://forum.needle.tools) •
46
+ [Youtube](https://youtube.com/@needle-tools)
package/dist/Compiler.js CHANGED
@@ -145,7 +145,8 @@ var Compiler = /** @class */ (function () {
145
145
  var result = [];
146
146
  for (var _i = 0, parameters_1 = parameters; _i < parameters_1.length; _i++) {
147
147
  var parameter = parameters_1[_i];
148
- var name_1 = parameter.name.getText();
148
+ // Destructured parameters (e.g. { xr }) get a generic name
149
+ var name_1 = ts.isObjectBindingPattern(parameter.name) ? "obj" : parameter.name.getText();
149
150
  var type = this.resolveType(parameter.type, writer);
150
151
  var defaultValue = parameter.initializer && this.resolveExpression(parameter.initializer, writer);
151
152
  result.push({ name: name_1, type: type, defaultValue: defaultValue });
@@ -75,14 +75,22 @@ var ExportContext = /** @class */ (function () {
75
75
  this.classEnd = -1;
76
76
  this.indentLevel = 0;
77
77
  this.emitMethodContextMenu = null;
78
+ this.emitTooltip = null;
78
79
  this.outputDir = outputDir;
79
80
  this.fileName = fileName;
80
81
  this.reset();
81
82
  }
83
+ ExportContext.prototype.onBeforeField = function (name) {
84
+ if (this.emitTooltip) {
85
+ this.appendLine("[UnityEngine.Tooltip(\"" + this.emitTooltip.trim().replace("\"", "'") + "\")]");
86
+ this.emitTooltip = undefined;
87
+ }
88
+ };
82
89
  ExportContext.prototype.onBeforeMethod = function (name) {
83
90
  if (this.emitMethodContextMenu !== undefined) {
84
91
  var contextMenuText = this.emitMethodContextMenu === null ? name : this.emitMethodContextMenu;
85
92
  this.appendLine("[UnityEngine.ContextMenu(\"" + contextMenuText + "\")]");
93
+ this.emitMethodContextMenu = undefined;
86
94
  }
87
95
  };
88
96
  ExportContext.prototype.append = function (text) {
@@ -95,18 +103,25 @@ var ExportContext = /** @class */ (function () {
95
103
  this.append(text + "\n");
96
104
  };
97
105
  ExportContext.prototype.flush = function () {
98
- if (this.textBuffer.length <= 0)
106
+ if (this.textBuffer.length <= 0) {
99
107
  return;
108
+ }
100
109
  this.textBuffer = CODEGEN_MARKER_START + "\n" + this.textBuffer + "\n" + CODEGEN_MARKER_END;
101
110
  var code = this.textBuffer;
102
111
  if (this.outputDir !== null) {
112
+ if (!fs.existsSync(this.outputDir))
113
+ fs.mkdirSync(this.outputDir);
103
114
  var dir = this.outputDir + "/";
104
115
  var path_1 = dir + this.fileName;
105
- this.replaceGeneratedCodeSection(path_1);
116
+ code = this.replaceGeneratedCodeSection(path_1, code);
106
117
  if (allowDebugLogs)
107
118
  console.log("Write to " + path_1);
108
119
  fs.writeFileSync(path_1, code);
109
120
  }
121
+ else {
122
+ if (allowDebugLogs)
123
+ console.log("No output dir specified");
124
+ }
110
125
  this.reset();
111
126
  return code;
112
127
  };
@@ -114,7 +129,7 @@ var ExportContext = /** @class */ (function () {
114
129
  this.textBuffer = "";
115
130
  this.classEnd = -1;
116
131
  };
117
- ExportContext.prototype.replaceGeneratedCodeSection = function (path) {
132
+ ExportContext.prototype.replaceGeneratedCodeSection = function (path, code) {
118
133
  if (fs.existsSync(path)) {
119
134
  var existing = fs.readFileSync(path, "utf8");
120
135
  var regex = new RegExp("(?<before>.*?)\/\/ ?NEEDLE_CODEGEN_START.+\/\/ ?NEEDLE_CODEGEN_END(?<after>.*)", "s");
@@ -124,9 +139,10 @@ var ExportContext = /** @class */ (function () {
124
139
  console.log("Found codegen sections");
125
140
  var before_1 = matches.groups.before;
126
141
  var after_1 = matches.groups.after;
127
- this.textBuffer = before_1 + this.textBuffer + after_1;
142
+ return before_1 + code + after_1;
128
143
  }
129
144
  }
145
+ return code;
130
146
  };
131
147
  return ExportContext;
132
148
  }());
@@ -154,7 +170,7 @@ function run(program, outputDir, sourceFile) {
154
170
  }
155
171
  return results;
156
172
  function visit(node) {
157
- var _a, _b, _c, _d, _e, _f;
173
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
158
174
  var context = contexts.length > 0 ? contexts[contexts.length - 1] : null;
159
175
  if (context) {
160
176
  if ((context === null || context === void 0 ? void 0 : context.classEnd) > 0 && node.pos >= (context === null || context === void 0 ? void 0 : context.classEnd)) {
@@ -179,12 +195,21 @@ function run(program, outputDir, sourceFile) {
179
195
  continue;
180
196
  commentStarts.push(r.pos);
181
197
  var comment = node.getSourceFile().getFullText().slice(r.pos, r.end);
198
+ if (allowDebugLogs)
199
+ console.log(comment);
182
200
  if (context) {
183
201
  // https://regex101.com/r/ud4oev/1
184
- var emitContextMenu = comment.match("(\/{2,}|\/\*)\s*@contextmenu {0,}(?<text>[a-zA-Z 0-9]+)?");
202
+ var emitContextMenu = comment.match("(\/{2,}|\/\*) {0,}@contextmenu {1,}(?<text>[a-zA-Z 0-9]+)?");
185
203
  if (emitContextMenu) {
186
204
  context.emitMethodContextMenu = (_b = (_a = emitContextMenu.groups) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : null;
187
205
  }
206
+ // https://regex101.com/r/Sa6Q8T/3
207
+ var emitTooltip = comment.match("\/{2,} {0,}(@tooltip) *?(?<text>.+)");
208
+ if (emitTooltip) {
209
+ if (allowDebugLogs)
210
+ console.log((_c = emitTooltip.groups) === null || _c === void 0 ? void 0 : _c.text);
211
+ context.emitTooltip = (_e = (_d = emitTooltip.groups) === null || _d === void 0 ? void 0 : _d.text) !== null && _e !== void 0 ? _e : null;
212
+ }
188
213
  else if (comment.includes(serializeCommand))
189
214
  serializeField = true;
190
215
  else if (comment.includes(dontSerializeCommand))
@@ -227,8 +252,6 @@ function run(program, outputDir, sourceFile) {
227
252
  // case ts.SyntaxKind.SingleLineCommentTrivia:
228
253
  // console.log("comment: " + node.getText())
229
254
  // break;
230
- case ts.SyntaxKind.Decorator:
231
- break;
232
255
  case ts.SyntaxKind.MethodDeclaration:
233
256
  lastTypeFound = null;
234
257
  serializeField = false;
@@ -240,12 +263,12 @@ function run(program, outputDir, sourceFile) {
240
263
  var meth = node;
241
264
  // const isCoroutine = func.asteriskToken;
242
265
  if (!skip && meth.name) {
243
- var pub_1 = isPublic(meth);
266
+ var pub_1 = shouldEmitMethod(meth);
244
267
  if (!pub_1)
245
268
  return;
246
269
  var paramsStr = "";
247
- for (var _g = 0, _h = meth.parameters; _g < _h.length; _g++) {
248
- var param = _h[_g];
270
+ for (var _k = 0, _l = meth.parameters; _k < _l.length; _k++) {
271
+ var param = _l[_k];
249
272
  if (!param || !param.name)
250
273
  continue;
251
274
  if (paramsStr.length > 0)
@@ -253,7 +276,17 @@ function run(program, outputDir, sourceFile) {
253
276
  var type = tryResolveTypeRecursive(param);
254
277
  if (type === undefined)
255
278
  type = "object";
256
- paramsStr += type + " @" + param.name.getText();
279
+ var paramName = "";
280
+ var paramNameKind = param.name.kind;
281
+ switch (paramNameKind) {
282
+ case ts.SyntaxKind.ObjectBindingPattern:
283
+ paramName = "obj";
284
+ break;
285
+ default:
286
+ paramName = param.name.getText();
287
+ break;
288
+ }
289
+ paramsStr += type + " @" + paramName;
257
290
  }
258
291
  var methodName = meth.name.getText();
259
292
  switch (methodName) {
@@ -275,21 +308,24 @@ function run(program, outputDir, sourceFile) {
275
308
  if (!context)
276
309
  break;
277
310
  if (allowDebugLogs)
278
- console.log("Found variable", node.getText());
311
+ console.log("Found variable", ts.SyntaxKind[node.kind], "\n", node.getText());
279
312
  var vardec = node;
280
313
  var varName = "@" + vardec.name.getText();
281
- var pub = isPublic(vardec);
314
+ var pub = shouldEmitMethod(vardec);
282
315
  var visibility = pub ? "public" : "private";
283
316
  var isAccessible = pub;
284
- dontSerialize = false;
317
+ if (!pub)
318
+ serializeField || (serializeField = testIsMarkedWithSerializable(node));
285
319
  if (serializeField) {
286
320
  if (allowDebugLogs)
287
- console.log("[SerializeField]");
321
+ console.log("Emit [UnityEngine.SerializeField]");
288
322
  context.appendLine("[UnityEngine.SerializeField]");
289
323
  isAccessible = true;
290
324
  }
291
325
  else if (skip)
292
326
  isAccessible = false;
327
+ dontSerialize = false;
328
+ serializeField = false;
293
329
  if (!isAccessible) {
294
330
  if (allowDebugLogs)
295
331
  console.log("Skip because not public or serializeable");
@@ -302,21 +338,21 @@ function run(program, outputDir, sourceFile) {
302
338
  break;
303
339
  var typeString = lastTypeFound !== null && lastTypeFound !== void 0 ? lastTypeFound : tryResolveTypeRecursive(node);
304
340
  var postFix = "";
305
- var typeName = (_c = vardec.type) === null || _c === void 0 ? void 0 : _c.getText();
341
+ var typeName = (_f = vardec.type) === null || _f === void 0 ? void 0 : _f.getText();
306
342
  var shouldCommentTheLine = typeString === undefined;
307
343
  if (typeString === undefined) {
308
344
  postFix = " → Could not resolve C# type";
309
345
  }
310
346
  var assignment = "";
311
347
  if (typeString !== undefined) {
312
- for (var _j = 0, _k = node.getChildren(); _j < _k.length; _j++) {
313
- var ch = _k[_j];
348
+ for (var _m = 0, _o = node.getChildren(); _m < _o.length; _m++) {
349
+ var ch = _o[_m];
314
350
  switch (ch.kind) {
315
351
  default:
316
352
  // console.log("Unknown assignment:", ts.SyntaxKind[ch.kind]);
317
353
  break;
318
354
  case ts.SyntaxKind.NewExpression:
319
- assignment = " = " + getTypeForAssignment(ch);
355
+ assignment = " = " + getTypeForAssignment(ch, typeString);
320
356
  break;
321
357
  case ts.SyntaxKind.FalseKeyword:
322
358
  case ts.SyntaxKind.TrueKeyword:
@@ -356,10 +392,18 @@ function run(program, outputDir, sourceFile) {
356
392
  // assignment = " = new object[0]";
357
393
  shouldCommentTheLine = true;
358
394
  }
395
+ context.onBeforeField(varName);
359
396
  if (allowDebugLogs)
360
397
  console.log("EMIT member: \"" + typeString + "\" " + varName, assignment, "Last type found:", lastTypeFound);
361
398
  var prefix = shouldCommentTheLine ? "// " : "";
399
+ var isUnityEditorType = typeString === null || typeString === void 0 ? void 0 : typeString.includes("UnityEditor");
400
+ if (isUnityEditorType) {
401
+ context.appendLine("#if UNITY_EDITOR");
402
+ }
362
403
  context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";" + postFix + "\n");
404
+ if (isUnityEditorType) {
405
+ context.appendLine("#endif");
406
+ }
363
407
  lastTypeFound = null;
364
408
  if (requireEndIf) {
365
409
  context.appendLine("#endif");
@@ -367,15 +411,16 @@ function run(program, outputDir, sourceFile) {
367
411
  break;
368
412
  case ts.SyntaxKind.ClassDeclaration:
369
413
  serializeField = false;
414
+ dontSerialize = false;
370
415
  var dec = node;
371
416
  // a class must inherit a component
372
417
  var inheritsComponent = testInheritsComponent(node);
373
418
  if (!dontExportNextClass && (lastTypeFound || exportNextClass || inheritsComponent)) {
374
419
  resetExportNextClass();
375
- var name_2 = (_d = dec.name) === null || _d === void 0 ? void 0 : _d.escapedText;
420
+ var name_2 = (_g = dec.name) === null || _g === void 0 ? void 0 : _g.escapedText;
376
421
  if (allowDebugLogs)
377
422
  console.log("Found class: ", name_2);
378
- var namespace = (_e = tryParseNamespace(node)) !== null && _e !== void 0 ? _e : "Needle.Typescript.GeneratedComponents";
423
+ var namespace = (_h = tryParseNamespace(node)) !== null && _h !== void 0 ? _h : "Needle.Typescript.GeneratedComponents";
379
424
  if (allowDebugLogs)
380
425
  console.log("NAMESPACE", namespace);
381
426
  var newContext = new ExportContext(outputDir, name_2 + ".cs");
@@ -396,8 +441,8 @@ function run(program, outputDir, sourceFile) {
396
441
  console.log(name_2 + " inherits " + typeName_1);
397
442
  var modifiers = "";
398
443
  if (dec.modifiers) {
399
- for (var _l = 0, _m = dec.modifiers; _l < _m.length; _l++) {
400
- var mod = _m[_l];
444
+ for (var _p = 0, _q = dec.modifiers; _p < _q.length; _p++) {
445
+ var mod = _q[_p];
401
446
  switch (mod.getText()) {
402
447
  case "abstract":
403
448
  modifiers += " abstract";
@@ -408,6 +453,10 @@ function run(program, outputDir, sourceFile) {
408
453
  }
409
454
  }
410
455
  modifiers += " partial";
456
+ // If it's a custom class and decorated with @type object then we want to make it serializable
457
+ if (typeName_1 === "object") {
458
+ newContext.appendLine("[System.Serializable]");
459
+ }
411
460
  newContext.appendLine("public " + modifiers.trim() + " class " + name_2 + " : " + typeName_1);
412
461
  newContext.appendLine("{");
413
462
  newContext.indentLevel += 1;
@@ -416,11 +465,27 @@ function run(program, outputDir, sourceFile) {
416
465
  }
417
466
  else {
418
467
  if (allowDebugLogs)
419
- console.log("Class type is unknown and will not generate a component: ", (_f = dec.name) === null || _f === void 0 ? void 0 : _f.escapedText);
468
+ console.log("Class type is unknown and will not generate a component: ", (_j = dec.name) === null || _j === void 0 ? void 0 : _j.escapedText);
420
469
  }
421
470
  lastTypeFound = null;
422
471
  break;
423
472
  }
473
+ function testIsMarkedWithSerializable(node) {
474
+ if (node.kind === ts.SyntaxKind.Decorator) {
475
+ // If a field is decorated with @serializable then we want to serialize it
476
+ var name_3 = node.getText();
477
+ if (name_3.startsWith("@serializable(")) {
478
+ // if(allowDebugLogs) console.log("Found @serializable decorator");
479
+ return true;
480
+ }
481
+ }
482
+ for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
483
+ var ch = _a[_i];
484
+ if (testIsMarkedWithSerializable(ch))
485
+ return true;
486
+ }
487
+ return false;
488
+ }
424
489
  function testInheritsComponent(node) {
425
490
  switch (node.kind) {
426
491
  case ts.SyntaxKind.ClassDeclaration:
@@ -449,7 +514,7 @@ function run(program, outputDir, sourceFile) {
449
514
  }
450
515
  return false;
451
516
  }
452
- function getTypeForAssignment(node) {
517
+ function getTypeForAssignment(node, typeString) {
453
518
  // console.log("-------------------\nAssign", ts.SyntaxKind[node.kind]);
454
519
  switch (node.kind) {
455
520
  case ts.SyntaxKind.FirstLiteralToken:
@@ -460,7 +525,8 @@ function run(program, outputDir, sourceFile) {
460
525
  for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
461
526
  var ch = _a[_i];
462
527
  var text = ch.getText();
463
- // console.log("child", ts.SyntaxKind[ch.kind], text);
528
+ if (allowDebugLogs)
529
+ console.log("child", ts.SyntaxKind[ch.kind], text);
464
530
  switch (ch.kind) {
465
531
  case ts.SyntaxKind.Identifier:
466
532
  case ts.SyntaxKind.PropertyAccessExpression:
@@ -471,7 +537,7 @@ function run(program, outputDir, sourceFile) {
471
537
  var arg = _c[_b];
472
538
  if (args === undefined)
473
539
  args = "";
474
- var res = getTypeForAssignment(arg);
540
+ var res = getTypeForAssignment(arg, typeString);
475
541
  // handle floats being assigned with "f" suffix
476
542
  if (Number.parseFloat(res) >= 0) {
477
543
  args += res + "f";
@@ -487,8 +553,12 @@ function run(program, outputDir, sourceFile) {
487
553
  }
488
554
  if (!args)
489
555
  args = "";
490
- if (type) {
491
- console.log(type, args);
556
+ if (type || typeString) {
557
+ if (typeString) {
558
+ if (allowDebugLogs)
559
+ console.log("Override type", type, typeString);
560
+ type = typeString;
561
+ }
492
562
  return "new " + type + "(" + args + ")";
493
563
  }
494
564
  // const expType = node.getChildren().find(c => c.kind === ts.SyntaxKind.Identifier);
@@ -499,16 +569,24 @@ function run(program, outputDir, sourceFile) {
499
569
  console.log("Unknown assignment:", str, ts.SyntaxKind[node.kind]);
500
570
  return str;
501
571
  }
502
- function isPublic(node) {
572
+ function shouldEmitMethod(node) {
503
573
  if (node.kind === ts.SyntaxKind.PublicKeyword) {
504
574
  return true;
505
575
  }
506
576
  else if (node.kind === ts.SyntaxKind.PrivateKeyword || node.kind === ts.SyntaxKind.ProtectedKeyword) {
507
577
  return false;
508
578
  }
579
+ // check if its static
580
+ else if (node.kind === ts.SyntaxKind.StaticKeyword) {
581
+ return false;
582
+ }
583
+ // check if its abstract
584
+ else if (node.kind === ts.SyntaxKind.AbstractKeyword) {
585
+ return false;
586
+ }
509
587
  for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
510
588
  var ch = _a[_i];
511
- if (!isPublic(ch))
589
+ if (!shouldEmitMethod(ch))
512
590
  return false;
513
591
  }
514
592
  return true;
@@ -574,8 +652,30 @@ function run(program, outputDir, sourceFile) {
574
652
  typeName = varDec.type.getText();
575
653
  }
576
654
  var res = undefined;
655
+ // if it's a { x: number } inline object type:
656
+ if (typeName.startsWith("{")) {
657
+ // check if it has XYZW we assume it's a vector
658
+ var hasX = typeName.includes("x");
659
+ var hasY = typeName.includes("y");
660
+ var hasZ = typeName.includes("z");
661
+ var hasW = typeName.includes("w");
662
+ if (hasX && hasY && hasZ && hasW) {
663
+ return tryGetTypeFromText("Vector4");
664
+ }
665
+ else if (hasX && hasY && hasZ) {
666
+ return tryGetTypeFromText("Vector3");
667
+ }
668
+ else if (hasX && hasY) {
669
+ return tryGetTypeFromText("Vector2");
670
+ }
671
+ return "object";
672
+ }
673
+ // const kindName = ts.SyntaxKind[node.kind];
577
674
  // console.log("Unknown type: " + typeName);
578
675
  switch (node.kind) {
676
+ case ts.SyntaxKind.ObjectBindingPattern:
677
+ res = "object";
678
+ break;
579
679
  // case ts.SyntaxKind.SyntaxList:
580
680
  // const list = node as ts.SyntaxList;
581
681
  // for (const ch of list._children) {
@@ -633,8 +733,14 @@ function run(program, outputDir, sourceFile) {
633
733
  var isInGenericDeclaration = false;
634
734
  for (var _b = 0, _c = node.getChildren(); _b < _c.length; _b++) {
635
735
  var child = _c[_b];
636
- // if (res !== undefined) break;
637
- // console.log("Child type: " + ts.SyntaxKind[child.kind]);
736
+ // Fix https://linear.app/needle/issue/NE-4423
737
+ if (child.kind === ts.SyntaxKind.Block) {
738
+ if (allowDebugLogs)
739
+ console.log("Skip block");
740
+ continue;
741
+ }
742
+ if (allowDebugLogs)
743
+ console.log("Child type: " + ts.SyntaxKind[child.kind]);
638
744
  var isGenericStart = false;
639
745
  var isAssignment = false;
640
746
  switch (child.kind) {
@@ -693,11 +799,17 @@ if (process) {
693
799
  var outputDir_1 = process.argv[2];
694
800
  var fileNames = process.argv.slice(3);
695
801
  fileNames.forEach(function (fileName) {
696
- if (!fs.existsSync(fileName)) {
802
+ try {
803
+ if (!fs.existsSync(fileName)) {
804
+ console.error("File not found: " + fileName);
805
+ }
806
+ else {
807
+ var code = (0, fs_1.readFileSync)(fileName).toString();
808
+ compile(code, fileName, outputDir_1);
809
+ }
697
810
  }
698
- else {
699
- var code = (0, fs_1.readFileSync)(fileName).toString();
700
- compile(code, fileName, outputDir_1);
811
+ catch (e) {
812
+ console.error(e);
701
813
  }
702
814
  });
703
815
  }
@@ -37,6 +37,7 @@ var TYPE_MAP = {
37
37
  "GameObject": "UnityEngine.GameObject",
38
38
  "Object3D": "UnityEngine.Transform",
39
39
  "Transform": "UnityEngine.Transform",
40
+ "RectTransform": "UnityEngine.RectTransform",
40
41
  "AssetReference": "UnityEngine.Transform",
41
42
  "THREE.Object3D": "UnityEngine.Transform",
42
43
  // Math types
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@needle-tools/needle-component-compiler",
3
- "version": "3.0.0-alpha",
3
+ "version": "3.0.0-alpha.1",
4
4
  "description": "Compile Editor components for Needle Engine for C# and Blender",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "tsc": "tsc",
8
8
  "dev": "npm-watch compile",
9
9
  "compile": "tsc",
10
+ "prepublishOnly": "npm run compile",
10
11
  "test": "npm run test:csharp && npm run test:blender",
11
12
  "test:csharp": "mocha -r ts-node/register 'test/csharp/**/*.test.ts'",
12
13
  "test:blender": "mocha -r ts-node/register 'test/blender/**/*.test.ts'",
@@ -51,5 +52,9 @@
51
52
  "repository": {
52
53
  "type": "git",
53
54
  "url": "git+https://github.com/needle-tools/needle-tiny-component-compiler.git"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public",
58
+ "registry": "https://registry.npmjs.org/"
54
59
  }
55
60
  }