Skip to content

Commit 0139c88

Browse files
lollekoPerryvw
authored andcommitted
[WIP] Module rework (#22)
* Progress on modules (version with exports table and require) * Fixed minor issues with exports table Exports table now wont be generated if file has not export statement * Fixed namespace chaining not working ocrrectly when inside ns scope * Updated module dedection & Added tests * Fixed namespaces beeign exported by default & Added more tests * Fixed module detection
1 parent 08d68e8 commit 0139c88

File tree

8 files changed

+315
-46
lines changed

8 files changed

+315
-46
lines changed

dist/TSHelper.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ var TSHelper = /** @class */ (function () {
2828
}
2929
return "unknown";
3030
};
31+
TSHelper.isFileModule = function (sourceFile) {
32+
if (sourceFile) {
33+
// Vanilla ts flags files as external module if they have an import or
34+
// export statement, we only check for export statements
35+
return sourceFile.statements.some(function (statement) {
36+
return (ts.getCombinedModifierFlags(statement) & ts.ModifierFlags.Export) !== 0
37+
|| statement.kind === ts.SyntaxKind.ExportAssignment
38+
|| statement.kind === ts.SyntaxKind.ExportDeclaration;
39+
});
40+
}
41+
return false;
42+
};
3143
TSHelper.isStringType = function (type) {
3244
return (type.flags & ts.TypeFlags.String) != 0
3345
|| (type.flags & ts.TypeFlags.StringLike) != 0

dist/Transpiler.js

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ exports.__esModule = true;
1313
var ts = require("typescript");
1414
var TSHelper_1 = require("./TSHelper");
1515
var ForHelper_1 = require("./ForHelper");
16+
var path = require("path");
1617
var TranspileError = /** @class */ (function (_super) {
1718
__extends(TranspileError, _super);
1819
function TranspileError(message, node) {
@@ -30,6 +31,8 @@ var LuaTranspiler = /** @class */ (function () {
3031
this.genVarCounter = 0;
3132
this.transpilingSwitch = false;
3233
this.namespace = [];
34+
this.importCount = 0;
35+
this.isModule = false;
3336
}
3437
// Transpile a source file
3538
LuaTranspiler.transpileSourceFile = function (node, checker, addHeader) {
@@ -39,7 +42,17 @@ var LuaTranspiler = /** @class */ (function () {
3942
+ "-- Date: " + new Date().toDateString() + "\n"
4043
+ "--=======================================================================================\n"
4144
: "";
42-
return header + transpiler.transpileBlock(node);
45+
var result = header;
46+
transpiler.isModule = TSHelper_1.TSHelper.isFileModule(node);
47+
if (transpiler.isModule) {
48+
// Shadow exports if it already exists
49+
result += "local exports = exports or {}\n";
50+
}
51+
result += transpiler.transpileBlock(node);
52+
if (transpiler.isModule) {
53+
result += "return exports\n";
54+
}
55+
return result;
4356
};
4457
LuaTranspiler.prototype.pushIndent = function () {
4558
this.indent = this.indent + " ";
@@ -50,6 +63,20 @@ var LuaTranspiler = /** @class */ (function () {
5063
LuaTranspiler.prototype.definitionName = function (name) {
5164
return this.namespace.concat(name).join(".");
5265
};
66+
LuaTranspiler.prototype.accessPrefix = function (node) {
67+
return node && this.isModule ?
68+
"local " : "";
69+
};
70+
LuaTranspiler.prototype.makeExport = function (name, node) {
71+
var result = "";
72+
if (node && node.modifiers && (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export)) {
73+
result = this.indent + ("exports." + this.definitionName(name) + " = " + name + "\n");
74+
}
75+
if (this.namespace.length !== 0) {
76+
result += this.indent + (this.definitionName(name) + " = " + name + "\n");
77+
}
78+
return result;
79+
};
5380
// Transpile a block
5481
LuaTranspiler.prototype.transpileBlock = function (node) {
5582
var _this = this;
@@ -117,19 +144,24 @@ var LuaTranspiler = /** @class */ (function () {
117144
}
118145
};
119146
LuaTranspiler.prototype.transpileImport = function (node) {
120-
var name = this.transpileExpression(node.moduleSpecifier);
121-
var imports = node.importClause.namedBindings;
122-
if (ts.isNamespaceImport(imports)) {
123-
return "{$imports.name.escapedText} = require(" + name + ")";
147+
var importFile = this.transpileExpression(node.moduleSpecifier);
148+
if (!node.importClause) {
149+
throw new TranspileError("Default Imports are not supported, please use named imports instead!", node);
124150
}
125-
else if (ts.isNamedImports(imports)) {
126-
// Forbid renaming
151+
var imports = node.importClause.namedBindings;
152+
if (ts.isNamedImports(imports)) {
153+
var fileImportTable_1 = path.basename(importFile.replace(new RegExp("\"", "g"), "")) + this.importCount;
154+
var result_1 = "local " + fileImportTable_1 + " = require(" + importFile + ")\n";
155+
this.importCount++;
127156
imports.elements.forEach(function (element) {
128157
if (element.propertyName) {
129-
throw new TranspileError("Renaming of individual imported objects is not allowed", node);
158+
result_1 += "local " + element.name.escapedText + " = " + fileImportTable_1 + "." + element.propertyName.escapedText + "\n";
159+
}
160+
else {
161+
result_1 += "local " + element.name.escapedText + " = " + fileImportTable_1 + "." + element.name.escapedText + "\n";
130162
}
131163
});
132-
return "require(" + name + ")";
164+
return result_1;
133165
}
134166
else {
135167
throw new TranspileError("Unsupported import type.", node);
@@ -140,10 +172,19 @@ var LuaTranspiler = /** @class */ (function () {
140172
if (TSHelper_1.TSHelper.isPhantom(this.checker.getTypeAtLocation(node), this.checker))
141173
return this.transpileNode(node.body);
142174
var defName = this.definitionName(node.name.text);
143-
var result = this.indent + (defName + " = {}\n");
175+
var result = this.indent + this.accessPrefix(node) + (node.name.text + " = " + node.name.text + " or {}\n");
176+
if (this.namespace.length > 0) {
177+
result += this.indent + (defName + " = " + node.name.text + " or {}\n");
178+
}
179+
result += this.makeExport(defName, node);
180+
// Create closure
181+
result += this.indent + "do\n";
182+
this.pushIndent();
144183
this.namespace.push(node.name.text);
145184
result += this.transpileNode(node.body);
146185
this.namespace.pop();
186+
this.popIndent();
187+
result += this.indent + "end\n";
147188
return result;
148189
};
149190
LuaTranspiler.prototype.transpileEnum = function (node) {
@@ -153,8 +194,9 @@ var LuaTranspiler = /** @class */ (function () {
153194
var type = this.checker.getTypeAtLocation(node);
154195
var membersOnly = TSHelper_1.TSHelper.isCompileMembersOnlyEnum(type, this.checker);
155196
if (!membersOnly) {
156-
var defName = this.definitionName(node.name.escapedText);
157-
result += this.indent + (defName + "={}\n");
197+
var name_1 = node.name.escapedText;
198+
result += this.indent + this.accessPrefix(node) + (name_1 + "={}\n");
199+
result += this.makeExport(name_1, node);
158200
}
159201
node.members.forEach(function (member) {
160202
if (member.initializer) {
@@ -166,7 +208,7 @@ var LuaTranspiler = /** @class */ (function () {
166208
}
167209
}
168210
if (membersOnly) {
169-
var defName = _this.definitionName(name);
211+
var defName = _this.definitionName(member.name.escapedText);
170212
result += _this.indent + (defName + "=" + val + "\n");
171213
}
172214
else {
@@ -638,6 +680,7 @@ var LuaTranspiler = /** @class */ (function () {
638680
var result = "";
639681
node.declarationList.declarations.forEach(function (declaration) {
640682
result += _this.transpileVariableDeclaration(declaration);
683+
result += _this.makeExport(declaration.name.escapedText, node);
641684
});
642685
return result;
643686
};
@@ -648,27 +691,27 @@ var LuaTranspiler = /** @class */ (function () {
648691
var identifier = node.name;
649692
if (node.initializer) {
650693
var value = this.transpileExpression(node.initializer);
651-
return "local " + identifier.escapedText + " = " + value;
694+
return "local " + identifier.escapedText + " = " + value + "\n";
652695
}
653696
else {
654-
return "local " + identifier.escapedText + " = nil";
697+
return "local " + identifier.escapedText + " = nil\n";
655698
}
656699
}
657700
else if (ts.isArrayBindingPattern(node.name)) {
658701
// Destructuring type
659702
var value = this.transpileExpression(node.initializer);
660703
var parentName_1 = "__destr" + this.genVarCounter;
661704
this.genVarCounter++;
662-
var result_1 = "local " + parentName_1 + " = " + value + "\n";
705+
var result_2 = "local " + parentName_1 + " = " + value + "\n";
663706
node.name.elements.forEach(function (elem, index) {
664707
if (!elem.dotDotDotToken) {
665-
result_1 += _this.indent + ("local " + elem.name.escapedText + " = " + parentName_1 + "[" + (index + 1) + "]\n");
708+
result_2 += _this.indent + ("local " + elem.name.escapedText + " = " + parentName_1 + "[" + (index + 1) + "]\n");
666709
}
667710
else {
668-
result_1 += _this.indent + ("local " + elem.name.escapedText + " = TS_slice(" + parentName_1 + ", " + index + ")\n");
711+
result_2 += _this.indent + ("local " + elem.name.escapedText + " = TS_slice(" + parentName_1 + ", " + index + ")\n");
669712
}
670713
});
671-
return result_1;
714+
return result_2;
672715
}
673716
else {
674717
throw new TranspileError("Unsupported variable declaration type " + TSHelper_1.TSHelper.enumName(node.name.kind, ts.SyntaxKind), node);
@@ -686,12 +729,13 @@ var LuaTranspiler = /** @class */ (function () {
686729
paramNames.push(param.name.escapedText);
687730
});
688731
// Build function header
689-
result += this.indent + ("function " + this.definitionName(methodName) + "(" + paramNames.join(",") + ")\n");
732+
result += this.indent + this.accessPrefix(node) + ("function " + methodName + "(" + paramNames.join(",") + ")\n");
690733
this.pushIndent();
691734
result += this.transpileBlock(body);
692735
this.popIndent();
693736
// Close function block
694737
result += this.indent + "end\n";
738+
result += this.makeExport(methodName, node);
695739
return result;
696740
};
697741
LuaTranspiler.prototype.transpileMethodDeclaration = function (node, path) {
@@ -739,11 +783,13 @@ var LuaTranspiler = /** @class */ (function () {
739783
// Write class declaration
740784
var classOr = noClassOr ? "" : className + " or ";
741785
if (!extendsType) {
742-
result += this.indent + (className + " = " + classOr + "{}\n");
786+
result += this.indent + this.accessPrefix(node) + (className + " = " + classOr + "{}\n");
787+
result += this.makeExport(className, node);
743788
}
744789
else {
745790
var baseName = extendsType.expression.escapedText;
746-
result += this.indent + (className + " = " + classOr + baseName + ".new()\n");
791+
result += this.indent + this.accessPrefix(node) + (className + " = " + classOr + baseName + ".new()\n");
792+
result += this.makeExport(className, node);
747793
}
748794
result += this.indent + (className + ".__index = " + className + "\n");
749795
if (extendsType) {

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"coverage": "nyc --reporter=lcov npm test && nyc report"
1010
},
1111
"devDependencies": {
12+
"dedent": "^0.7.0",
1213
"deep-equal": "^1.0.1",
1314
"nyc": "^11.4.1"
1415
}

src/TSHelper.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,19 @@ export class TSHelper {
2626
return name;
2727
}
2828
}
29-
return "unknown"
29+
return "unknown";
30+
}
31+
32+
static isFileModule(sourceFile: ts.SourceFile) {
33+
if (sourceFile) {
34+
// Vanilla ts flags files as external module if they have an import or
35+
// export statement, we only check for export statements
36+
return sourceFile.statements.some(statement =>
37+
(ts.getCombinedModifierFlags(statement) & ts.ModifierFlags.Export) !== 0
38+
|| statement.kind === ts.SyntaxKind.ExportAssignment
39+
|| statement.kind === ts.SyntaxKind.ExportDeclaration)
40+
}
41+
return false;
3042
}
3143

3244
static isStringType(type: ts.Type): boolean {
@@ -40,8 +52,8 @@ export class TSHelper {
4052
}
4153

4254
static isArrayType(type: ts.Type): boolean {
43-
return (type.flags & ts.TypeFlags.Object) != 0
44-
&& (<ts.ObjectType>type).symbol
55+
return (type.flags & ts.TypeFlags.Object) != 0
56+
&& (<ts.ObjectType>type).symbol
4557
&& (<ts.ObjectType>type).symbol.escapedName == "Array";
4658
}
4759

@@ -51,14 +63,14 @@ export class TSHelper {
5163
}
5264

5365
static isCompileMembersOnlyEnum(type: ts.Type, checker: ts.TypeChecker): boolean {
54-
return type.symbol
66+
return type.symbol
5567
&& ((type.symbol.flags & ts.SymbolFlags.Enum) != 0)
5668
&& type.symbol.getDocumentationComment(checker)[0] != undefined
5769
&& this.hasCustomDecorator(type, checker, "!CompileMembersOnly");
5870
}
5971

6072
static isPureAbstractClass(type: ts.Type, checker: ts.TypeChecker): boolean {
61-
return type.symbol
73+
return type.symbol
6274
&& ((type.symbol.flags & ts.SymbolFlags.Class) != 0)
6375
&& this.hasCustomDecorator(type, checker, "!PureAbstract");
6476
}
@@ -83,4 +95,4 @@ export class TSHelper {
8395
}
8496
return false;
8597
}
86-
}
98+
}

0 commit comments

Comments
 (0)