diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index 386dab5ba..135658f0a 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -136,7 +136,7 @@ function runDiagnostics(commandLine: ts.ParsedCommandLine) { } } - commandLine.errors.forEach((err) => { + commandLine.errors.forEach(err => { let ignore = false; // Ignore errors caused by tstl specific compiler options if (err.code === tsInvalidCompilerOptionErrorCode) { diff --git a/src/Compiler.ts b/src/Compiler.ts index 457b40647..fada07cd0 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -13,8 +13,8 @@ export function compile(fileNames: string[], options: CompilerOptions): void { const checker = program.getTypeChecker(); // Get all diagnostics, ignore unsupported extension - const diagnostics = ts.getPreEmitDiagnostics(program).filter((diag) => diag.code !== 6054); - diagnostics.forEach((diagnostic) => { + const diagnostics = ts.getPreEmitDiagnostics(program).filter(diag => diag.code !== 6054); + diagnostics.forEach(diagnostic => { if (diagnostic.file) { const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); @@ -30,12 +30,12 @@ export function compile(fileNames: string[], options: CompilerOptions): void { }); // If there are errors dont emit - if (diagnostics.filter((diag) => diag.category === ts.DiagnosticCategory.Error).length > 0) { + if (diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error).length > 0) { console.log("Stopping compilation process because of errors."); process.exit(1); } - program.getSourceFiles().forEach((sourceFile) => { + program.getSourceFiles().forEach(sourceFile => { if (!sourceFile.isDeclarationFile) { try { const rootDir = options.rootDir; diff --git a/src/TSHelper.ts b/src/TSHelper.ts index b0b49e3d2..8194197dc 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -24,14 +24,14 @@ export class TSHelper { } public static containsStatement(statements: ts.NodeArray, kind: ts.SyntaxKind): boolean { - return statements.some((statement) => statement.kind === kind); + return statements.some(statement => statement.kind === kind); } public static isFileModule(sourceFile: ts.SourceFile) { if (sourceFile) { // Vanilla ts flags files as external module if they have an import or // export statement, we only check for export statements - return sourceFile.statements.some((statement) => + return sourceFile.statements.some(statement => (ts.getCombinedModifierFlags(statement) & ts.ModifierFlags.Export) !== 0 || statement.kind === ts.SyntaxKind.ExportAssignment || statement.kind === ts.SyntaxKind.ExportDeclaration); @@ -88,9 +88,11 @@ export class TSHelper { public static hasCustomDecorator(type: ts.Type, checker: ts.TypeChecker, decorator: string): boolean { if (type.symbol) { - const comment = type.symbol.getDocumentationComment(checker); + const comments = type.symbol.getDocumentationComment(checker); const decorators = - comment.filter((_) => _.kind === "text").map((_) => _.text.trim()).filter((_) => _[0] === "!"); + comments.filter(comment => comment.kind === "text") + .map(comment => comment.text.trim()) + .filter(comment => comment[0] === "!"); return decorators.indexOf(decorator) > -1; } return false; diff --git a/src/Transpiler.ts b/src/Transpiler.ts index 9f6213164..8d9877919 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import { CompilerOptions } from "./CommandLineParser"; -import { TSHelper as tsEx } from "./TSHelper"; +import { TSHelper as tsHelper } from "./TSHelper"; import * as path from "path"; @@ -71,7 +71,7 @@ export class LuaTranspiler { this.namespace = []; this.importCount = 0; this.sourceFile = sourceFile; - this.isModule = tsEx.isFileModule(sourceFile); + this.isModule = tsHelper.isFileModule(sourceFile); } public pushIndent(): void { @@ -133,11 +133,11 @@ export class LuaTranspiler { let result = ""; if (ts.isBlock(node)) { - node.statements.forEach((statement) => { + node.statements.forEach(statement => { result += this.transpileNode(statement); }); } else { - node.forEachChild((child) => { + node.forEachChild(child => { result += this.transpileNode(child); }); } @@ -148,7 +148,7 @@ export class LuaTranspiler { // Transpile a node of unknown kind. public transpileNode(node: ts.Node): string { // Ignore declarations - if (node.modifiers && node.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DeclareKeyword)) { + if (node.modifiers && node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DeclareKeyword)) { return ""; } @@ -224,7 +224,7 @@ export class LuaTranspiler { const resolvedImportPath = this.getImportPath(importPathWithoutQuotes); let result = `local ${fileImportTable} = require(${resolvedImportPath})\n`; this.importCount++; - imports.elements.forEach((element) => { + imports.elements.forEach(element => { const nameText = element.name.escapedText; if (element.propertyName) { result += @@ -245,7 +245,7 @@ export class LuaTranspiler { public transpileNamespace(node: ts.ModuleDeclaration): string { // If phantom namespace just transpile the body as normal - if (tsEx.isPhantom(this.checker.getTypeAtLocation(node), this.checker) && node.body) { + if (tsHelper.isPhantom(this.checker.getTypeAtLocation(node), this.checker) && node.body) { return this.transpileNode(node.body); } @@ -277,7 +277,7 @@ export class LuaTranspiler { let result = ""; const type = this.checker.getTypeAtLocation(node); - const membersOnly = tsEx.isCompileMembersOnlyEnum(type, this.checker); + const membersOnly = tsHelper.isCompileMembersOnlyEnum(type, this.checker); if (!membersOnly) { const name = node.name.escapedText; @@ -285,7 +285,7 @@ export class LuaTranspiler { result += this.makeExport(name, node); } - node.members.forEach((member) => { + node.members.forEach(member => { if (member.initializer) { if (ts.isNumericLiteral(member.initializer)) { val = parseInt(member.initializer.text); @@ -386,7 +386,7 @@ export class LuaTranspiler { const expression = this.transpileExpression(node.expression); // Use ipairs for array types, pairs otherwise - const isArray = tsEx.isArrayType(this.checker.getTypeAtLocation(node.expression), this.checker); + const isArray = tsHelper.isArrayType(this.checker.getTypeAtLocation(node.expression), this.checker); const pairs = isArray ? "ipairs" : "pairs"; // Make header @@ -408,7 +408,7 @@ export class LuaTranspiler { // Transpile expression const expression = this.transpileExpression(node.expression); - if (tsEx.isArrayType(this.checker.getTypeAtLocation(node.expression), this.checker)) { + if (tsHelper.isArrayType(this.checker.getTypeAtLocation(node.expression), this.checker)) { throw new TranspileError("Iterating over arrays with 'for in' is not allowed.", node); } @@ -456,13 +456,13 @@ export class LuaTranspiler { this.pushIndent(); this.transpilingSwitch++; - clause.statements.forEach((statement) => { + clause.statements.forEach(statement => { result += this.transpileNode(statement); }); this.transpilingSwitch--; let i = index + 1; - if (i < clauses.length && !tsEx.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) { + if (i < clauses.length && !tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) { let nextClause = clauses[i]; while (i < clauses.length && ts.isCaseClause(nextClause) @@ -538,10 +538,10 @@ export class LuaTranspiler { if (node.expression) { // If parent function is a TupleReturn function // and return expression is an array literal, leave out brackets. - const declaration = tsEx.findFirstNodeAbove(node, ts.isFunctionDeclaration); - if (declaration && tsEx.isTupleReturnFunction(this.checker.getTypeAtLocation(declaration), this.checker) + const declaration = tsHelper.findFirstNodeAbove(node, ts.isFunctionDeclaration); + if (declaration && tsHelper.isTupleReturnFunction(this.checker.getTypeAtLocation(declaration), this.checker) && ts.isArrayLiteralExpression(node.expression)) { - return "return " + node.expression.elements.map((elem) => this.transpileExpression(elem)).join(","); + return "return " + node.expression.elements.map(elem => this.transpileExpression(elem)).join(","); } // Otherwise just do a normal return @@ -566,7 +566,11 @@ export class LuaTranspiler { case ts.SyntaxKind.ElementAccessExpression: return this.transpileElementAccessExpression(node as ts.ElementAccessExpression); case ts.SyntaxKind.Identifier: - // For identifiers simply return their name + // Catch undefined which is passed as identifier + if ((node as ts.Identifier).originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) { + return "nil"; + } + // Otherwise simply return the name return (node as ts.Identifier).text; case ts.SyntaxKind.StringLiteral: const text = (node as ts.StringLiteral).text; @@ -580,6 +584,7 @@ export class LuaTranspiler { case ts.SyntaxKind.FalseKeyword: return "false"; case ts.SyntaxKind.NullKeyword: + case ts.SyntaxKind.UndefinedKeyword: return "nil"; case ts.SyntaxKind.ThisKeyword: return "self"; @@ -612,7 +617,7 @@ export class LuaTranspiler { return this.transpileExpression((node as ts.AsExpression).expression); default: throw new TranspileError( - "Unsupported expression kind: " + tsEx.enumName(node.kind, ts.SyntaxKind), + "Unsupported expression kind: " + tsHelper.enumName(node.kind, ts.SyntaxKind), node ); } @@ -633,7 +638,7 @@ export class LuaTranspiler { result = `bit.band(${lhs},${rhs})`; break; case ts.SyntaxKind.AmpersandEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `bit.band(${lhs},${rhs})`); } @@ -643,7 +648,7 @@ export class LuaTranspiler { result = `bit.bor(${lhs},${rhs})`; break; case ts.SyntaxKind.BarEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `bit.bor(${lhs},${rhs})`); } @@ -653,7 +658,7 @@ export class LuaTranspiler { result = `bit.lshift(${lhs},${rhs})`; break; case ts.SyntaxKind.LessThanLessThanEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `bit.lshift(${lhs},${rhs})`); } @@ -663,7 +668,7 @@ export class LuaTranspiler { result = `bit.arshift(${lhs},${rhs})`; break; case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `bit.arshift(${lhs},${rhs})`); } @@ -673,7 +678,7 @@ export class LuaTranspiler { result = `bit.rshift(${lhs},${rhs})`; break; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `bit.rshift(${lhs},${rhs})`); } @@ -686,7 +691,7 @@ export class LuaTranspiler { result = `${lhs}&${rhs}`; break; case ts.SyntaxKind.AmpersandEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}&${rhs}`); } result = `${lhs}=${lhs}&${rhs}`; @@ -695,7 +700,7 @@ export class LuaTranspiler { result = `${lhs}|${rhs}`; break; case ts.SyntaxKind.BarEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}|${rhs}`); } result = `${lhs}=${lhs}|${rhs}`; @@ -704,7 +709,7 @@ export class LuaTranspiler { result = `${lhs}<<${rhs}`; break; case ts.SyntaxKind.LessThanLessThanEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}<<${rhs}`); } result = `${lhs}=${lhs}<<${rhs}`; @@ -713,7 +718,7 @@ export class LuaTranspiler { result = `${lhs}>>${rhs}`; break; case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}>>${rhs}`); } result = `${lhs}=${lhs}>>${rhs}`; @@ -722,7 +727,7 @@ export class LuaTranspiler { result = `${lhs}>>>${rhs}`; break; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}>>>${rhs}`); } result = `${lhs}=${lhs}>>>${rhs}`; @@ -734,25 +739,25 @@ export class LuaTranspiler { if (result === "") { switch (node.operatorToken.kind) { case ts.SyntaxKind.PlusEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}+${rhs}`); } result = `${lhs}=${lhs}+${rhs}`; break; case ts.SyntaxKind.MinusEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}-${rhs}`); } result = `${lhs}=${lhs}-${rhs}`; break; case ts.SyntaxKind.AsteriskEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}*${rhs}`); } result = `${lhs}=${lhs}*${rhs}`; break; case ts.SyntaxKind.SlashEqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, `${lhs}/${rhs}`); } result = `${lhs}=${lhs}/${rhs}`; @@ -798,7 +803,7 @@ export class LuaTranspiler { result = `${lhs}<=${rhs}`; break; case ts.SyntaxKind.EqualsToken: - if (tsEx.hasSetAccessor(node.left, this.checker)) { + if (tsHelper.hasSetAccessor(node.left, this.checker)) { return this.transpileSetAccessor(node.left as ts.PropertyAccessExpression, rhs); } result = `${lhs}=${rhs}`; @@ -832,7 +837,7 @@ export class LuaTranspiler { public transpileTemplateExpression(node: ts.TemplateExpression) { const parts = [`"${node.head.text}"`]; - node.templateSpans.forEach((span) => { + node.templateSpans.forEach(span => { const expr = this.transpileExpression(span.expression, true); if (ts.isTemplateTail(span.literal)) { parts.push(`tostring(${expr}).."${span.literal.text}"`); @@ -859,7 +864,8 @@ export class LuaTranspiler { case ts.SyntaxKind.MinusMinusToken: return `${operand}=${operand}-1`; default: - throw new TranspileError("Unsupported unary postfix: " + tsEx.enumName(node.kind, ts.SyntaxKind), node); + throw new TranspileError("Unsupported unary postfix: " + tsHelper.enumName(node.kind, ts.SyntaxKind), + node); } } @@ -875,7 +881,8 @@ export class LuaTranspiler { case ts.SyntaxKind.MinusToken: return `-${operand}`; default: - throw new TranspileError("Unsupported unary prefix: " + tsEx.enumName(node.kind, ts.SyntaxKind), node); + throw new TranspileError("Unsupported unary prefix: " + tsHelper.enumName(node.kind, ts.SyntaxKind), + node); } } @@ -909,7 +916,7 @@ export class LuaTranspiler { return this.transpileStringCallExpression(node); } - if (tsEx.isArrayType(expType, this.checker)) { + if (tsHelper.isArrayType(expType, this.checker)) { return this.transpileArrayCallExpression(node); } @@ -1038,7 +1045,7 @@ export class LuaTranspiler { parameters.push(this.transpileExpression(context)); } - params.forEach((param) => { + params.forEach(param => { parameters.push(this.transpileExpression(param)); }); @@ -1055,15 +1062,15 @@ export class LuaTranspiler { case ts.TypeFlags.StringLiteral: return this.transpileStringProperty(node); case ts.TypeFlags.Object: - if (tsEx.isArrayType(type, this.checker)) { + if (tsHelper.isArrayType(type, this.checker)) { return this.transpileArrayProperty(node); - } else if (tsEx.hasGetAccessor(node, this.checker)) { + } else if (tsHelper.hasGetAccessor(node, this.checker)) { return this.transpileGetAccessor(node); } } // Do not output path for member only enums - if (tsEx.isCompileMembersOnlyEnum(type, this.checker)) { + if (tsHelper.isCompileMembersOnlyEnum(type, this.checker)) { return property; } @@ -1145,9 +1152,9 @@ export class LuaTranspiler { const index = this.transpileExpression(node.argumentExpression); const type = this.checker.getTypeAtLocation(node.expression); - if (tsEx.isArrayType(type, this.checker) || tsEx.isTupleType(type, this.checker)) { + if (tsHelper.isArrayType(type, this.checker) || tsHelper.isTupleType(type, this.checker)) { return `${element}[${index}+1]`; - } else if (tsEx.isStringType(type)) { + } else if (tsHelper.isStringType(type)) { return `string.sub(${element},${index}+1,${index}+1)`; } else { return `${element}[${index}]`; @@ -1158,7 +1165,7 @@ export class LuaTranspiler { public transpileVariableStatement(node: ts.VariableStatement): string { let result = ""; - node.declarationList.declarations.forEach((declaration) => { + node.declarationList.declarations.forEach(declaration => { result += this.transpileVariableDeclaration(declaration as ts.VariableDeclaration); result += this.makeExport((declaration.name as ts.Identifier).escapedText, node); }); @@ -1181,16 +1188,18 @@ export class LuaTranspiler { const value = this.transpileExpression(node.initializer); // Disallow ellipsis destruction - if (node.name.elements.some((elem) => !ts.isBindingElement(elem) || elem.dotDotDotToken !== undefined)) { + if (node.name.elements.some(elem => !ts.isBindingElement(elem) || elem.dotDotDotToken !== undefined)) { throw new TranspileError(`Ellipsis destruction is not allowed.`, node); } const vars = node.name.elements.map( - (element) => ((element as ts.BindingElement).name as ts.Identifier).escapedText).join(","); + element => ((element as ts.BindingElement).name as ts.Identifier + ).escapedText).join(","); // Don't unpack TupleReturn decorated functions if (ts.isCallExpression(node.initializer) - && tsEx.isTupleReturnFunction(this.checker.getTypeAtLocation(node.initializer.expression), this.checker) + && tsHelper.isTupleReturnFunction(this.checker.getTypeAtLocation(node.initializer.expression), + this.checker) ) { return `local ${vars}=${value}\n`; } else { @@ -1198,7 +1207,7 @@ export class LuaTranspiler { } } else { throw new TranspileError( - "Unsupported variable declaration type " + tsEx.enumName(node.name.kind, ts.SyntaxKind), + "Unsupported variable declaration type " + tsHelper.enumName(node.name.kind, ts.SyntaxKind), node ); } @@ -1277,7 +1286,7 @@ export class LuaTranspiler { } } // Parameters with default values - const defaultValueParams = node.parameters.filter((declaration) => declaration.initializer !== undefined); + const defaultValueParams = node.parameters.filter(declaration => declaration.initializer !== undefined); // Build function header result += this.indent + `function ${callPath}${methodName}(${paramNames.join(",")})\n`; @@ -1304,14 +1313,14 @@ export class LuaTranspiler { // Find extends class, ignore implements let extendsType: ts.ExpressionWithTypeArguments | undefined; let noClassOr = false; - if (node.heritageClauses) { node.heritageClauses.forEach((clause) => { + if (node.heritageClauses) { node.heritageClauses.forEach(clause => { if (clause.token === ts.SyntaxKind.ExtendsKeyword) { const superType = this.checker.getTypeAtLocation(clause.types[0]); // Ignore purely abstract types (decorated with /** @PureAbstract */) - if (!tsEx.isPureAbstractClass(superType, this.checker)) { + if (!tsHelper.isPureAbstractClass(superType, this.checker)) { extendsType = clause.types[0]; } - noClassOr = tsEx.hasCustomDecorator(superType, this.checker, "!NoClassOr"); + noClassOr = tsHelper.hasCustomDecorator(superType, this.checker, "!NoClassOr"); } }); } @@ -1324,7 +1333,7 @@ export class LuaTranspiler { let result = ""; // Skip header if this is an extension class - const isExtension = tsEx.isExtensionClass(this.checker.getTypeAtLocation(node), this.checker); + const isExtension = tsHelper.isExtensionClass(this.checker.getTypeAtLocation(node), this.checker); if (!isExtension) { // Write class declaration const classOr = noClassOr ? "" : `${className} or `; @@ -1358,12 +1367,12 @@ export class LuaTranspiler { // Get all properties with value const properties = node.members.filter(ts.isPropertyDeclaration) - .filter((_) => _.initializer); + .filter(member => member.initializer); // Divide properties into static and non-static - const isStatic = (_) => _.modifiers && _.modifiers.some((__) => __.kind === ts.SyntaxKind.StaticKeyword); + const isStatic = prop => prop.modifiers && prop.modifiers.some(m => m.kind === ts.SyntaxKind.StaticKeyword); const staticFields = properties.filter(isStatic); - const instanceFields = properties.filter((_) => !isStatic(_)); + const instanceFields = properties.filter(prop => !isStatic(prop)); // Add static declarations for (const field of staticFields) { @@ -1387,17 +1396,17 @@ export class LuaTranspiler { } // Transpile get accessors - node.members.filter(ts.isGetAccessor).forEach((getAccessor) => { + node.members.filter(ts.isGetAccessor).forEach(getAccessor => { result += this.transpileGetAccessorDeclaration(getAccessor, className); }); // Transpile set accessors - node.members.filter(ts.isSetAccessor).forEach((setAccessor) => { + node.members.filter(ts.isSetAccessor).forEach(setAccessor => { result += this.transpileSetAccessorDeclaration(setAccessor, className); }); // Transpile methods - node.members.filter(ts.isMethodDeclaration).forEach((method) => { + node.members.filter(ts.isMethodDeclaration).forEach(method => { result += this.transpileMethodDeclaration(method, `${className}.`); }); @@ -1422,7 +1431,7 @@ export class LuaTranspiler { const name = (setAccessor.name as ts.Identifier).escapedText; const paramNames: string[] = ["self"]; - setAccessor.parameters.forEach((param) => { + setAccessor.parameters.forEach(param => { paramNames.push((param.name as ts.Identifier).escapedText as string); }); @@ -1443,7 +1452,7 @@ export class LuaTranspiler { const extraInstanceFields = []; const parameters = ["self"]; - node.parameters.forEach((param) => { + node.parameters.forEach(param => { // If param has decorators, add extra instance field if (param.modifiers !== undefined) { extraInstanceFields.push((param.name as ts.Identifier).escapedText as string); @@ -1480,7 +1489,7 @@ export class LuaTranspiler { public transpileArrayLiteral(node: ts.ArrayLiteralExpression): string { const values: string[] = []; - node.elements.forEach((child) => { + node.elements.forEach(child => { values.push(this.transpileExpression(child)); }); @@ -1490,7 +1499,7 @@ export class LuaTranspiler { public transpileObjectLiteral(node: ts.ObjectLiteralExpression): string { const properties: string[] = []; // Add all property assignments - node.properties.forEach((element) => { + node.properties.forEach(element => { let name = ""; if (ts.isIdentifier(element.name)) { name = element.name.escapedText as string; @@ -1514,11 +1523,11 @@ export class LuaTranspiler { public transpileFunctionExpression(node: ts.ArrowFunction): string { // Build parameter string const paramNames: string[] = []; - node.parameters.forEach((param) => { + node.parameters.forEach(param => { paramNames.push((param.name as ts.Identifier).escapedText as string); }); - const defaultValueParams = node.parameters.filter((declaration) => declaration.initializer !== undefined); + const defaultValueParams = node.parameters.filter(declaration => declaration.initializer !== undefined); if (ts.isBlock(node.body) || defaultValueParams.length > 0) { let result = `function(${paramNames.join(",")})\n`; @@ -1535,7 +1544,7 @@ export class LuaTranspiler { public transpileParameterDefaultValues(params: ts.ParameterDeclaration[]): string { let result = ""; - params.filter((declaration) => declaration.initializer !== undefined).forEach((declaration) => { + params.filter(declaration => declaration.initializer !== undefined).forEach(declaration => { const paramName = (declaration.name as ts.Identifier).escapedText; const paramValue = this.transpileExpression(declaration.initializer); result += this.indent + `if ${paramName}==nil then ${paramName}=${paramValue} end\n`; diff --git a/test/translation/lua/classConstructorAssignment.lua b/test/translation/lua/classConstructorAssignment.lua new file mode 100644 index 000000000..1aad52a2f --- /dev/null +++ b/test/translation/lua/classConstructorAssignment.lua @@ -0,0 +1,10 @@ +Test = Test or {} +Test.__index = Test +function Test.new(construct, ...) + local instance = setmetatable({}, Test) + if construct and Test.constructor then Test.constructor(instance, ...) end + return instance +end +function Test.constructor(self,field) + self.field = field +end diff --git a/test/translation/lua/enumMembersOnly.lua b/test/translation/lua/enumMembersOnly.lua index 1ae319913..12b5ecc1c 100644 --- a/test/translation/lua/enumMembersOnly.lua +++ b/test/translation/lua/enumMembersOnly.lua @@ -1,3 +1,4 @@ val1=0 val2=2 -val3=3 \ No newline at end of file +val3=3 +local a = val1 \ No newline at end of file diff --git a/test/translation/lua/returnDefault.lua b/test/translation/lua/returnDefault.lua new file mode 100644 index 000000000..52c2123e3 --- /dev/null +++ b/test/translation/lua/returnDefault.lua @@ -0,0 +1,3 @@ +function myFunc() + return +end diff --git a/test/translation/ts/classConstructorAssignment.ts b/test/translation/ts/classConstructorAssignment.ts new file mode 100644 index 000000000..45bbd58cf --- /dev/null +++ b/test/translation/ts/classConstructorAssignment.ts @@ -0,0 +1,3 @@ +class Test { + constructor(private field: number) {} +} diff --git a/test/translation/ts/enumMembersOnly.ts b/test/translation/ts/enumMembersOnly.ts index 850b59031..8b5174636 100644 --- a/test/translation/ts/enumMembersOnly.ts +++ b/test/translation/ts/enumMembersOnly.ts @@ -3,4 +3,6 @@ enum TestEnum { val1 = 0, val2 = 2, val3 -} \ No newline at end of file +} + +const a = TestEnum.val1; \ No newline at end of file diff --git a/test/translation/ts/returnDefault.ts b/test/translation/ts/returnDefault.ts new file mode 100644 index 000000000..a121d7ca0 --- /dev/null +++ b/test/translation/ts/returnDefault.ts @@ -0,0 +1,3 @@ +function myFunc() { + return; +} diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index d5bd1c748..b701179d0 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -1,4 +1,5 @@ -import { Expect, Test, TestCase } from "alsatian"; +import { Expect, Test, TestCase, FocusTest } from "alsatian"; +import { TranspileError } from "../../src/Transpiler"; import * as util from "../src/util"; const fs = require("fs"); @@ -13,7 +14,7 @@ export class AssignmentTests { @TestCase(`{a:3,b:"4"}`, `{a = 3,b = "4"}`) @Test("Const assignment") public constAssignment(inp: string, out: string) { - var lua = util.transpileString(`const myvar = ${inp};`) + const lua = util.transpileString(`const myvar = ${inp};`); Expect(lua).toBe(`local myvar = ${out}`); } @@ -25,7 +26,7 @@ export class AssignmentTests { @TestCase(`{a:3,b:"4"}`, `{a = 3,b = "4"}`) @Test("Const assignment") public letAssignment(inp: string, out: string) { - var lua = util.transpileString(`let myvar = ${inp};`) + const lua = util.transpileString(`let myvar = ${inp};`); Expect(lua).toBe(`local myvar = ${out}`); } @@ -37,7 +38,39 @@ export class AssignmentTests { @TestCase(`{a:3,b:"4"}`, `{a = 3,b = "4"}`) @Test("Const assignment") public varAssignment(inp: string, out: string) { - var lua = util.transpileString(`var myvar = ${inp};`) + const lua = util.transpileString(`var myvar = ${inp};`); Expect(lua).toBe(`local myvar = ${out}`); } + + @TestCase("var myvar;") + @TestCase("let myvar;") + @TestCase("const myvar;") + @TestCase("const myvar = null;") + @TestCase("const myvar = undefined;") + @Test("Null assignments") + public nullAssignment(declaration: string) { + const lua = util.transpileString(declaration + " return myvar;"); + const result = util.executeLua(lua); + Expect(result).toBe(undefined); + } + + @TestCase(["a", "b"], ["e", "f"]) + @TestCase(["a", "b"], ["e", "f", "g"]) + @TestCase(["a", "b", "c"], ["e", "f", "g"]) + @Test("Binding pattern assignment") + public bindingPattern(input: string[], values: string[]) { + const pattern = input.join(","); + const initializer = values.map(v => `"${v}"`).join(","); + + const lua = util.transpileString(`const [${pattern}] = [${initializer}]; return [${pattern}].join("-");`); + const result = util.executeLua(lua); + + Expect(result).toBe(values.slice(0, input.length).join("-")); + } + + @Test("Ellipsis binding pattern") + public ellipsisBindingPattern() { + Expect(() => util.transpileString("let [a,b,...c] = [1,2,3];")) + .toThrowError(Error, "Ellipsis destruction is not allowed."); + } } diff --git a/test/unit/error.spec.ts b/test/unit/error.spec.ts index ed2e66f91..51f5c33c0 100644 --- a/test/unit/error.spec.ts +++ b/test/unit/error.spec.ts @@ -6,7 +6,7 @@ export class LuaErrorTests { @Test("throwString") public trowString() { // Transpile - let lua = util.transpileString( + const lua = util.transpileString( `throw "Some Error"` ); // Assert @@ -17,7 +17,7 @@ export class LuaErrorTests { public throwError() { // Transpile & Asser Expect(() => { - let lua = util.transpileString( + const lua = util.transpileString( `throw Error("Some Error")` ); }).toThrowError(Error, "Unsupported throw expression, only string literals are supported"); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 9664fd485..4f964aada 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -1,4 +1,4 @@ -import { Expect, Test, TestCase, FocusTest } from "alsatian"; +import { Expect, Test, TestCase } from "alsatian"; import { LuaTarget } from "../../src/Transpiler"; import * as ts from "typescript"; @@ -175,6 +175,16 @@ export class ExpressionTests { Expect(result).toBe(3); } + @Test("Null Expression") + public nullExpression() { + Expect(util.transpileString("null")).toBe("nil"); + } + + @Test("Undefined Expression") + public undefinedExpression() { + Expect(util.transpileString("undefined")).toBe("nil"); + } + @TestCase([], 7) @TestCase([5], 9) @TestCase([1, 2], 3) @@ -230,6 +240,7 @@ export class ExpressionTests { } @TestCase("= 4", 4 + 4) + @TestCase("-= 3", 4 - 3 + 4) @TestCase("+= 3", 4 + 3 + 4) @TestCase("*= 3", 4 * 3 + 4) @TestCase("/= 3", 4 / 3 + 4) diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index 18ed59a9e..6a0c12ea6 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -1,5 +1,5 @@ -import { Expect, Test, TestCase, IgnoreTest } from "alsatian"; -import * as util from "../src/util" +import { Expect, Test, TestCase } from "alsatian"; +import * as util from "../src/util"; export class StringTests { @@ -20,12 +20,12 @@ export class StringTests { @Test("String.fromCharCode") public stringFromCharcode(inp: number[], expected: string) { // Transpile - let lua = util.transpileString( - `return String.fromCharCode(${inp.toString()})`, + const lua = util.transpileString( + `return String.fromCharCode(${inp.toString()})` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(String.fromCharCode(...inp)); @@ -42,12 +42,12 @@ export class StringTests { const b1 = typeof(b) === "string" ? "'" + b + "'" : b; const c1 = typeof(c) === "string" ? "'" + c + "'" : c; - let lua = util.transpileString( + const lua = util.transpileString( "let a = " + a1 + "; let b = " + b1 + "; let c = " + c1 + "; return `${a} ${b} test ${c}`;" ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(`${a} ${b} test ${c}`); @@ -61,12 +61,12 @@ export class StringTests { @Test("string.replace") public replace(inp: string, searchValue: string, replaceValue: string) { // Transpile - let lua = util.transpileString( - `return "${inp}".replace("${searchValue}", "${replaceValue}")`, + const lua = util.transpileString( + `return "${inp}".replace("${searchValue}", "${replaceValue}")` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.replace(searchValue, replaceValue)); @@ -79,15 +79,15 @@ export class StringTests { @TestCase([42, "hello"], "42hello") @Test("string.concat[+]") public concat(inp: any[], expected: string) { - let concatStr = inp.map(elem => typeof(elem) === "string" ? `"${elem}"` : elem).join(" + "); + const concatStr = inp.map(elem => typeof(elem) === "string" ? `"${elem}"` : elem).join(" + "); // Transpile - let lua = util.transpileString( - `return ${concatStr}`, + const lua = util.transpileString( + `return ${concatStr}` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(expected); @@ -100,17 +100,35 @@ export class StringTests { @Test("string.indexOf") public indexOf(inp: string, searchValue: string) { // Transpile - let lua = util.transpileString( - `return "${inp}".indexOf("${searchValue}")`, + const lua = util.transpileString( + `return "${inp}".indexOf("${searchValue}")` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.indexOf(searchValue)); } + @TestCase("hello test", "t", 5) + @TestCase("hello test", "t", 6) + @TestCase("hello test", "t", 7) + @TestCase("hello test", "h", 4) + @Test("string.indexOf with offset") + public indexOfOffset(inp: string, searchValue: string, offset: number) { + // Transpile + const lua = util.transpileString( + `return "${inp}".indexOf("${searchValue}", ${offset})` + ); + + // Execute + const result = util.executeLua(lua); + + // Assert + Expect(result).toBe(inp.indexOf(searchValue, offset)); + } + @TestCase("hello test", 0) @TestCase("hello test", 1) @TestCase("hello test", 1, 2) @@ -118,13 +136,13 @@ export class StringTests { @Test("string.substring") public substring(inp: string, start: number, end?: number) { // Transpile - let paramStr = end ? `${start}, ${end}` : `${start}`; - let lua = util.transpileString( - `return "${inp}".substring(${paramStr})`, + const paramStr = end ? `${start}, ${end}` : `${start}`; + const lua = util.transpileString( + `return "${inp}".substring(${paramStr})` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.substring(start, end)); @@ -136,12 +154,12 @@ export class StringTests { @Test("string.length") public length(inp: string, expected: number) { // Transpile - let lua = util.transpileString( - `return "${inp}".length`, + const lua = util.transpileString( + `return "${inp}".length` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.length); @@ -151,12 +169,12 @@ export class StringTests { @Test("string.toLowerCase") public toLowerCase(inp: string) { // Transpile - let lua = util.transpileString( - `return "${inp}".toLowerCase()`, + const lua = util.transpileString( + `return "${inp}".toLowerCase()` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.toLowerCase()); @@ -166,12 +184,12 @@ export class StringTests { @Test("string.toUpperCase") public toUpperCase(inp: string) { // Transpile - let lua = util.transpileString( - `return "${inp}".toUpperCase()`, + const lua = util.transpileString( + `return "${inp}".toUpperCase()` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.toUpperCase()); @@ -187,12 +205,12 @@ export class StringTests { @Test("string.split") public split(inp: string, separator: string) { // Transpile - let lua = util.transpileString( - `return JSONStringify("${inp}".split("${separator}"))`, + const lua = util.transpileString( + `return JSONStringify("${inp}".split("${separator}"))` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(JSON.stringify(inp.split(separator))); @@ -205,15 +223,27 @@ export class StringTests { @Test("string.charAt") public charAt(inp: string, index: number) { // Transpile - let lua = util.transpileString( - `return "${inp}".charAt(${index})`, + const lua = util.transpileString( + `return "${inp}".charAt(${index})` ); // Execute - let result = util.executeLua(lua); + const result = util.executeLua(lua); // Assert Expect(result).toBe(inp.charAt(index)); } + @TestCase("abcd", 3) + @TestCase("abcde", 3) + @TestCase("abcde", 0) + @TestCase("a", 0) + @Test("string index") + public index(input: string, index: number) { + const lua = util.transpileString(`return "${input}"[${index}];`); + + const result = util.executeLua(lua); + + Expect(result).toBe(input[index]); + } } diff --git a/tslint.json b/tslint.json index a8b9bce9f..2a9c29441 100644 --- a/tslint.json +++ b/tslint.json @@ -6,6 +6,7 @@ "jsRules": {}, "rules": { "align": [true, "parameters", "statements", "arguments", "members", "elements"], + "arrow-parens": [true, "ban-single-arg-parens"], "class-name": true, "no-bitwise": false, "indent": [true, "spaces", 4],