@needle-tools/needle-component-compiler 1.2.1 → 1.5.0

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,16 @@ 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
+ ## [1.5.0] - 2022-07-07
8
+ - change ``@type`` annotation to work with and without braces (e.g. ``@type My.Type`` or ``@type (My.Type)``)
9
+
10
+ ## [1.4.0] - 2022-07-06
11
+ - add CODEGEN_START and END sections to allow adding code before or after a generated componnet
12
+ - add ``@ifdef`` to ifdef fields
13
+
14
+ ## [1.3.0] - 2022-06-27
15
+ - add support for typescript namespace
16
+
7
17
  ## [1.2.1] - 2022-06-15
8
18
  - allow adding ``@type(MyNamespace.MyType)`` to class declaration
9
19
 
package/Readme.md CHANGED
@@ -11,4 +11,5 @@ Please run ``npm install`` first before using.
11
11
  - ``@dont-generate-component`` add before class to skip generating a component
12
12
  - ``@generate-component`` to enforce generating a component (not required)
13
13
  - ``@serializeField`` field decorator, similar to ``[SerializeField]``
14
- - ``@type(MyNamespace.MyType)`` field decorator, specifiy C# type of field
14
+ - ``@type MyNamespace.MyType`` decorator for fields or classes, specifiy C# type of field or class
15
+ - ``@ifdef MY_IFDEF`` field decorator only at the moment
package/dist/MyClass.cs CHANGED
@@ -1,23 +1,16 @@
1
- // auto generated code - do not edit
2
- namespace Needle.Typescript.GeneratedComponents
1
+ // NEEDLE_CODEGEN_START
2
+ // auto generated code - do not edit directly
3
+
4
+ #pragma warning disable
5
+
6
+ namespace Hello.World.Deep
3
7
  {
4
- // source: C:\git\needle-tiny-playground\projects\Compiled_Export\myProject\src\scripts\MyClass.ts
5
- public class MyClass : UnityEngine.MonoBehaviour
8
+ public partial class MyClass : UnityEngine.MonoBehaviour
6
9
  {
7
- public void start(){}
8
- public float myFloat = 15;
9
- public bool myBool;
10
- // just some default values
11
- public float[] myArray = new float[]{ 1, 2, 3 };
12
- // comment for myString
13
- public string myString = "this is a string";
14
- public UnityEngine.Transform myObject;
15
- public bool myBool2;
16
- public void myFunction(){}
17
- public void myFunctionWithStringParameter(string @string){}
18
- public void myFunctionWithSomeObjectAndArray(UnityEngine.Transform @obj, float[] @arr){}
19
- public void myFunctionWithoutParamTypes(object @test){}
20
- public UnityEngine.Transform[] someOtherStuff;
21
- public UnityEngine.Events.UnityEvent myEvent;
10
+ #ifdef TEST
11
+ public float @myFloat;
12
+ #endif
22
13
  }
23
14
  }
15
+
16
+ // NEEDLE_CODEGEN_END
@@ -0,0 +1,17 @@
1
+
2
+ // I can edit this
3
+
4
+
5
+ // NEEDLE_CODEGEN_START
6
+ // auto generated code - do not edit directly
7
+
8
+ #pragma warning disable
9
+
10
+ namespace Needle.Typescript.GeneratedComponents
11
+ {
12
+ public partial class OtherClass : UnityEngine.MonoBehaviour
13
+ {
14
+ }
15
+ }
16
+
17
+ // NEEDLE_CODEGEN_END
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/needle-component-compiler",
3
- "version": "1.2.1",
3
+ "version": "1.5.0",
4
4
  "description": "Compile mock unity components from typescript",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -11,7 +11,10 @@ var exportNextClassCommand = "@generate-component";
11
11
  var dontExportNextClassCommand = "@dont-generate-component";
12
12
  // add above field to add [SerializeField] attribute
13
13
  var serializeCommand = "@serializeField";
14
- var typePattern = new RegExp("@type ?\((?<type>.+)\)");
14
+ var typePattern = new RegExp("@type ?\(?(?<type>[\w\.]+)\)?");
15
+ var ifdefPattern = new RegExp("@ifdef ?(?<ifdef>.+)");
16
+ var CODEGEN_MARKER_START = "// NEEDLE_CODEGEN_START";
17
+ var CODEGEN_MARKER_END = "// NEEDLE_CODEGEN_END";
15
18
  // will be set to true when e.g. a comment for export is found
16
19
  var exportNextClass = false;
17
20
  var dontExportNextClass = false;
@@ -51,10 +54,8 @@ var ExportContext = /** @class */ (function () {
51
54
  if (this.textBuffer.length <= 0)
52
55
  return;
53
56
  var dir = this.outputDir + "/";
54
- if (!fs.existsSync(dir)) {
55
- fs.mkdirSync(dir);
56
- }
57
57
  var path = dir + this.fileName;
58
+ this.replaceGeneratedCodeSection(path);
58
59
  console.log("Write to " + path);
59
60
  fs.writeFileSync(path, this.textBuffer);
60
61
  this.reset();
@@ -63,10 +64,25 @@ var ExportContext = /** @class */ (function () {
63
64
  this.textBuffer = "";
64
65
  this.classEnd = -1;
65
66
  };
67
+ ExportContext.prototype.replaceGeneratedCodeSection = function (path) {
68
+ this.textBuffer = CODEGEN_MARKER_START + "\n" + this.textBuffer + "\n" + CODEGEN_MARKER_END;
69
+ if (fs.existsSync(path)) {
70
+ var existing = fs.readFileSync(path, "utf8");
71
+ var regex = new RegExp("(?<before>.*?)\/\/ ?NEEDLE_CODEGEN_START.+\/\/ ?NEEDLE_CODEGEN_END(?<after>.*)", "s");
72
+ var matches = regex.exec(existing);
73
+ if (matches === null || matches === void 0 ? void 0 : matches.groups) {
74
+ console.log("Found codegen sections");
75
+ var before = matches.groups.before;
76
+ var after = matches.groups.after;
77
+ this.textBuffer = before + this.textBuffer + after;
78
+ }
79
+ }
80
+ };
66
81
  return ExportContext;
67
82
  }());
68
83
  var contexts = [];
69
84
  var lastTypeFound = null;
85
+ var ifdefSections = [];
70
86
  function run(program, outputDir, sourceFile) {
71
87
  if (!fs.existsSync(outputDir)) {
72
88
  console.error("Output directory does not exist: \"" + outputDir + "\"");
@@ -78,7 +94,7 @@ function run(program, outputDir, sourceFile) {
78
94
  ts.forEachChild(node, traverseFile);
79
95
  }
80
96
  function visit(node) {
81
- var _a, _b, _c, _d;
97
+ var _a, _b, _c, _d, _e;
82
98
  var context = contexts.length > 0 ? contexts[contexts.length - 1] : null;
83
99
  if (context) {
84
100
  if ((context === null || context === void 0 ? void 0 : context.classEnd) > 0 && node.pos >= (context === null || context === void 0 ? void 0 : context.classEnd)) {
@@ -114,18 +130,33 @@ function run(program, outputDir, sourceFile) {
114
130
  exportNextClass = true;
115
131
  if (comment.includes(dontExportNextClassCommand))
116
132
  dontExportNextClass = true;
117
- var match = typePattern.exec(comment);
118
- if (match && match.groups) {
133
+ var typeMatch = typePattern.exec(comment);
134
+ if (typeMatch && typeMatch.groups) {
119
135
  // for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
120
136
  // so we remove them
121
- var type = match.groups["type"];
137
+ var type = typeMatch.groups["type"];
122
138
  type = type.replace(/\(/, "").replace(/\)/, "");
123
139
  console.log("found type: ", type);
124
140
  lastTypeFound = type;
125
141
  }
142
+ var ifdefMatch = ifdefPattern.exec(comment);
143
+ if (ifdefMatch && ifdefMatch.groups) {
144
+ var ifdef = ifdefMatch.groups["ifdef"];
145
+ if (ifdef)
146
+ ifdefSections.push(ifdef);
147
+ }
126
148
  }
127
149
  }
128
150
  switch (node.kind) {
151
+ // Namespace
152
+ // case ts.SyntaxKind.ModuleDeclaration:
153
+ // const mod = node as ts.ModuleDeclaration;
154
+ // console.log(ts.SyntaxKind[mod.getChildAt(1).kind])
155
+ // const type = mod.getChildAt(1) as ts.Identifier;
156
+ // console.log("MODULE", type.text)
157
+ // break;
158
+ // case ts.SyntaxKind.Identifier:
159
+ // break;
129
160
  // case ts.SyntaxKind.ClassDeclaration:
130
161
  // case ts.SyntaxKind.SingleLineCommentTrivia:
131
162
  // console.log("comment: " + node.getText())
@@ -143,8 +174,8 @@ function run(program, outputDir, sourceFile) {
143
174
  // const isCoroutine = func.asteriskToken;
144
175
  if (meth.name) {
145
176
  var paramsStr = "";
146
- for (var _e = 0, _f = meth.parameters; _e < _f.length; _e++) {
147
- var param = _f[_e];
177
+ for (var _f = 0, _g = meth.parameters; _f < _g.length; _f++) {
178
+ var param = _g[_f];
148
179
  if (!param || !param.name)
149
180
  continue;
150
181
  if (paramsStr.length > 0)
@@ -177,8 +208,8 @@ function run(program, outputDir, sourceFile) {
177
208
  var prefix = typeString === undefined ? "// " : "";
178
209
  var assignment = "";
179
210
  if (typeString !== undefined) {
180
- for (var _g = 0, _h = node.getChildren(); _g < _h.length; _g++) {
181
- var ch = _h[_g];
211
+ for (var _h = 0, _j = node.getChildren(); _h < _j.length; _h++) {
212
+ var ch = _j[_h];
182
213
  switch (ch.kind) {
183
214
  case ts.SyntaxKind.FalseKeyword:
184
215
  case ts.SyntaxKind.TrueKeyword:
@@ -208,8 +239,16 @@ function run(program, outputDir, sourceFile) {
208
239
  console.log("SERIALIZE");
209
240
  context.appendLine("[UnityEngine.SerializeField]");
210
241
  }
242
+ var requireEndIf = false;
243
+ if (ifdefSections.length > 0) {
244
+ requireEndIf = true;
245
+ context.appendLine("#ifdef " + ifdefSections.pop());
246
+ }
211
247
  context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";\n");
212
248
  lastTypeFound = null;
249
+ if (requireEndIf) {
250
+ context.appendLine("#endif");
251
+ }
213
252
  break;
214
253
  case ts.SyntaxKind.ClassDeclaration:
215
254
  serializeField = false;
@@ -219,12 +258,14 @@ function run(program, outputDir, sourceFile) {
219
258
  resetExportNextClass();
220
259
  var name_2 = (_d = dec.name) === null || _d === void 0 ? void 0 : _d.escapedText;
221
260
  console.log("Found class: ", name_2);
261
+ var namespace = (_e = tryParseNamespace(node)) !== null && _e !== void 0 ? _e : "Needle.Typescript.GeneratedComponents";
262
+ console.log("NAMESPACE", namespace);
222
263
  var newContext = new ExportContext(outputDir, name_2 + ".cs");
223
264
  newContext.appendLine("// auto generated code - do not edit directly");
224
265
  newContext.appendLine("");
225
266
  newContext.appendLine("#pragma warning disable");
226
267
  newContext.appendLine("");
227
- newContext.appendLine("namespace Needle.Typescript.GeneratedComponents");
268
+ newContext.appendLine("namespace " + namespace);
228
269
  newContext.appendLine("{");
229
270
  newContext.indentLevel += 1;
230
271
  // newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
@@ -277,6 +318,31 @@ function run(program, outputDir, sourceFile) {
277
318
  }
278
319
  return true;
279
320
  }
321
+ function tryParseNamespace(node, namespace) {
322
+ // console.log("TRAVERSE - " + ts.SyntaxKind[node.kind]);
323
+ switch (node.kind) {
324
+ case ts.SyntaxKind.ModuleDeclaration:
325
+ for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) {
326
+ var ch = _a[_i];
327
+ // console.log("-- TRAVERSE - " + ts.SyntaxKind[ch.kind]);
328
+ switch (ch.kind) {
329
+ case ts.SyntaxKind.Identifier:
330
+ var id = ch;
331
+ if (id.text) {
332
+ if (!namespace)
333
+ namespace = "";
334
+ namespace = id.text + (namespace ? "." : "") + namespace;
335
+ }
336
+ break;
337
+ }
338
+ }
339
+ break;
340
+ }
341
+ if (node.parent) {
342
+ return tryParseNamespace(node.parent, namespace);
343
+ }
344
+ return namespace;
345
+ }
280
346
  function tryResolveTypeRecursive(node) {
281
347
  var _a;
282
348
  if (!node)
@@ -4,6 +4,7 @@ import * as fs from "fs";
4
4
  import * as path from "path";
5
5
 
6
6
  import * as types from "./types";
7
+ import { traceDeprecation } from "process";
7
8
  const dict = types.dict;
8
9
 
9
10
  // add either of these two comments above a class to enforce code gen or disable it for the next class
@@ -11,7 +12,12 @@ const exportNextClassCommand = "@generate-component";
11
12
  const dontExportNextClassCommand = "@dont-generate-component";
12
13
  // add above field to add [SerializeField] attribute
13
14
  const serializeCommand = "@serializeField";
14
- const typePattern = new RegExp("@type ?\((?<type>.+)\)");
15
+ // https://regex101.com/r/ltpcKT/1
16
+ const typePattern = new RegExp("@type ?\(?(?<type>[\w\.]+)\)?");
17
+ const ifdefPattern = new RegExp("@ifdef ?(?<ifdef>.+)")
18
+
19
+ const CODEGEN_MARKER_START = "// NEEDLE_CODEGEN_START";
20
+ const CODEGEN_MARKER_END = "// NEEDLE_CODEGEN_END";
15
21
 
16
22
  // will be set to true when e.g. a comment for export is found
17
23
  let exportNextClass: boolean = false;
@@ -64,10 +70,8 @@ class ExportContext {
64
70
  flush() {
65
71
  if (this.textBuffer.length <= 0) return;
66
72
  const dir = this.outputDir + "/";
67
- if (!fs.existsSync(dir)) {
68
- fs.mkdirSync(dir);
69
- }
70
73
  const path = dir + this.fileName;
74
+ this.replaceGeneratedCodeSection(path);
71
75
  console.log("Write to " + path);
72
76
  fs.writeFileSync(path, this.textBuffer);
73
77
  this.reset();
@@ -77,11 +81,27 @@ class ExportContext {
77
81
  this.textBuffer = "";
78
82
  this.classEnd = -1;
79
83
  }
84
+
85
+ private replaceGeneratedCodeSection(path: string) {
86
+ this.textBuffer = CODEGEN_MARKER_START + "\n" + this.textBuffer + "\n" + CODEGEN_MARKER_END;
87
+ if (fs.existsSync(path)) {
88
+ const existing = fs.readFileSync(path, "utf8");
89
+ const regex = new RegExp("(?<before>.*?)\/\/ ?NEEDLE_CODEGEN_START.+\/\/ ?NEEDLE_CODEGEN_END(?<after>.*)", "s");
90
+ const matches = regex.exec(existing);
91
+ if (matches?.groups) {
92
+ console.log("Found codegen sections")
93
+ const before = matches.groups.before;
94
+ const after = matches.groups.after;
95
+ this.textBuffer = before + this.textBuffer + after;
96
+ }
97
+ }
98
+ }
80
99
  }
81
100
 
82
101
  const contexts: ExportContext[] = [];
83
102
 
84
103
  let lastTypeFound: string | null = null;
104
+ let ifdefSections: string[] = [];
85
105
 
86
106
  export function run(program: ts.Program, outputDir: string, sourceFile: ts.SourceFile) {
87
107
 
@@ -135,19 +155,35 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
135
155
  exportNextClass = true;
136
156
  if (comment.includes(dontExportNextClassCommand))
137
157
  dontExportNextClass = true;
138
- const match = typePattern.exec(comment);
139
- if (match && match.groups) {
158
+ const typeMatch = typePattern.exec(comment);
159
+ if (typeMatch && typeMatch.groups) {
140
160
  // for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
141
161
  // so we remove them
142
- let type = match.groups["type"];
162
+ let type = typeMatch.groups["type"];
143
163
  type = type.replace(/\(/, "").replace(/\)/, "");
144
164
  console.log("found type: ", type);
145
165
  lastTypeFound = type;
146
166
  }
167
+
168
+ const ifdefMatch = ifdefPattern.exec(comment);
169
+ if (ifdefMatch && ifdefMatch.groups) {
170
+ const ifdef = ifdefMatch.groups["ifdef"];
171
+ if (ifdef)
172
+ ifdefSections.push(ifdef);
173
+ }
147
174
  }
148
175
  }
149
176
 
150
177
  switch (node.kind) {
178
+ // Namespace
179
+ // case ts.SyntaxKind.ModuleDeclaration:
180
+ // const mod = node as ts.ModuleDeclaration;
181
+ // console.log(ts.SyntaxKind[mod.getChildAt(1).kind])
182
+ // const type = mod.getChildAt(1) as ts.Identifier;
183
+ // console.log("MODULE", type.text)
184
+ // break;
185
+ // case ts.SyntaxKind.Identifier:
186
+ // break;
151
187
  // case ts.SyntaxKind.ClassDeclaration:
152
188
  // case ts.SyntaxKind.SingleLineCommentTrivia:
153
189
  // console.log("comment: " + node.getText())
@@ -224,8 +260,16 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
224
260
  console.log("SERIALIZE");
225
261
  context.appendLine("[UnityEngine.SerializeField]");
226
262
  }
263
+ let requireEndIf = false;
264
+ if(ifdefSections.length > 0){
265
+ requireEndIf = true;
266
+ context.appendLine("#ifdef " + ifdefSections.pop());
267
+ }
227
268
  context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";\n");
228
269
  lastTypeFound = null;
270
+ if(requireEndIf){
271
+ context.appendLine("#endif");
272
+ }
229
273
  break;
230
274
 
231
275
  case ts.SyntaxKind.ClassDeclaration:
@@ -236,12 +280,14 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
236
280
  resetExportNextClass();
237
281
  const name = dec.name?.escapedText;
238
282
  console.log("Found class: ", name);
283
+ const namespace = tryParseNamespace(node) ?? "Needle.Typescript.GeneratedComponents";
284
+ console.log("NAMESPACE", namespace);
239
285
  const newContext = new ExportContext(outputDir, name + ".cs");
240
286
  newContext.appendLine("// auto generated code - do not edit directly");
241
287
  newContext.appendLine("");
242
288
  newContext.appendLine("#pragma warning disable");
243
289
  newContext.appendLine("");
244
- newContext.appendLine("namespace Needle.Typescript.GeneratedComponents");
290
+ newContext.appendLine("namespace " + namespace);
245
291
  newContext.appendLine("{");
246
292
  newContext.indentLevel += 1;
247
293
  // newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
@@ -291,6 +337,30 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
291
337
  return true;
292
338
  }
293
339
 
340
+ function tryParseNamespace(node: ts.Node, namespace?: string): string | null | undefined {
341
+ // console.log("TRAVERSE - " + ts.SyntaxKind[node.kind]);
342
+ switch (node.kind) {
343
+ case ts.SyntaxKind.ModuleDeclaration:
344
+ for (const ch of node.getChildren()) {
345
+ // console.log("-- TRAVERSE - " + ts.SyntaxKind[ch.kind]);
346
+ switch (ch.kind) {
347
+ case ts.SyntaxKind.Identifier:
348
+ const id = ch as ts.Identifier;
349
+ if (id.text) {
350
+ if (!namespace) namespace = "";
351
+ namespace = id.text + (namespace ? "." : "") + namespace;
352
+ }
353
+ break;
354
+ }
355
+ }
356
+ break;
357
+ }
358
+ if (node.parent) {
359
+ return tryParseNamespace(node.parent, namespace);
360
+ }
361
+ return namespace;
362
+ }
363
+
294
364
  function tryResolveTypeRecursive(node: ts.Node | ts.VariableDeclaration): string | undefined {
295
365
  if (!node) return undefined;
296
366
 
package/src/test.ts CHANGED
@@ -1,27 +1,42 @@
1
1
 
2
- import { Behaviour } from "needle.tiny.engine/engine-components/Component";
3
- import { RoomEntity } from "./Room";
2
+ // import { Behaviour } from "needle.tiny.engine/engine-components/Component";
3
+ // import { RoomEntity } from "./Room";
4
+
5
+
6
+ namespace Hello.World
7
+ {
8
+ namespace Deep {
9
+ export class MyClass extends Behaviour {
10
+ //@ifdef TEST
11
+ public myFloat :number;
12
+ }
13
+ }
14
+ }
15
+
16
+ class OtherClass extends Behaviour {
17
+
18
+ }
4
19
 
5
20
  //@type (RoomEntity)
6
- export class NavigationManager extends RoomEntity {
21
+ // export class NavigationManager extends RoomEntity {
7
22
 
8
- fl:number = 1;
23
+ // fl:number = 1;
9
24
 
10
- nav_forward() {
25
+ // nav_forward() {
11
26
 
12
- }
27
+ // }
13
28
 
14
- nav_backward() {
29
+ // nav_backward() {
15
30
 
16
- }
17
- }
31
+ // }
32
+ // }
18
33
 
19
- export abstract class NavComponent extends Behaviour {
34
+ // export abstract class NavComponent extends Behaviour {
20
35
 
21
- abstract next();
22
- abstract prev();
23
- abstract isAtEnd():boolean;
24
- }
36
+ // abstract next();
37
+ // abstract prev();
38
+ // abstract isAtEnd():boolean;
39
+ // }
25
40
 
26
41
 
27
42
  // export class PointOfInterest extends Behaviour {