From 59c179681ca0f7eef01f9d17d5dadff6aa1b1059 Mon Sep 17 00:00:00 2001 From: lolleko Date: Tue, 20 Feb 2018 20:20:03 +0100 Subject: [PATCH 1/2] Lualib Integration & Import path resolving Lualib now gets copied to the output directory of a project and required automatically in each file. In the feature we might only want to include it if its actually used. (Closes #36) Import Paths are now correctly resolved to their lua counterpart --- .gitignore | 3 --- dist/lualib/lib-typescript.d.ts | 22 -------------------- src/Compiler.ts | 21 +++++++++++++++---- src/Transpiler.ts | 36 ++++++++++++++++++++++++--------- test/src/util.ts | 7 ++++--- test/unit/expressions.spec.ts | 17 ++++++---------- 6 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 dist/lualib/lib-typescript.d.ts diff --git a/.gitignore b/.gitignore index 15e7c9320..152eac512 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,5 @@ *.js node_modules/ -*.lua -!json.lua -!dist/lualib/*.lua coverage/ .nyc* diff --git a/dist/lualib/lib-typescript.d.ts b/dist/lualib/lib-typescript.d.ts deleted file mode 100644 index 7d138b63f..000000000 --- a/dist/lualib/lib-typescript.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -declare class Set { - constructor(other?: Set); - add(item: T): void; - contains(item: T): boolean; - remove(item: T): boolean; - items(): T[]; - count(): number; - - //forEach((item: T) => U): U[]; -} - -declare class Map { - constructor(other?: Map); - put(key: S, value: T): void; - remove(key: S): boolean; - get(key: S): T; - containsKey(key: S): boolean; - keys(): S[]; - values(): T[]; - items(): {key: S, value: T}[]; - count(): number; -} \ No newline at end of file diff --git a/src/Compiler.ts b/src/Compiler.ts index 20c16a4cc..fd61fa9a9 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -34,19 +34,32 @@ function compile(fileNames: string[], options: ts.CompilerOptions): void { process.exit(1); } + if (!options.rootDir) { + options.rootDir = process.cwd(); + } + + if (!options.outDir) { + options.outDir = options.rootDir; + } + + // Copy lualib to target dir + // This isnt run in sync because copyFileSync wont report errors. + fs.copyFile(path.resolve(__dirname, "../dist/lualib/typescript.lua"), path.join(options.outDir, "typescript_lualib.lua"), (err: NodeJS.ErrnoException) => { + if (err) { + console.log("ERROR: copying lualib to output."); + } + }); + program.getSourceFiles().forEach(sourceFile => { if (!sourceFile.isDeclarationFile) { try { let rootDir = options.rootDir; - if (!rootDir) { - rootDir = process.cwd(); - } // Transpile AST let lua = LuaTranspiler.transpileSourceFile(sourceFile, checker, options); let outPath = sourceFile.fileName; - if (options.outDir) { + if (options.outDir !== options.rootDir) { outPath = path.join(options.outDir, sourceFile.fileName.replace(rootDir, "")); } diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 3d9dc5499..4804049f8 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -16,14 +16,17 @@ export class TranspileError extends Error { export class LuaTranspiler { // Transpile a source file static transpileSourceFile(node: ts.SourceFile, checker: ts.TypeChecker, options: ts.CompilerOptions): string { - let transpiler = new LuaTranspiler(checker, options); + let transpiler = new LuaTranspiler(checker, options, node); let header = options.addHeader ? "--=======================================================================================\n" + "-- Generated by TypescriptToLua transpiler https://github.com/Perryvw/TypescriptToLua \n" + "-- Date: " + new Date().toDateString() + "\n" + "--=======================================================================================\n" : ""; - let result = header - transpiler.isModule = tsEx.isFileModule(node); + let result = header; + if (!options.dontRequireLualib) { + // require helper functions + result += `require("typescript_lualib")\n`; + } if (transpiler.isModule) { // Shadow exports if it already exists result += "local exports = exports or {}\n"; @@ -43,8 +46,9 @@ export class LuaTranspiler { namespace: string[]; importCount: number; isModule: boolean; + sourceFile: ts.SourceFile; - constructor(checker: ts.TypeChecker, options: ts.CompilerOptions) { + constructor(checker: ts.TypeChecker, options: ts.CompilerOptions, sourceFile: ts.SourceFile) { this.indent = ""; this.checker = checker; this.options = options; @@ -52,7 +56,8 @@ export class LuaTranspiler { this.transpilingSwitch = false; this.namespace = []; this.importCount = 0; - this.isModule = false; + this.sourceFile = sourceFile; + this.isModule = tsEx.isFileModule(sourceFile); } pushIndent(): void { @@ -83,6 +88,16 @@ export class LuaTranspiler { return result; } + getImportPath(relativePath: string) { + // Calculate absolute path to import + let absolutePathToImport = path.resolve(path.dirname(this.sourceFile.fileName), relativePath); + if (this.options.rootDir) { + // Calculate path realtive to project root and replace path.sep with dots (lua doesn't know paths) + return `"${absolutePathToImport.replace(this.options.rootDir, "").replace(new RegExp(path.sep, "g"), ".").slice(1)}"`; + } + return `"${relativePath.replace(new RegExp(path.sep, "g"), ".")}"`; + } + // Transpile a block transpileBlock(node: ts.Node): string { let result = ""; @@ -152,7 +167,7 @@ export class LuaTranspiler { } transpileImport(node: ts.ImportDeclaration): string { - const importFile = this.transpileExpression(node.moduleSpecifier); + const importPath = this.transpileExpression(node.moduleSpecifier); if (!node.importClause || !node.importClause.namedBindings) { throw new TranspileError("Default Imports are not supported, please use named imports instead!", node); } @@ -160,8 +175,9 @@ export class LuaTranspiler { const imports = node.importClause.namedBindings; if (ts.isNamedImports(imports)) { - let fileImportTable = path.basename(importFile.replace(new RegExp("\"", "g"), "")) + this.importCount - let result = `local ${fileImportTable} = require(${importFile})\n` + let importPathWithoutQuotes = importPath.replace(new RegExp("\"", "g"), ""); + let fileImportTable = path.basename(importPathWithoutQuotes) + this.importCount + let result = `local ${fileImportTable} = require(${this.getImportPath(importPathWithoutQuotes)})\n` this.importCount++; imports.elements.forEach(element => { if (element.propertyName) { @@ -375,9 +391,9 @@ export class LuaTranspiler { this.transpilingSwitch = false; let i = index + 1; - if (i < clauses.length && !tsEx.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) { + if (i < clauses.length && !tsEx.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement))  { let nextClause = clauses[i]; - while(i < clauses.length + while (i < clauses.length && ts.isCaseClause(nextClause) && nextClause.statements.length === 0 ) { diff --git a/test/src/util.ts b/test/src/util.ts index 4ade2f44e..269d3955e 100644 --- a/test/src/util.ts +++ b/test/src/util.ts @@ -11,10 +11,11 @@ export namespace dummyTypes { export const Number = { flags: ts.TypeFlags.Number, symbol: { escapedName: "Number" } }; } -export function transpileString(str: string, dummyType: any): string { +export function transpileString(str: string, dummyType: any = dummyTypes.None): string { const dummyChecker = { getTypeAtLocation: function() { return dummyType; } } - const file = ts.createSourceFile("temp.ts", str, ts.ScriptTarget.Latest); - const result = LuaTranspiler.transpileSourceFile(file, dummyChecker, false); + const file = ts.createSourceFile("____internal_test_file.tstl", str, ts.ScriptTarget.Latest); + const options: ts.CompilerOptions = { dontRequireLualib: true }; + const result = LuaTranspiler.transpileSourceFile(file, dummyChecker, options); return result.trim(); } diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index ab231b996..ee570015e 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -3,12 +3,7 @@ import { Expect, Test, TestCase } from "alsatian"; import * as ts from "typescript"; import {LuaTranspiler, TranspileError} from "../../dist/Transpiler"; -const dummyChecker = {getTypeAtLocation: function() {return {};}} -function transpileString(str: string): string { - const file = ts.createSourceFile("", str, ts.ScriptTarget.Latest); - const result = LuaTranspiler.transpileSourceFile(file, dummyChecker, false); - return result.trim(); -} +import * as util from "../src/util"; export class ExpressionTests { @@ -20,7 +15,7 @@ export class ExpressionTests { @TestCase("-a", "-a") @Test("Unary expressions basic") public unaryBasic(input: string, lua: string) { - Expect(transpileString(input)).toBe(lua); + Expect(util.transpileString(input)).toBe(lua); } @TestCase("1+1", "1+1") @@ -31,7 +26,7 @@ export class ExpressionTests { @TestCase("1==1", "1==1") @Test("Binary expressions basic") public binary(input: string, lua: string) { - Expect(transpileString(input)).toBe(lua); + Expect(util.transpileString(input)).toBe(lua); } @TestCase("a+=b", "a=a+b") @@ -50,7 +45,7 @@ export class ExpressionTests { @TestCase("a>>>=b", "a=bit.rshift(a,b)") @Test("Binary expressions overridden operators") public binaryOperatorOverride(input: string, lua: string) { - Expect(transpileString(input)).toBe(lua); + Expect(util.transpileString(input)).toBe(lua); } @TestCase("1+1", "1+1") @@ -60,13 +55,13 @@ export class ExpressionTests { @TestCase("1*(3+4*2)", "1*(3+(4*2))") @Test("Binary expressions ordering parentheses") public binaryParentheses(input: string, lua: string) { - Expect(transpileString(input)).toBe(lua); + Expect(util.transpileString(input)).toBe(lua); } @TestCase("1 + a ? 3*a : c", "TS_ITE(1+a,function() return 3*a end,function() return c end)") @TestCase("a ? b : c", "TS_ITE(a,function() return b end,function() return c end)") @Test("Ternary operator") public conditional(input: string, lua: string) { - Expect(transpileString(input)).toBe(lua); + Expect(util.transpileString(input)).toBe(lua); } } From b47578fe974c478c29a0616ac4bba7e0eaa3f23d Mon Sep 17 00:00:00 2001 From: lolleko Date: Wed, 21 Feb 2018 09:15:37 +0100 Subject: [PATCH 2/2] Fixed import path resolve not wokring on win32 --- src/Transpiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 4804049f8..4cfb59514 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -93,9 +93,9 @@ export class LuaTranspiler { let absolutePathToImport = path.resolve(path.dirname(this.sourceFile.fileName), relativePath); if (this.options.rootDir) { // Calculate path realtive to project root and replace path.sep with dots (lua doesn't know paths) - return `"${absolutePathToImport.replace(this.options.rootDir, "").replace(new RegExp(path.sep, "g"), ".").slice(1)}"`; + return `"${absolutePathToImport.replace(this.options.rootDir, "").replace(new RegExp("\\\\|\/", "g"), ".").slice(1)}"`; } - return `"${relativePath.replace(new RegExp(path.sep, "g"), ".")}"`; + return `"${relativePath.replace(new RegExp("\\\\|\/", "g"), ".")}"`; } // Transpile a block