Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Language: JavaScript
BasedOnStyle: Google
IndentWidth: 4
JavaScriptQuotes: Double
ColumnLimit: 120
AlignAfterOpenBracket: Align
AllowAllParametersOfDeclarationOnNextLine: false
BinPackParameters: false
BinPackArguments: false
ExperimentalAutoDetectBinPacking: false
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ coverage:
status:
project:
default:
target: 90
target: 80
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MonkaS

threshold: null
base: auto
changes: off
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
node_modules/
yarn.lock

.vscode/

coverage/
.nyc*
*.js.map
Expand Down
21 changes: 21 additions & 0 deletions src/TransformHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as ts from "typescript";

export class TransformHelper {
// Helper to create simple lua variable statement;
public static createLuaVariableStatement(
identifier: ts.Identifier,
expression: ts.Expression,
typeNode?: ts.TypeNode
): ts.VariableStatement {
const declaration = ts.createVariableDeclaration(identifier, typeNode, expression);
const statement = ts.createVariableStatement([], ts.createVariableDeclarationList([declaration]));
return statement;
}

public static createLuaImport(identifier: ts.Identifier, moduleSpecifier: ts.StringLiteral): ts.VariableStatement {
const requireIdentifier = ts.createIdentifier("require");
const requireCall =
ts.createCall(requireIdentifier, [ts.createLiteralTypeNode(moduleSpecifier)], [moduleSpecifier]);
return this.createLuaVariableStatement(identifier, requireCall);
}
}
428 changes: 428 additions & 0 deletions src/Transformer.ts

Large diffs are not rendered by default.

79 changes: 5 additions & 74 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { DecoratorKind } from "./Decorator";
import { TSTLErrors } from "./Errors";
import { TSHelper as tsHelper } from "./TSHelper";

import { LuaTransformer } from "./Transformer";

/* tslint:disable */
const packageJSON = require("../package.json");
/* tslint:enable */
Expand Down Expand Up @@ -159,31 +161,6 @@ export abstract class LuaTranspiler {
this.luaLibFeatureSet.add(feature);
}

public getAbsoluteImportPath(relativePath: string): string {
if (relativePath.charAt(0) !== "." && this.options.baseUrl) {
return path.resolve(this.options.baseUrl, relativePath);
}
return path.resolve(path.dirname(this.sourceFile.fileName), relativePath);
}

public getImportPath(relativePath: string): string {
// Calculate absolute path to import
const absolutePathToImport = this.getAbsoluteImportPath(relativePath);
if (this.options.rootDir) {
// Calculate path relative to project root
// and replace path.sep with dots (lua doesn't know paths)
const relativePathToRoot =
this.pathToLuaRequirePath(absolutePathToImport.replace(this.options.rootDir, "").slice(1));
return `"${relativePathToRoot}"`;
}

return `"${this.pathToLuaRequirePath(relativePath)}"`;
}

public pathToLuaRequirePath(filePath: string): string {
return filePath.replace(new RegExp("\\\\|\/", "g"), ".");
}

public computeEnumMembers(node: ts.EnumDeclaration): Array<{ name: string, value: string | number }> {
let val: number | string = 0;
let hasStringInitializers = false;
Expand Down Expand Up @@ -221,6 +198,9 @@ export abstract class LuaTranspiler {
}
let result = header;

const transformer = new LuaTransformer(this.checker, this.options);
this. sourceFile = transformer.transform(this.sourceFile);

// Transpile content first to gather some info on dependencies
let fileStatements = "";
this.exportStack.push([]);
Expand Down Expand Up @@ -285,8 +265,6 @@ export abstract class LuaTranspiler {
}

switch (node.kind) {
case ts.SyntaxKind.ImportDeclaration:
return this.transpileImport(node as ts.ImportDeclaration);
case ts.SyntaxKind.ClassDeclaration:
return this.transpileClass(node as ts.ClassDeclaration);
case ts.SyntaxKind.ModuleDeclaration:
Expand Down Expand Up @@ -341,53 +319,6 @@ export abstract class LuaTranspiler {
return `__TS__${func}(${params.join(", ")})`;
}

public transpileImport(node: ts.ImportDeclaration): string {
const importPath = this.transpileExpression(node.moduleSpecifier);
const importPathWithoutQuotes = importPath.replace(new RegExp("\"", "g"), "");

if (!node.importClause || !node.importClause.namedBindings) {
throw TSTLErrors.DefaultImportsNotSupported(node);
}

const imports = node.importClause.namedBindings;

const requireKeyword = "require";

if (ts.isNamedImports(imports)) {
const fileImportTable = path.basename(importPathWithoutQuotes) + this.importCount;
const resolvedImportPath = this.getImportPath(importPathWithoutQuotes);

let result = `local ${fileImportTable} = ${requireKeyword}(${resolvedImportPath})\n`;
this.importCount++;

const filteredElements = imports.elements.filter(e => {
const decorators = tsHelper.getCustomDecorators(this.checker.getTypeAtLocation(e), this.checker);
return !decorators.has(DecoratorKind.Extension) && !decorators.has(DecoratorKind.MetaExtension);
});

if (filteredElements.length === 0) {
return "";
}

filteredElements.forEach(element => {
const nameText = this.transpileIdentifier(element.name);
if (element.propertyName) {
const propertyText = this.transpileIdentifier(element.propertyName);
result += `local ${nameText} = ${fileImportTable}.${propertyText}\n`;
} else {
result += `local ${nameText} = ${fileImportTable}.${nameText}\n`;
}
});

return result;
} else if (ts.isNamespaceImport(imports)) {
const resolvedImportPath = this.getImportPath(importPathWithoutQuotes);
return `local ${this.transpileIdentifier(imports.name)} = ${requireKeyword}(${resolvedImportPath})\n`;
} else {
throw TSTLErrors.UnsupportedImportType(imports);
}
}

public transpileNamespace(node: ts.ModuleDeclaration): string {
const decorators = tsHelper.getCustomDecorators(this.checker.getTypeAtLocation(node), this.checker);
// If phantom namespace just transpile the body as normal
Expand Down
2 changes: 1 addition & 1 deletion test/translation/lua/modulesImportAll.lua
Original file line number Diff line number Diff line change
@@ -1 +1 @@
local Test = require("test")
local Test = require("test");
4 changes: 2 additions & 2 deletions test/translation/lua/modulesImportNamed.lua
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
local test0 = require("test")
local TestClass = test0.TestClass
local test = require("test");
local TestClass = test.TestClass;
4 changes: 2 additions & 2 deletions test/translation/lua/modulesImportRenamed.lua
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
local test0 = require("test")
local RenamedClass = test0.TestClass
local test = require("test");
local RenamedClass = test.TestClass;
14 changes: 0 additions & 14 deletions test/unit/modules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,6 @@ export class LuaModuleTests {
Expect(lua).toBe(`require("lualib_bundle")`);
}

@Test("Import named bindings exception")
public namedBindigsException(): void {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe ignore this for now instead of removing, since we probably want to still have this test on the transformer instead of transpiler.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was using a custom created AST/Node I don't think that named bindings exception can ever be thrown.

const transpiler = util.makeTestTranspiler();

const mockDeclaration: any = {
importClause: {namedBindings: {}},
kind: ts.SyntaxKind.ImportDeclaration,
moduleSpecifier: ts.createLiteral("test"),
};

Expect(() => transpiler.transpileImport(mockDeclaration as ts.ImportDeclaration))
.toThrowError(Error, "Unsupported import type.");
}

@Test("Non-exported module")
public nonExportedModule(): void {
const lua = util.transpileString("module g { export function test() { return 3; } } return g.test();");
Expand Down