diff --git a/package.json b/package.json index fa2670998..2a79b9825 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "typescript-to-lua", - "version": "1.33.2", + "name": "its-typescript-to-lua", + "version": "1.33.2-7", "description": "A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!", "repository": "https://github.com/TypeScriptToLua/TypeScriptToLua", "homepage": "https://typescripttolua.github.io/", diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 7f5562c07..87dc7ad96 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -46,6 +46,7 @@ export interface TypeScriptToLuaOptions { tstlVerbose?: boolean; lua51AllowTryCatchInAsyncAwait?: boolean; measurePerformance?: boolean; + arrayIndexModification?: ArrayIndexModification; } export type CompilerOptions = OmitIndexSignature & @@ -76,9 +77,17 @@ export enum BuildMode { Library = "library", } +export enum ArrayIndexModification { + Never = "never", + Always = "always", +} + export const isBundleEnabled = (options: CompilerOptions) => options.luaBundle !== undefined && options.luaBundleEntry !== undefined; +export const arrayIndexModificationEnabled = (options: CompilerOptions) => + options.arrayIndexModification !== ArrayIndexModification.Never; + export function validateOptions(options: CompilerOptions): ts.Diagnostic[] { const diagnostics: ts.Diagnostic[] = []; diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index b0a02dfaf..1edb29d86 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -664,26 +664,27 @@ export class LuaPrinter { public printFunctionExpression(expression: lua.FunctionExpression): SourceNode { const chunks: SourceChunk[] = []; + chunks.push("\n"); chunks.push("function("); chunks.push(...this.printFunctionParameters(expression)); chunks.push(")"); - if (lua.isInlineFunctionExpression(expression)) { - const returnStatement = expression.body.statements[0]; - chunks.push(" "); - const returnNode: SourceChunk[] = [ - "return ", - ...this.joinChunksWithComma(returnStatement.expressions.map(e => this.printExpression(e))), - ]; - chunks.push(this.createSourceNode(returnStatement, returnNode)); - chunks.push(this.createSourceNode(expression, " end")); - } else { - chunks.push("\n"); - this.pushIndent(); - chunks.push(this.printBlock(expression.body)); - this.popIndent(); - chunks.push(this.indent(this.createSourceNode(expression, "end"))); - } + // if (lua.isInlineFunctionExpression(expression)) { + // const returnStatement = expression.body.statements[0]; + // chunks.push(" "); + // const returnNode: SourceChunk[] = [ + // "return ", + // ...this.joinChunksWithComma(returnStatement.expressions.map(e => this.printExpression(e))), + // ]; + // chunks.push(this.createSourceNode(returnStatement, returnNode)); + // chunks.push(this.createSourceNode(expression, " end")); + // } else { + chunks.push("\n"); + this.pushIndent(); + chunks.push(this.printBlock(expression.body)); + this.popIndent(); + chunks.push(this.indent(this.createSourceNode(expression, "end"))); + // } return this.createSourceNode(expression, chunks); } @@ -867,17 +868,23 @@ export class LuaPrinter { protected printExpressionList(expressions: lua.Expression[]): SourceChunk[] { const chunks: SourceChunk[] = []; - if (this.isSimpleExpressionList(expressions)) { - chunks.push(...this.joinChunksWithComma(expressions.map(e => this.printExpression(e)))); - } else { - chunks.push("\n"); - this.pushIndent(); - for (const [index, expression] of expressions.entries()) { + for (const [index, expression] of expressions.entries()) { + if (lua.isCallExpression(expression)) { + if (index === 0) { + chunks.push("\n"); + } else { + chunks.pop(); + chunks.push(",\n"); + } + this.pushIndent(); const tail = index < expressions.length - 1 ? ",\n" : "\n"; chunks.push(this.indent(), this.printExpression(expression), tail); + this.popIndent(); + chunks.push(this.indent()); + } else { + const tail = index < expressions.length - 1 ? ", " : ""; + chunks.push(this.printExpression(expression), tail); } - this.popIndent(); - chunks.push(this.indent()); } return chunks; diff --git a/src/cli/parse.ts b/src/cli/parse.ts index ca4f039ad..0395c6c55 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -1,6 +1,8 @@ import * as ts from "typescript"; import { BuildMode, CompilerOptions, LuaLibImportKind, LuaTarget } from "../CompilerOptions"; import * as cliDiagnostics from "./diagnostics"; +import * as fs from "fs"; +import * as path from "path"; export interface ParsedCommandLine extends ts.ParsedCommandLine { options: CompilerOptions; @@ -104,8 +106,38 @@ export const optionDeclarations: CommandLineOption[] = [ description: "Measure performance of the tstl compiler.", type: "boolean", }, + { + name: "arrayIndexModification", + description: "If array indexing operations should be modified (0 based to 1 based).", + type: "string", + }, ]; +function substituteFirmwareLocation(config: ts.ParsedCommandLine) { + if (config.options.outDir?.includes("${firmwareLocation}")) { + let substitution = ""; + const baseDir = process.cwd(); + const paths = fs.readdirSync(baseDir, { recursive: true }); + const filtered = paths.filter(dir => dir.toString().endsWith("sharedscripts_default")); + if (filtered.length === 1) { + substitution = path.dirname(filtered[0].toString()); + } + if (substitution && substitution !== "") { + config.options.outDir = path.normalize(config.options.outDir?.replace("${firmwareLocation}", substitution)); + } else { + config.errors.push({ + file: undefined, + start: undefined, + length: undefined, + category: ts.DiagnosticCategory.Error, + code: 42, + source: "typescript-to-lua", + messageText: "Cannot find firmware location (sharedscripts_default)" + }); + } + } +} + export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine { let hasRootLevelOptions = false; for (const [name, rawValue] of Object.entries(parsedConfigFile.raw)) { @@ -135,6 +167,8 @@ export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): } } + substituteFirmwareLocation(parsedConfigFile); + return parsedConfigFile; } @@ -176,6 +210,8 @@ function updateParsedCommandLine(parsedCommandLine: ts.ParsedCommandLine, args: } } + substituteFirmwareLocation(parsedCommandLine); + return parsedCommandLine; } diff --git a/src/transformation/visitors/access.ts b/src/transformation/visitors/access.ts index 7c96bdcd8..b38199df2 100644 --- a/src/transformation/visitors/access.ts +++ b/src/transformation/visitors/access.ts @@ -25,6 +25,7 @@ import { import { SyntaxKind } from "typescript"; import { getCustomNameFromSymbol } from "./identifier"; import { getSymbolExportScope, isSymbolExported } from "../utils/export"; +import { arrayIndexModificationEnabled } from "../../CompilerOptions"; function addOneToArrayAccessArgument( context: TransformationContext, @@ -34,7 +35,10 @@ function addOneToArrayAccessArgument( const type = context.checker.getTypeAtLocation(node.expression); const argumentType = context.checker.getTypeAtLocation(node.argumentExpression); if (isArrayType(context, type) && isNumberType(context, argumentType)) { - return addToNumericExpression(index, 1); + const options = context.program.getCompilerOptions(); + if (arrayIndexModificationEnabled(options)) { + return addToNumericExpression(index, 1); + } } return index; } diff --git a/src/transformation/visitors/delete.ts b/src/transformation/visitors/delete.ts index efa879c2e..1409a47f1 100644 --- a/src/transformation/visitors/delete.ts +++ b/src/transformation/visitors/delete.ts @@ -6,6 +6,7 @@ import { unsupportedProperty } from "../utils/diagnostics"; import { isArrayType, isNumberType } from "../utils/typescript"; import { addToNumericExpression } from "../utils/lua-ast"; import { transformOptionalDeleteExpression } from "./optional-chaining"; +import { arrayIndexModificationEnabled } from "../../CompilerOptions"; export const transformDeleteExpression: FunctionVisitor = (node, context) => { if (ts.isOptionalChain(node.expression)) { @@ -27,7 +28,10 @@ export const transformDeleteExpression: FunctionVisitor = ( const argumentType = context.checker.getTypeAtLocation(node.expression.argumentExpression); if (isArrayType(context, type) && isNumberType(context, argumentType)) { - propertyExpression = addToNumericExpression(propertyExpression, 1); + const options = context.program.getCompilerOptions(); + if (arrayIndexModificationEnabled(options)) { + propertyExpression = addToNumericExpression(propertyExpression, 1); + } } } diff --git a/src/transformation/visitors/enum.ts b/src/transformation/visitors/enum.ts index 3c496344e..9de658a45 100644 --- a/src/transformation/visitors/enum.ts +++ b/src/transformation/visitors/enum.ts @@ -2,7 +2,7 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; import { AnnotationKind, getTypeAnnotations } from "../utils/annotations"; -import { getSymbolExportScope } from "../utils/export"; +import { addExportToIdentifier, getSymbolExportScope } from "../utils/export"; import { createLocalOrExportedOrGlobalDeclaration } from "../utils/lua-ast"; import { isFirstDeclaration } from "../utils/typescript"; import { transformIdentifier } from "./identifier"; @@ -32,7 +32,7 @@ export const transformEnumDeclaration: FunctionVisitor = (no if (!membersOnly && isFirstDeclaration(context, node)) { const name = transformIdentifier(context, node.name); const table = lua.createBinaryExpression( - lua.cloneIdentifier(name), + addExportToIdentifier(context, name), lua.createTableExpression(), lua.SyntaxKind.OrOperator ); diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index f421be2c5..98f186f3f 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -203,3 +203,19 @@ test("enum merging multiple files", () => { ) .expectToMatchJsResult(); }); + +test("enum nested in namespace", () => { + util.testModule` + namespace A { + export enum TestEnum { + C, + D + } + } + `.tap(builder => { + const lua = builder.getMainLuaCodeChunk(); + expect(lua).toMatch( + 'A = A or ({})\ndo\n A.TestEnum = A.TestEnum or ({})\n A.TestEnum.C = 0\n A.TestEnum[A.TestEnum.C] = "C"\n A.TestEnum.D = 1\n A.TestEnum[A.TestEnum.D] = "D"\nend' + ); + }); +}); diff --git a/test/util.ts b/test/util.ts index 501b24bfb..e492ad196 100644 --- a/test/util.ts +++ b/test/util.ts @@ -165,6 +165,7 @@ export abstract class TestBuilder { moduleResolution: ts.ModuleResolutionKind.Node10, resolveJsonModule: true, sourceMap: true, + arrayIndexModification: tstl.ArrayIndexModification.Never, }; public setOptions(options: tstl.CompilerOptions = {}): this { this.throwIfProgramExists("setOptions"); diff --git a/tsconfig-schema.json b/tsconfig-schema.json index 87366ea76..8e0476ac8 100644 --- a/tsconfig-schema.json +++ b/tsconfig-schema.json @@ -102,6 +102,11 @@ "measurePerformance": { "description": "Measure and report performance of the tstl compiler.", "type": "boolean" + }, + "arrayIndexModification": { + "description": "If array indexing operations should be modified (0 based to 1 based).", + "type": "string", + "enum": ["never", "always"] } }, "dependencies": {