@needle-tools/needle-component-compiler 1.2.0 → 1.4.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,7 +4,14 @@ 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.1.0] - 2022-06-15
7
+ ## [1.3.0] - 2022-07-06
8
+ - add CODEGEN_START and END sections to allow adding code before or after a generated componnet
9
+ - add ``@ifdef`` to ifdef fields
10
+
11
+ ## [1.3.0] - 2022-06-27
12
+ - add support for typescript namespace
13
+
14
+ ## [1.2.1] - 2022-06-15
8
15
  - allow adding ``@type(MyNamespace.MyType)`` to class declaration
9
16
 
10
17
  ## [1.1.0] - 2022-06-13
package/Readme.md CHANGED
@@ -11,4 +11,4 @@ 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
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,13 @@
1
+ // auto generated code - do not edit directly
2
+
3
+ #pragma warning disable
4
+
5
+ namespace Needle.Typescript.GeneratedComponents
6
+ {
7
+ public partial class NavComponent : UnityEngine.MonoBehaviour
8
+ {
9
+ public void next(){}
10
+ public void prev(){}
11
+ public void isAtEnd(){}
12
+ }
13
+ }
@@ -4,7 +4,7 @@
4
4
 
5
5
  namespace Needle.Typescript.GeneratedComponents
6
6
  {
7
- public partial class NavigationManager : RoomEntity
7
+ public partial class NavigationManager : RoomEntity
8
8
  {
9
9
  public float @fl = 1f;
10
10
  public void nav_forward(){}
@@ -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.0",
3
+ "version": "1.4.0",
4
4
  "description": "Compile mock unity components from typescript",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -12,6 +12,9 @@ var dontExportNextClassCommand = "@dont-generate-component";
12
12
  // add above field to add [SerializeField] attribute
13
13
  var serializeCommand = "@serializeField";
14
14
  var typePattern = new RegExp("@type ?\((?<type>.+)\)");
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,21 +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"];
122
- if (type.startsWith("("))
123
- type = type.slice(1);
124
- if (type.endsWith(")"))
125
- type = type.slice(0, -1);
137
+ var type = typeMatch.groups["type"];
138
+ type = type.replace(/\(/, "").replace(/\)/, "");
126
139
  console.log("found type: ", type);
127
140
  lastTypeFound = type;
128
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
+ }
129
148
  }
130
149
  }
131
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;
132
160
  // case ts.SyntaxKind.ClassDeclaration:
133
161
  // case ts.SyntaxKind.SingleLineCommentTrivia:
134
162
  // console.log("comment: " + node.getText())
@@ -146,8 +174,8 @@ function run(program, outputDir, sourceFile) {
146
174
  // const isCoroutine = func.asteriskToken;
147
175
  if (meth.name) {
148
176
  var paramsStr = "";
149
- for (var _e = 0, _f = meth.parameters; _e < _f.length; _e++) {
150
- var param = _f[_e];
177
+ for (var _f = 0, _g = meth.parameters; _f < _g.length; _f++) {
178
+ var param = _g[_f];
151
179
  if (!param || !param.name)
152
180
  continue;
153
181
  if (paramsStr.length > 0)
@@ -180,8 +208,8 @@ function run(program, outputDir, sourceFile) {
180
208
  var prefix = typeString === undefined ? "// " : "";
181
209
  var assignment = "";
182
210
  if (typeString !== undefined) {
183
- for (var _g = 0, _h = node.getChildren(); _g < _h.length; _g++) {
184
- var ch = _h[_g];
211
+ for (var _h = 0, _j = node.getChildren(); _h < _j.length; _h++) {
212
+ var ch = _j[_h];
185
213
  switch (ch.kind) {
186
214
  case ts.SyntaxKind.FalseKeyword:
187
215
  case ts.SyntaxKind.TrueKeyword:
@@ -211,8 +239,16 @@ function run(program, outputDir, sourceFile) {
211
239
  console.log("SERIALIZE");
212
240
  context.appendLine("[UnityEngine.SerializeField]");
213
241
  }
242
+ var requireEndIf = false;
243
+ if (ifdefSections.length > 0) {
244
+ requireEndIf = true;
245
+ context.appendLine("#ifdef " + ifdefSections.pop());
246
+ }
214
247
  context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";\n");
215
248
  lastTypeFound = null;
249
+ if (requireEndIf) {
250
+ context.appendLine("#endif");
251
+ }
216
252
  break;
217
253
  case ts.SyntaxKind.ClassDeclaration:
218
254
  serializeField = false;
@@ -222,12 +258,14 @@ function run(program, outputDir, sourceFile) {
222
258
  resetExportNextClass();
223
259
  var name_2 = (_d = dec.name) === null || _d === void 0 ? void 0 : _d.escapedText;
224
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);
225
263
  var newContext = new ExportContext(outputDir, name_2 + ".cs");
226
264
  newContext.appendLine("// auto generated code - do not edit directly");
227
265
  newContext.appendLine("");
228
266
  newContext.appendLine("#pragma warning disable");
229
267
  newContext.appendLine("");
230
- newContext.appendLine("namespace Needle.Typescript.GeneratedComponents");
268
+ newContext.appendLine("namespace " + namespace);
231
269
  newContext.appendLine("{");
232
270
  newContext.indentLevel += 1;
233
271
  // newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
@@ -280,6 +318,31 @@ function run(program, outputDir, sourceFile) {
280
318
  }
281
319
  return true;
282
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
+ }
283
346
  function tryResolveTypeRecursive(node) {
284
347
  var _a;
285
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
@@ -12,6 +13,10 @@ const dontExportNextClassCommand = "@dont-generate-component";
12
13
  // add above field to add [SerializeField] attribute
13
14
  const serializeCommand = "@serializeField";
14
15
  const typePattern = new RegExp("@type ?\((?<type>.+)\)");
16
+ const ifdefPattern = new RegExp("@ifdef ?(?<ifdef>.+)")
17
+
18
+ const CODEGEN_MARKER_START = "// NEEDLE_CODEGEN_START";
19
+ const CODEGEN_MARKER_END = "// NEEDLE_CODEGEN_END";
15
20
 
16
21
  // will be set to true when e.g. a comment for export is found
17
22
  let exportNextClass: boolean = false;
@@ -64,10 +69,8 @@ class ExportContext {
64
69
  flush() {
65
70
  if (this.textBuffer.length <= 0) return;
66
71
  const dir = this.outputDir + "/";
67
- if (!fs.existsSync(dir)) {
68
- fs.mkdirSync(dir);
69
- }
70
72
  const path = dir + this.fileName;
73
+ this.replaceGeneratedCodeSection(path);
71
74
  console.log("Write to " + path);
72
75
  fs.writeFileSync(path, this.textBuffer);
73
76
  this.reset();
@@ -77,11 +80,27 @@ class ExportContext {
77
80
  this.textBuffer = "";
78
81
  this.classEnd = -1;
79
82
  }
83
+
84
+ private replaceGeneratedCodeSection(path: string) {
85
+ this.textBuffer = CODEGEN_MARKER_START + "\n" + this.textBuffer + "\n" + CODEGEN_MARKER_END;
86
+ if (fs.existsSync(path)) {
87
+ const existing = fs.readFileSync(path, "utf8");
88
+ const regex = new RegExp("(?<before>.*?)\/\/ ?NEEDLE_CODEGEN_START.+\/\/ ?NEEDLE_CODEGEN_END(?<after>.*)", "s");
89
+ const matches = regex.exec(existing);
90
+ if (matches?.groups) {
91
+ console.log("Found codegen sections")
92
+ const before = matches.groups.before;
93
+ const after = matches.groups.after;
94
+ this.textBuffer = before + this.textBuffer + after;
95
+ }
96
+ }
97
+ }
80
98
  }
81
99
 
82
100
  const contexts: ExportContext[] = [];
83
101
 
84
102
  let lastTypeFound: string | null = null;
103
+ let ifdefSections: string[] = [];
85
104
 
86
105
  export function run(program: ts.Program, outputDir: string, sourceFile: ts.SourceFile) {
87
106
 
@@ -135,20 +154,35 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
135
154
  exportNextClass = true;
136
155
  if (comment.includes(dontExportNextClassCommand))
137
156
  dontExportNextClass = true;
138
- const match = typePattern.exec(comment);
139
- if (match && match.groups) {
157
+ const typeMatch = typePattern.exec(comment);
158
+ if (typeMatch && typeMatch.groups) {
140
159
  // for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
141
160
  // so we remove them
142
- let type = match.groups["type"];
143
- if (type.startsWith("(")) type = type.slice(1);
144
- if (type.endsWith(")")) type = type.slice(0, -1);
161
+ let type = typeMatch.groups["type"];
162
+ type = type.replace(/\(/, "").replace(/\)/, "");
145
163
  console.log("found type: ", type);
146
164
  lastTypeFound = type;
147
165
  }
166
+
167
+ const ifdefMatch = ifdefPattern.exec(comment);
168
+ if (ifdefMatch && ifdefMatch.groups) {
169
+ const ifdef = ifdefMatch.groups["ifdef"];
170
+ if (ifdef)
171
+ ifdefSections.push(ifdef);
172
+ }
148
173
  }
149
174
  }
150
175
 
151
176
  switch (node.kind) {
177
+ // Namespace
178
+ // case ts.SyntaxKind.ModuleDeclaration:
179
+ // const mod = node as ts.ModuleDeclaration;
180
+ // console.log(ts.SyntaxKind[mod.getChildAt(1).kind])
181
+ // const type = mod.getChildAt(1) as ts.Identifier;
182
+ // console.log("MODULE", type.text)
183
+ // break;
184
+ // case ts.SyntaxKind.Identifier:
185
+ // break;
152
186
  // case ts.SyntaxKind.ClassDeclaration:
153
187
  // case ts.SyntaxKind.SingleLineCommentTrivia:
154
188
  // console.log("comment: " + node.getText())
@@ -225,8 +259,16 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
225
259
  console.log("SERIALIZE");
226
260
  context.appendLine("[UnityEngine.SerializeField]");
227
261
  }
262
+ let requireEndIf = false;
263
+ if(ifdefSections.length > 0){
264
+ requireEndIf = true;
265
+ context.appendLine("#ifdef " + ifdefSections.pop());
266
+ }
228
267
  context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";\n");
229
268
  lastTypeFound = null;
269
+ if(requireEndIf){
270
+ context.appendLine("#endif");
271
+ }
230
272
  break;
231
273
 
232
274
  case ts.SyntaxKind.ClassDeclaration:
@@ -237,12 +279,14 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
237
279
  resetExportNextClass();
238
280
  const name = dec.name?.escapedText;
239
281
  console.log("Found class: ", name);
282
+ const namespace = tryParseNamespace(node) ?? "Needle.Typescript.GeneratedComponents";
283
+ console.log("NAMESPACE", namespace);
240
284
  const newContext = new ExportContext(outputDir, name + ".cs");
241
285
  newContext.appendLine("// auto generated code - do not edit directly");
242
286
  newContext.appendLine("");
243
287
  newContext.appendLine("#pragma warning disable");
244
288
  newContext.appendLine("");
245
- newContext.appendLine("namespace Needle.Typescript.GeneratedComponents");
289
+ newContext.appendLine("namespace " + namespace);
246
290
  newContext.appendLine("{");
247
291
  newContext.indentLevel += 1;
248
292
  // newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
@@ -292,6 +336,30 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
292
336
  return true;
293
337
  }
294
338
 
339
+ function tryParseNamespace(node: ts.Node, namespace?: string): string | null | undefined {
340
+ // console.log("TRAVERSE - " + ts.SyntaxKind[node.kind]);
341
+ switch (node.kind) {
342
+ case ts.SyntaxKind.ModuleDeclaration:
343
+ for (const ch of node.getChildren()) {
344
+ // console.log("-- TRAVERSE - " + ts.SyntaxKind[ch.kind]);
345
+ switch (ch.kind) {
346
+ case ts.SyntaxKind.Identifier:
347
+ const id = ch as ts.Identifier;
348
+ if (id.text) {
349
+ if (!namespace) namespace = "";
350
+ namespace = id.text + (namespace ? "." : "") + namespace;
351
+ }
352
+ break;
353
+ }
354
+ }
355
+ break;
356
+ }
357
+ if (node.parent) {
358
+ return tryParseNamespace(node.parent, namespace);
359
+ }
360
+ return namespace;
361
+ }
362
+
295
363
  function tryResolveTypeRecursive(node: ts.Node | ts.VariableDeclaration): string | undefined {
296
364
  if (!node) return undefined;
297
365
 
package/src/test.ts CHANGED
@@ -1,20 +1,43 @@
1
1
 
2
- // @generate-component
2
+ // import { Behaviour } from "needle.tiny.engine/engine-components/Component";
3
+ // import { RoomEntity } from "./Room";
3
4
 
4
- //@type (RoomEntity)
5
- export class NavigationManager extends RoomEntity {
6
-
7
- fl:number = 1;
8
-
9
- nav_forward() {
10
5
 
6
+ namespace Hello.World
7
+ {
8
+ namespace Deep {
9
+ export class MyClass extends Behaviour {
10
+ //@ifdef TEST
11
+ public myFloat :number;
12
+ }
11
13
  }
14
+ }
12
15
 
13
- nav_backward() {
16
+ class OtherClass extends Behaviour {
14
17
 
15
- }
16
18
  }
17
19
 
20
+ //@type (RoomEntity)
21
+ // export class NavigationManager extends RoomEntity {
22
+
23
+ // fl:number = 1;
24
+
25
+ // nav_forward() {
26
+
27
+ // }
28
+
29
+ // nav_backward() {
30
+
31
+ // }
32
+ // }
33
+
34
+ // export abstract class NavComponent extends Behaviour {
35
+
36
+ // abstract next();
37
+ // abstract prev();
38
+ // abstract isAtEnd():boolean;
39
+ // }
40
+
18
41
 
19
42
  // export class PointOfInterest extends Behaviour {
20
43