@needle-tools/needle-component-compiler 1.0.1 → 1.2.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 +9 -0
- package/Readme.md +8 -1
- package/dist/NavComponent.cs +13 -0
- package/dist/NavigationManager.cs +13 -0
- package/dist/PointOfInterest.cs +14 -0
- package/package.json +1 -1
- package/src/component-compiler.js +35 -19
- package/src/component-compiler.ts +34 -18
- package/src/test.ts +41 -4
package/Changelog.md
CHANGED
|
@@ -4,6 +4,15 @@ 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.2.1] - 2022-06-15
|
|
8
|
+
- allow adding ``@type(MyNamespace.MyType)`` to class declaration
|
|
9
|
+
|
|
10
|
+
## [1.1.0] - 2022-06-13
|
|
11
|
+
- add ``@type(MyNamespace.MyType)`` decorator for fields to specifiy C# type
|
|
12
|
+
- add minimal docs to readme
|
|
13
|
+
- dont emit comments anymore
|
|
14
|
+
- make created classes partial
|
|
15
|
+
|
|
7
16
|
## [1.0.1] - 2022-05-29
|
|
8
17
|
- disable all warnings for generated scripts
|
|
9
18
|
- add mesh and texture types
|
package/Readme.md
CHANGED
|
@@ -4,4 +4,11 @@ Little helper package to transpile typescript files to Unity C# components.
|
|
|
4
4
|
Please run ``npm install`` first before using.
|
|
5
5
|
|
|
6
6
|
### Usage
|
|
7
|
-
``node <path to>/component-compiler.js <output_directory> <path_to/my_script.ts>``
|
|
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]``
|
|
14
|
+
- ``@type(MyNamespace.MyType)`` field decorator, specifiy C# type of field
|
|
@@ -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
|
+
}
|
|
@@ -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 NavigationManager : RoomEntity
|
|
8
|
+
{
|
|
9
|
+
public float @fl = 1f;
|
|
10
|
+
public void nav_forward(){}
|
|
11
|
+
public void nav_backward(){}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// auto generated code - do not edit directly
|
|
2
|
+
|
|
3
|
+
#pragma warning disable
|
|
4
|
+
|
|
5
|
+
namespace Needle.Typescript.GeneratedComponents
|
|
6
|
+
{
|
|
7
|
+
public partial class PointOfInterest : UnityEngine.MonoBehaviour
|
|
8
|
+
{
|
|
9
|
+
public float @myVal = 12f;
|
|
10
|
+
public void myFunction(){}
|
|
11
|
+
public UnityEngine.Camera @view;
|
|
12
|
+
public string @test = "123";
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ 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
15
|
// will be set to true when e.g. a comment for export is found
|
|
15
16
|
var exportNextClass = false;
|
|
16
17
|
var dontExportNextClass = false;
|
|
@@ -65,6 +66,7 @@ var ExportContext = /** @class */ (function () {
|
|
|
65
66
|
return ExportContext;
|
|
66
67
|
}());
|
|
67
68
|
var contexts = [];
|
|
69
|
+
var lastTypeFound = null;
|
|
68
70
|
function run(program, outputDir, sourceFile) {
|
|
69
71
|
if (!fs.existsSync(outputDir)) {
|
|
70
72
|
console.error("Output directory does not exist: \"" + outputDir + "\"");
|
|
@@ -76,16 +78,18 @@ function run(program, outputDir, sourceFile) {
|
|
|
76
78
|
ts.forEachChild(node, traverseFile);
|
|
77
79
|
}
|
|
78
80
|
function visit(node) {
|
|
79
|
-
var _a, _b, _c;
|
|
81
|
+
var _a, _b, _c, _d;
|
|
80
82
|
var context = contexts.length > 0 ? contexts[contexts.length - 1] : null;
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
context.indentLevel
|
|
84
|
-
|
|
83
|
+
if (context) {
|
|
84
|
+
if ((context === null || context === void 0 ? void 0 : context.classEnd) > 0 && node.pos >= (context === null || context === void 0 ? void 0 : context.classEnd)) {
|
|
85
|
+
while (context.indentLevel > 0) {
|
|
86
|
+
context.indentLevel -= 1;
|
|
87
|
+
context.append("}\n");
|
|
88
|
+
}
|
|
89
|
+
context.flush();
|
|
90
|
+
context = null;
|
|
91
|
+
contexts.pop();
|
|
85
92
|
}
|
|
86
|
-
context.flush();
|
|
87
|
-
context = null;
|
|
88
|
-
contexts.pop();
|
|
89
93
|
}
|
|
90
94
|
console.log("\t", ts.SyntaxKind[node.kind]);
|
|
91
95
|
var commentRanges = ts.getLeadingCommentRanges(sourceFile.getFullText(), node.getFullStart());
|
|
@@ -105,13 +109,20 @@ function run(program, outputDir, sourceFile) {
|
|
|
105
109
|
}
|
|
106
110
|
else if (comment.includes(serializeCommand))
|
|
107
111
|
serializeField = true;
|
|
108
|
-
else
|
|
109
|
-
context.appendLine(comment);
|
|
110
112
|
}
|
|
111
113
|
if (comment.includes(exportNextClassCommand))
|
|
112
114
|
exportNextClass = true;
|
|
113
115
|
if (comment.includes(dontExportNextClassCommand))
|
|
114
116
|
dontExportNextClass = true;
|
|
117
|
+
var match = typePattern.exec(comment);
|
|
118
|
+
if (match && match.groups) {
|
|
119
|
+
// for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
|
|
120
|
+
// so we remove them
|
|
121
|
+
var type = match.groups["type"];
|
|
122
|
+
type = type.replace(/\(/, "").replace(/\)/, "");
|
|
123
|
+
console.log("found type: ", type);
|
|
124
|
+
lastTypeFound = type;
|
|
125
|
+
}
|
|
115
126
|
}
|
|
116
127
|
}
|
|
117
128
|
switch (node.kind) {
|
|
@@ -122,6 +133,7 @@ function run(program, outputDir, sourceFile) {
|
|
|
122
133
|
case ts.SyntaxKind.Decorator:
|
|
123
134
|
break;
|
|
124
135
|
case ts.SyntaxKind.MethodDeclaration:
|
|
136
|
+
lastTypeFound = null;
|
|
125
137
|
serializeField = false;
|
|
126
138
|
resetExportNextClass();
|
|
127
139
|
if (!context)
|
|
@@ -131,8 +143,8 @@ function run(program, outputDir, sourceFile) {
|
|
|
131
143
|
// const isCoroutine = func.asteriskToken;
|
|
132
144
|
if (meth.name) {
|
|
133
145
|
var paramsStr = "";
|
|
134
|
-
for (var
|
|
135
|
-
var param = _e
|
|
146
|
+
for (var _e = 0, _f = meth.parameters; _e < _f.length; _e++) {
|
|
147
|
+
var param = _f[_e];
|
|
136
148
|
if (!param || !param.name)
|
|
137
149
|
continue;
|
|
138
150
|
if (paramsStr.length > 0)
|
|
@@ -158,15 +170,15 @@ function run(program, outputDir, sourceFile) {
|
|
|
158
170
|
console.log(name_1);
|
|
159
171
|
if (name_1.startsWith("\"@") || name_1.startsWith("\"$") || name_1.startsWith("$"))
|
|
160
172
|
break;
|
|
161
|
-
var typeString = tryResolveTypeRecursive(node);
|
|
173
|
+
var typeString = lastTypeFound !== null && lastTypeFound !== void 0 ? lastTypeFound : tryResolveTypeRecursive(node);
|
|
162
174
|
if (typeString === undefined) {
|
|
163
175
|
context.append("// Could not resolve type \"" + ((_c = vardec.type) === null || _c === void 0 ? void 0 : _c.getText()) + "\"\n");
|
|
164
176
|
}
|
|
165
177
|
var prefix = typeString === undefined ? "// " : "";
|
|
166
178
|
var assignment = "";
|
|
167
179
|
if (typeString !== undefined) {
|
|
168
|
-
for (var
|
|
169
|
-
var ch = _g
|
|
180
|
+
for (var _g = 0, _h = node.getChildren(); _g < _h.length; _g++) {
|
|
181
|
+
var ch = _h[_g];
|
|
170
182
|
switch (ch.kind) {
|
|
171
183
|
case ts.SyntaxKind.FalseKeyword:
|
|
172
184
|
case ts.SyntaxKind.TrueKeyword:
|
|
@@ -197,17 +209,18 @@ function run(program, outputDir, sourceFile) {
|
|
|
197
209
|
context.appendLine("[UnityEngine.SerializeField]");
|
|
198
210
|
}
|
|
199
211
|
context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";\n");
|
|
212
|
+
lastTypeFound = null;
|
|
200
213
|
break;
|
|
201
214
|
case ts.SyntaxKind.ClassDeclaration:
|
|
202
215
|
serializeField = false;
|
|
203
216
|
var dec = node;
|
|
204
217
|
// a class must inherit a component
|
|
205
|
-
if (!dontExportNextClass && (exportNextClass || testInheritsComponent(node))) {
|
|
218
|
+
if (!dontExportNextClass && (lastTypeFound || exportNextClass || testInheritsComponent(node))) {
|
|
206
219
|
resetExportNextClass();
|
|
207
|
-
var name_2 = dec.name.escapedText;
|
|
220
|
+
var name_2 = (_d = dec.name) === null || _d === void 0 ? void 0 : _d.escapedText;
|
|
208
221
|
console.log("Found class: ", name_2);
|
|
209
222
|
var newContext = new ExportContext(outputDir, name_2 + ".cs");
|
|
210
|
-
newContext.appendLine("// auto generated code - do not edit");
|
|
223
|
+
newContext.appendLine("// auto generated code - do not edit directly");
|
|
211
224
|
newContext.appendLine("");
|
|
212
225
|
newContext.appendLine("#pragma warning disable");
|
|
213
226
|
newContext.appendLine("");
|
|
@@ -215,12 +228,15 @@ function run(program, outputDir, sourceFile) {
|
|
|
215
228
|
newContext.appendLine("{");
|
|
216
229
|
newContext.indentLevel += 1;
|
|
217
230
|
// newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
|
|
218
|
-
|
|
231
|
+
var typeName = lastTypeFound !== null && lastTypeFound !== void 0 ? lastTypeFound : "UnityEngine.MonoBehaviour";
|
|
232
|
+
console.log(name_2 + " inherits " + typeName);
|
|
233
|
+
newContext.appendLine("public partial class " + name_2 + " : " + typeName);
|
|
219
234
|
newContext.appendLine("{");
|
|
220
235
|
newContext.indentLevel += 1;
|
|
221
236
|
newContext.classEnd = dec.end;
|
|
222
237
|
contexts.push(newContext);
|
|
223
238
|
}
|
|
239
|
+
lastTypeFound = null;
|
|
224
240
|
break;
|
|
225
241
|
}
|
|
226
242
|
function testInheritsComponent(node) {
|
|
@@ -11,6 +11,7 @@ const exportNextClassCommand = "@generate-component";
|
|
|
11
11
|
const dontExportNextClassCommand = "@dont-generate-component";
|
|
12
12
|
// add above field to add [SerializeField] attribute
|
|
13
13
|
const serializeCommand = "@serializeField";
|
|
14
|
+
const typePattern = new RegExp("@type ?\((?<type>.+)\)");
|
|
14
15
|
|
|
15
16
|
// will be set to true when e.g. a comment for export is found
|
|
16
17
|
let exportNextClass: boolean = false;
|
|
@@ -80,6 +81,8 @@ class ExportContext {
|
|
|
80
81
|
|
|
81
82
|
const contexts: ExportContext[] = [];
|
|
82
83
|
|
|
84
|
+
let lastTypeFound: string | null = null;
|
|
85
|
+
|
|
83
86
|
export function run(program: ts.Program, outputDir: string, sourceFile: ts.SourceFile) {
|
|
84
87
|
|
|
85
88
|
if (!fs.existsSync(outputDir)) {
|
|
@@ -97,14 +100,16 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
97
100
|
function visit(node: ts.Node) {
|
|
98
101
|
let context: ExportContext | null = contexts.length > 0 ? contexts[contexts.length - 1] : null;
|
|
99
102
|
|
|
100
|
-
if (context
|
|
101
|
-
|
|
102
|
-
context.indentLevel
|
|
103
|
-
|
|
103
|
+
if (context) {
|
|
104
|
+
if (context?.classEnd > 0 && node.pos >= context?.classEnd) {
|
|
105
|
+
while (context.indentLevel > 0) {
|
|
106
|
+
context.indentLevel -= 1;
|
|
107
|
+
context.append("}\n");
|
|
108
|
+
}
|
|
109
|
+
context.flush();
|
|
110
|
+
context = null;
|
|
111
|
+
contexts.pop();
|
|
104
112
|
}
|
|
105
|
-
context.flush();
|
|
106
|
-
context = null;
|
|
107
|
-
contexts.pop();
|
|
108
113
|
}
|
|
109
114
|
console.log("\t", ts.SyntaxKind[node.kind]);
|
|
110
115
|
|
|
@@ -125,13 +130,20 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
125
130
|
}
|
|
126
131
|
else if (comment.includes(serializeCommand))
|
|
127
132
|
serializeField = true;
|
|
128
|
-
else
|
|
129
|
-
context.appendLine(comment);
|
|
130
133
|
}
|
|
131
134
|
if (comment.includes(exportNextClassCommand))
|
|
132
135
|
exportNextClass = true;
|
|
133
136
|
if (comment.includes(dontExportNextClassCommand))
|
|
134
137
|
dontExportNextClass = true;
|
|
138
|
+
const match = typePattern.exec(comment);
|
|
139
|
+
if (match && match.groups) {
|
|
140
|
+
// for some reason our regex does also match surrounding ( ) even tho: https://regex101.com/r/PoWK6V/1
|
|
141
|
+
// so we remove them
|
|
142
|
+
let type = match.groups["type"];
|
|
143
|
+
type = type.replace(/\(/, "").replace(/\)/, "");
|
|
144
|
+
console.log("found type: ", type);
|
|
145
|
+
lastTypeFound = type;
|
|
146
|
+
}
|
|
135
147
|
}
|
|
136
148
|
}
|
|
137
149
|
|
|
@@ -143,6 +155,7 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
143
155
|
case ts.SyntaxKind.Decorator:
|
|
144
156
|
break;
|
|
145
157
|
case ts.SyntaxKind.MethodDeclaration:
|
|
158
|
+
lastTypeFound = null;
|
|
146
159
|
serializeField = false;
|
|
147
160
|
resetExportNextClass();
|
|
148
161
|
if (!context) break;
|
|
@@ -174,7 +187,7 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
174
187
|
const name = vardec.name.getText();
|
|
175
188
|
console.log(name);
|
|
176
189
|
if (name.startsWith("\"@") || name.startsWith("\"$") || name.startsWith("$")) break;
|
|
177
|
-
let typeString = tryResolveTypeRecursive(node);
|
|
190
|
+
let typeString = lastTypeFound ?? tryResolveTypeRecursive(node);
|
|
178
191
|
if (typeString === undefined) {
|
|
179
192
|
context.append("// Could not resolve type \"" + vardec.type?.getText() + "\"\n");
|
|
180
193
|
}
|
|
@@ -212,15 +225,16 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
212
225
|
context.appendLine("[UnityEngine.SerializeField]");
|
|
213
226
|
}
|
|
214
227
|
context.append(prefix + visibility + " " + typeString + " " + varName + assignment + ";\n");
|
|
228
|
+
lastTypeFound = null;
|
|
215
229
|
break;
|
|
216
230
|
|
|
217
231
|
case ts.SyntaxKind.ClassDeclaration:
|
|
218
232
|
serializeField = false;
|
|
219
233
|
const dec = <ts.ClassDeclaration>node;
|
|
220
234
|
// a class must inherit a component
|
|
221
|
-
if (!dontExportNextClass && (exportNextClass || testInheritsComponent(node))) {
|
|
235
|
+
if (!dontExportNextClass && (lastTypeFound || exportNextClass || testInheritsComponent(node))) {
|
|
222
236
|
resetExportNextClass();
|
|
223
|
-
const name = dec.name
|
|
237
|
+
const name = dec.name?.escapedText;
|
|
224
238
|
console.log("Found class: ", name);
|
|
225
239
|
const newContext = new ExportContext(outputDir, name + ".cs");
|
|
226
240
|
newContext.appendLine("// auto generated code - do not edit directly");
|
|
@@ -231,12 +245,15 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
231
245
|
newContext.appendLine("{");
|
|
232
246
|
newContext.indentLevel += 1;
|
|
233
247
|
// newContext.appendLine("// source: " + path.resolve(sourceFile.fileName));
|
|
234
|
-
|
|
248
|
+
const typeName = lastTypeFound ?? "UnityEngine.MonoBehaviour";
|
|
249
|
+
console.log(name + " inherits " + typeName);
|
|
250
|
+
newContext.appendLine("public partial class " + name + " : " + typeName);
|
|
235
251
|
newContext.appendLine("{");
|
|
236
252
|
newContext.indentLevel += 1;
|
|
237
253
|
newContext.classEnd = dec.end;
|
|
238
254
|
contexts.push(newContext);
|
|
239
255
|
}
|
|
256
|
+
lastTypeFound = null;
|
|
240
257
|
break;
|
|
241
258
|
}
|
|
242
259
|
|
|
@@ -278,7 +295,7 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
278
295
|
if (!node) return undefined;
|
|
279
296
|
|
|
280
297
|
// skip decorators (e.g. @serializable() may break array generation)
|
|
281
|
-
if(node.kind === ts.SyntaxKind.Decorator)
|
|
298
|
+
if (node.kind === ts.SyntaxKind.Decorator)
|
|
282
299
|
return undefined;
|
|
283
300
|
|
|
284
301
|
let typeName = node?.getText();
|
|
@@ -326,12 +343,11 @@ export function run(program: ts.Program, outputDir: string, sourceFile: ts.Sourc
|
|
|
326
343
|
console.log("Child type: " + ts.SyntaxKind[child.kind]);
|
|
327
344
|
res = tryResolveTypeRecursive(child);
|
|
328
345
|
}
|
|
329
|
-
|
|
330
|
-
if(ts.isTypeReferenceNode(node)){
|
|
346
|
+
|
|
347
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
331
348
|
const typeRef = node as ts.TypeReferenceNode;
|
|
332
349
|
const typeName = typeRef.typeName.getText();
|
|
333
|
-
switch(typeName)
|
|
334
|
-
{
|
|
350
|
+
switch (typeName) {
|
|
335
351
|
case "Array":
|
|
336
352
|
res += "[]";
|
|
337
353
|
return res;
|
package/src/test.ts
CHANGED
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
import { Behaviour } from "needle.tiny.engine/engine-components/Component";
|
|
3
|
+
import { RoomEntity } from "./Room";
|
|
3
4
|
|
|
5
|
+
//@type (RoomEntity)
|
|
6
|
+
export class NavigationManager extends RoomEntity {
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
fl:number = 1;
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
nav_forward() {
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
nav_backward() {
|
|
15
|
+
|
|
16
|
+
}
|
|
9
17
|
}
|
|
10
18
|
|
|
19
|
+
export abstract class NavComponent extends Behaviour {
|
|
20
|
+
|
|
21
|
+
abstract next();
|
|
22
|
+
abstract prev();
|
|
23
|
+
abstract isAtEnd():boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
// export class PointOfInterest extends Behaviour {
|
|
28
|
+
|
|
29
|
+
// myVal:number = 12;
|
|
30
|
+
|
|
31
|
+
// // @type(HELLO)
|
|
32
|
+
// myFunction(){
|
|
33
|
+
|
|
34
|
+
// }
|
|
35
|
+
|
|
36
|
+
// // @type(UnityEngine.Camera)
|
|
37
|
+
// view?:Camera;
|
|
38
|
+
// test:string = "123";
|
|
39
|
+
// // test
|
|
40
|
+
// }
|
|
41
|
+
|
|
42
|
+
// export class MaterialColorHandler extends Behaviour {
|
|
43
|
+
|
|
44
|
+
// @serializeable(Renderer)
|
|
45
|
+
// renderer?: Renderer[];
|
|
46
|
+
// }
|
|
47
|
+
|
|
11
48
|
// export class MyArray extends Behaviour {
|
|
12
49
|
|
|
13
50
|
// arr? : Array<number> = [1,2,3];
|