diff --git a/.codecov.yml b/.codecov.yml index d8bafa304..8fcaf7415 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,4 +1,4 @@ -comment: off +comment: false coverage: status: @@ -7,5 +7,5 @@ coverage: target: 90 threshold: null base: auto - changes: off - patch: off + changes: false + patch: false diff --git a/.gitignore b/.gitignore index 0878aa261..09a293fc7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,8 @@ -/node_modules +node_modules /dist /coverage yarn.lock .vscode .idea - -# Release -*.tgz - -# OSX .DS_Store -*.lcov diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index e9fb0b291..000000000 --- a/.prettierrc.js +++ /dev/null @@ -1,10 +0,0 @@ -const isCI = require("is-ci"); - -/** @type {import("prettier").Options} */ -module.exports = { - printWidth: 120, - tabWidth: 4, - trailingComma: "es5", - endOfLine: isCI ? "lf" : "auto", - overrides: [{ files: ["**/*.md", "**/*.yml"], options: { tabWidth: 2 } }], -}; diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..f8774a2f2 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "trailingComma": "es5", + "endOfLine": "lf", + "overrides": [{ "files": ["**/*.md", "**/*.yml"], "options": { "tabWidth": 2 } }] +} diff --git a/package.json b/package.json index cb25b2809..be2ebd2d4 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,6 @@ "lint:prettier": "prettier --check \"**/*.{js,ts,yml,json,md}\" || (echo 'Run `npm run fix:prettier` to fix it.' && exit 1)", "lint:tslint": "tslint -p . && tslint -p test && tslint -p src/lualib", "fix:prettier": "prettier --check --write \"**/*.{js,ts,yml,json,md}\"", - "release-major": "npm version major", - "release-minor": "npm version minor", - "release-patch": "npm version patch", "preversion": "npm run build && npm test", "postversion": "git push && git push --tags" }, diff --git a/src/LuaAST.ts b/src/LuaAST.ts index 8ab4f7417..d39639213 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -5,6 +5,7 @@ // because we don't create the AST from text import * as ts from "typescript"; +import { castArray } from "./utils"; export enum SyntaxKind { Block, @@ -236,14 +237,8 @@ export function createVariableDeclarationStatement( tsOriginal?: ts.Node ): VariableDeclarationStatement { const statement = createNode(SyntaxKind.VariableDeclarationStatement, tsOriginal) as VariableDeclarationStatement; - statement.left = Array.isArray(left) ? left : [left]; - - if (Array.isArray(right)) { - statement.right = right; - } else if (right) { - statement.right = [right]; - } - + statement.left = castArray(left); + if (right) statement.right = castArray(right); return statement; } @@ -264,14 +259,8 @@ export function createAssignmentStatement( tsOriginal?: ts.Node ): AssignmentStatement { const statement = createNode(SyntaxKind.AssignmentStatement, tsOriginal) as AssignmentStatement; - statement.left = Array.isArray(left) ? left : [left]; - - if (Array.isArray(right)) { - statement.right = right; - } else { - statement.right = right ? [right] : []; - } - + statement.left = castArray(left); + statement.right = right ? castArray(right) : []; return statement; } @@ -429,14 +418,14 @@ export function createLabelStatement(name: string, tsOriginal?: ts.Node): LabelS export interface ReturnStatement extends Statement { kind: SyntaxKind.ReturnStatement; - expressions?: Expression[]; + expressions: Expression[]; } export function isReturnStatement(node: Node): node is ReturnStatement { return node.kind === SyntaxKind.ReturnStatement; } -export function createReturnStatement(expressions?: Expression[], tsOriginal?: ts.Node): ReturnStatement { +export function createReturnStatement(expressions: Expression[], tsOriginal?: ts.Node): ReturnStatement { const statement = createNode(SyntaxKind.ReturnStatement, tsOriginal) as ReturnStatement; statement.expressions = expressions; return statement; @@ -496,11 +485,7 @@ export function isBooleanLiteral(node: Node): node is BooleanLiteral { } export function createBooleanLiteral(value: boolean, tsOriginal?: ts.Node): BooleanLiteral { - if (value) { - return createNode(SyntaxKind.TrueKeyword, tsOriginal) as BooleanLiteral; - } else { - return createNode(SyntaxKind.FalseKeyword, tsOriginal) as BooleanLiteral; - } + return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, tsOriginal) as BooleanLiteral; } // TODO Call this DotsLiteral or DotsKeyword? @@ -518,7 +503,7 @@ export function createDotsLiteral(tsOriginal?: ts.Node): DotsLiteral { // StringLiteral / NumberLiteral // TODO TS uses the export interface "LiteralLikeNode" with a "text: string" member -// but since we dont parse from text i think we can simplify by just having a value member +// but since we don't parse from text I think we can simplify by just having a value member // TODO NumericLiteral or NumberLiteral? export interface NumericLiteral extends Expression { @@ -552,16 +537,15 @@ export function createStringLiteral(value: string, tsOriginal?: ts.Node): String } export enum FunctionExpressionFlags { - None = 0x0, - Inline = 0x1, // Keep function on same line - Declaration = 0x2, // Prefer declaration syntax `function foo()` over assignment syntax `foo = function()` + None = 1 << 0, + Inline = 1 << 1, // Keep function body on same line + Declaration = 1 << 2, // Prefer declaration syntax `function foo()` over assignment syntax `foo = function()` } export interface FunctionExpression extends Expression { kind: SyntaxKind.FunctionExpression; params?: Identifier[]; dots?: DotsLiteral; - restParamName?: Identifier; body: Block; flags: FunctionExpressionFlags; } @@ -574,7 +558,6 @@ export function createFunctionExpression( body: Block, params?: Identifier[], dots?: DotsLiteral, - restParamName?: Identifier, flags = FunctionExpressionFlags.None, tsOriginal?: ts.Node ): FunctionExpression { @@ -582,7 +565,6 @@ export function createFunctionExpression( expression.body = body; expression.params = params; expression.dots = dots; - expression.restParamName = restParamName; expression.flags = flags; return expression; } @@ -671,7 +653,7 @@ export function createBinaryExpression( export interface CallExpression extends Expression { kind: SyntaxKind.CallExpression; expression: Expression; - params?: Expression[]; + params: Expression[]; } export function isCallExpression(node: Node): node is CallExpression { @@ -680,7 +662,7 @@ export function isCallExpression(node: Node): node is CallExpression { export function createCallExpression( expression: Expression, - params?: Expression[], + params: Expression[], tsOriginal?: ts.Node ): CallExpression { const callExpression = createNode(SyntaxKind.CallExpression, tsOriginal) as CallExpression; @@ -693,7 +675,7 @@ export interface MethodCallExpression extends Expression { kind: SyntaxKind.MethodCallExpression; prefixExpression: Expression; name: Identifier; - params?: Expression[]; + params: Expression[]; } export function isMethodCallExpression(node: Node): node is MethodCallExpression { @@ -703,7 +685,7 @@ export function isMethodCallExpression(node: Node): node is MethodCallExpression export function createMethodCallExpression( prefixExpression: Expression, name: Identifier, - params?: Expression[], + params: Expression[], tsOriginal?: ts.Node ): MethodCallExpression { const callExpression = createNode(SyntaxKind.MethodCallExpression, tsOriginal) as MethodCallExpression; diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 816bb1aea..dc94c6b53 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -6,7 +6,7 @@ import * as lua from "./LuaAST"; import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib"; import { isValidLuaIdentifier } from "./transformation/utils/safe-names"; import { EmitHost } from "./transpilation"; -import { trimExtension } from "./utils"; +import { intersperse, trimExtension } from "./utils"; // https://www.lua.org/pil/2.4.html // https://www.ecma-international.org/ecma-262/10.0/index.html#table-34 @@ -271,8 +271,6 @@ export class LuaPrinter { protected printStatementArray(statements: lua.Statement[]): SourceChunk[] { const statementNodes: SourceNode[] = []; for (const [index, statement] of statements.entries()) { - if (this.isStatementEmpty(statement)) continue; - const node = this.printStatement(statement); if ( @@ -288,7 +286,7 @@ export class LuaPrinter { if (lua.isReturnStatement(statement)) break; } - return statementNodes.length > 0 ? [...this.joinChunks("\n", statementNodes), "\n"] : []; + return statementNodes.length > 0 ? [...intersperse(statementNodes, "\n"), "\n"] : []; } public printStatement(statement: lua.Statement): SourceNode { @@ -345,21 +343,11 @@ export class LuaPrinter { // Print all local functions as `local function foo()` instead of `local foo = function` to allow recursion chunks.push(this.printFunctionDefinition(statement)); } else { - chunks.push( - ...this.joinChunks( - ", ", - statement.left.map(e => this.printExpression(e)) - ) - ); + chunks.push(...this.joinChunksWithComma(statement.left.map(e => this.printExpression(e)))); if (statement.right) { chunks.push(" = "); - chunks.push( - ...this.joinChunks( - ", ", - statement.right.map(e => this.printExpression(e)) - ) - ); + chunks.push(...this.joinChunksWithComma(statement.right.map(e => this.printExpression(e)))); } } @@ -383,19 +371,9 @@ export class LuaPrinter { } } - chunks.push( - ...this.joinChunks( - ", ", - statement.left.map(e => this.printExpression(e)) - ) - ); + chunks.push(...this.joinChunksWithComma(statement.left.map(e => this.printExpression(e)))); chunks.push(" = "); - chunks.push( - ...this.joinChunks( - ", ", - statement.right.map(e => this.printExpression(e)) - ) - ); + chunks.push(...this.joinChunksWithComma(statement.right.map(e => this.printExpression(e)))); return this.createSourceNode(statement, chunks); } @@ -479,14 +457,8 @@ export class LuaPrinter { } public printForInStatement(statement: lua.ForInStatement): SourceNode { - const names = this.joinChunks( - ", ", - statement.names.map(i => this.printIdentifier(i)) - ); - const expressions = this.joinChunks( - ", ", - statement.expressions.map(e => this.printExpression(e)) - ); + const names = this.joinChunksWithComma(statement.names.map(i => this.printIdentifier(i))); + const expressions = this.joinChunksWithComma(statement.expressions.map(e => this.printExpression(e))); const chunks: SourceChunk[] = []; @@ -509,18 +481,13 @@ export class LuaPrinter { } public printReturnStatement(statement: lua.ReturnStatement): SourceNode { - if (!statement.expressions || statement.expressions.length === 0) { + if (statement.expressions.length === 0) { return this.createSourceNode(statement, this.indent("return")); } const chunks: SourceChunk[] = []; - chunks.push( - ...this.joinChunks( - ", ", - statement.expressions.map(e => this.printExpression(e)) - ) - ); + chunks.push(...this.joinChunksWithComma(statement.expressions.map(e => this.printExpression(e)))); return this.createSourceNode(statement, [this.indent(), "return ", ...chunks]); } @@ -587,23 +554,17 @@ export class LuaPrinter { } public printBooleanLiteral(expression: lua.BooleanLiteral): SourceNode { - if (expression.kind === lua.SyntaxKind.TrueKeyword) { - return this.createSourceNode(expression, "true"); - } else { - return this.createSourceNode(expression, "false"); - } + return this.createSourceNode(expression, expression.kind === lua.SyntaxKind.TrueKeyword ? "true" : "false"); } private printFunctionParameters(expression: lua.FunctionExpression): SourceChunk[] { - const parameterChunks: SourceNode[] = expression.params - ? expression.params.map(i => this.printIdentifier(i)) - : []; + const parameterChunks = (expression.params ?? []).map(i => this.printIdentifier(i)); if (expression.dots) { parameterChunks.push(this.printDotsLiteral(expression.dots)); } - return this.joinChunks(", ", parameterChunks); + return this.joinChunksWithComma(parameterChunks); } public printFunctionExpression(expression: lua.FunctionExpression): SourceNode { @@ -618,10 +579,7 @@ export class LuaPrinter { chunks.push(" "); const returnNode: SourceChunk[] = [ "return ", - ...this.joinChunks( - ", ", - returnStatement.expressions.map(e => this.printExpression(e)) - ), + ...this.joinChunksWithComma(returnStatement.expressions.map(e => this.printExpression(e))), ]; chunks.push(this.createSourceNode(returnStatement, returnNode)); chunks.push(this.createSourceNode(expression, " end")); @@ -770,38 +728,22 @@ export class LuaPrinter { return new SourceNode(null, null, this.sourceFile, LuaPrinter.operatorMap[kind]); } - protected isStatementEmpty(statement: lua.Statement): boolean { - return lua.isDoStatement(statement) && (!statement.statements || statement.statements.length === 0); - } - - protected joinChunks(separator: string, chunks: SourceChunk[]): SourceChunk[] { - const result = []; - for (let i = 0; i < chunks.length; i++) { - result.push(chunks[i]); - if (i < chunks.length - 1) { - result.push(separator); - } - } - return result; + protected joinChunksWithComma(chunks: SourceChunk[]): SourceChunk[] { + return intersperse(chunks, ", "); } protected printExpressionList(expressions: lua.Expression[]): SourceChunk[] { const chunks: SourceChunk[] = []; if (expressions.every(isSimpleExpression)) { - chunks.push( - ...this.joinChunks( - ", ", - expressions.map(e => this.printExpression(e)) - ) - ); + chunks.push(...this.joinChunksWithComma(expressions.map(e => this.printExpression(e)))); } else { chunks.push("\n"); this.pushIndent(); - expressions.forEach((p, i) => { - const tail = i < expressions.length - 1 ? ",\n" : "\n"; - chunks.push(this.indent(), this.printExpression(p), tail); - }); + for (const [index, expression] of expressions.entries()) { + const tail = index < expressions.length - 1 ? ",\n" : "\n"; + chunks.push(this.indent(), this.printExpression(expression), tail); + } this.popIndent(); chunks.push(this.indent()); } diff --git a/src/cli/information.ts b/src/cli/information.ts index 08ffb12f4..4c4b3ce1a 100644 --- a/src/cli/information.ts +++ b/src/cli/information.ts @@ -20,7 +20,7 @@ export function getHelpString(): string { result += "Options:\n"; for (const option of optionDeclarations) { const aliasStrings = (option.aliases ?? []).map(a => "-" + a); - const optionString = aliasStrings.concat(["--" + option.name]).join("|"); + const optionString = [...aliasStrings, "--" + option.name].join("|"); const valuesHint = option.type === "enum" ? option.choices.join("|") : option.type; const spacing = " ".repeat(Math.max(1, 45 - optionString.length - valuesHint.length)); diff --git a/src/transformation/context/context.ts b/src/transformation/context/context.ts index ebc8dfea1..ea375766b 100644 --- a/src/transformation/context/context.ts +++ b/src/transformation/context/context.ts @@ -1,6 +1,7 @@ import * as ts from "typescript"; import { CompilerOptions, LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; +import { castArray } from "../../utils"; import { unwrapVisitorResult } from "../utils/lua-ast"; import { ExpressionLikeNode, ObjectVisitor, StatementLikeNode, VisitorMap } from "./visitors"; @@ -87,16 +88,10 @@ export class TransformationContext { } public transformStatements(node: StatementLikeNode | readonly StatementLikeNode[]): lua.Statement[] { - return Array.isArray(node) - ? node.flatMap(n => this.transformStatements(n)) - : // TODO: https://github.com/microsoft/TypeScript/pull/28916 - (this.transformNode(node as StatementLikeNode) as lua.Statement[]); + return castArray(node).flatMap(n => this.transformNode(n) as lua.Statement[]); } public superTransformStatements(node: StatementLikeNode | readonly StatementLikeNode[]): lua.Statement[] { - return Array.isArray(node) - ? node.flatMap(n => this.superTransformStatements(n)) - : // TODO: https://github.com/microsoft/TypeScript/pull/28916 - (this.superTransformNode(node as StatementLikeNode) as lua.Statement[]); + return castArray(node).flatMap(n => this.superTransformNode(n) as lua.Statement[]); } } diff --git a/src/transformation/utils/annotations.ts b/src/transformation/utils/annotations.ts index b3b2c0697..8f5936126 100644 --- a/src/transformation/utils/annotations.ts +++ b/src/transformation/utils/annotations.ts @@ -15,7 +15,7 @@ export enum AnnotationKind { LuaTable = "luaTable", NoSelf = "noSelf", NoSelfInFile = "noSelfInFile", - VarArg = "varArg", + Vararg = "vararg", ForRange = "forRange", } @@ -170,9 +170,9 @@ export function isLuaIteratorType(context: TransformationContext, node: ts.Node) return getTypeAnnotations(type).has(AnnotationKind.LuaIterator); } -export function isVarArgType(context: TransformationContext, node: ts.Node): boolean { +export function isVarargType(context: TransformationContext, node: ts.Node): boolean { const type = context.checker.getTypeAtLocation(node); - return getTypeAnnotations(type).has(AnnotationKind.VarArg); + return getTypeAnnotations(type).has(AnnotationKind.Vararg); } export function isForRangeType(context: TransformationContext, node: ts.Node): boolean { diff --git a/src/transformation/utils/assignment-validation.ts b/src/transformation/utils/assignment-validation.ts index 05cfea467..a31a24fdf 100644 --- a/src/transformation/utils/assignment-validation.ts +++ b/src/transformation/utils/assignment-validation.ts @@ -67,18 +67,19 @@ export function validateAssignment( fromType.symbol.members ) { // Recurse into interfaces - toType.symbol.members.forEach((toMember, memberName) => { + toType.symbol.members.forEach((toMember, escapedMemberName) => { if (fromType.symbol.members) { - const fromMember = fromType.symbol.members.get(memberName); + const fromMember = fromType.symbol.members.get(escapedMemberName); if (fromMember) { const toMemberType = context.checker.getTypeOfSymbolAtLocation(toMember, node); const fromMemberType = context.checker.getTypeOfSymbolAtLocation(fromMember, node); + const memberName = ts.unescapeLeadingUnderscores(escapedMemberName); validateAssignment( context, node, fromMemberType, toMemberType, - toName ? `${toName}.${memberName}` : memberName.toString() + toName ? `${toName}.${memberName}` : memberName ); } } @@ -99,10 +100,10 @@ function validateFunctionAssignment( if (fromContext === ContextType.Mixed || toContext === ContextType.Mixed) { context.diagnostics.push(unsupportedOverloadAssignment(node, toName)); } else if (fromContext !== toContext && fromContext !== ContextType.None && toContext !== ContextType.None) { - if (toContext === ContextType.Void) { - context.diagnostics.push(unsupportedNoSelfFunctionConversion(node, toName)); - } else { - context.diagnostics.push(unsupportedSelfFunctionConversion(node, toName)); - } + context.diagnostics.push( + toContext === ContextType.Void + ? unsupportedNoSelfFunctionConversion(node, toName) + : unsupportedSelfFunctionConversion(node, toName) + ); } } diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index 318c4abaf..dfce4b2fc 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; -import { assert } from "../../utils"; +import { assert, castArray } from "../../utils"; import { TransformationContext } from "../context"; import { createExportedIdentifier, getIdentifierExportScope } from "./export"; import { peekScope, ScopeType } from "./scope"; @@ -9,13 +9,7 @@ import { isFunctionType } from "./typescript"; export type OneToManyVisitorResult = T | T[] | undefined; export function unwrapVisitorResult(result: OneToManyVisitorResult): T[] { - if (result === undefined || result === null) { - return []; - } else if (Array.isArray(result)) { - return result; - } else { - return [result]; - } + return result === undefined ? [] : castArray(result); } export function createSelfIdentifier(tsOriginal?: ts.Node): lua.Identifier { @@ -51,10 +45,9 @@ export function createImmediatelyInvokedFunctionExpression( result: lua.Expression | lua.Expression[], tsOriginal?: ts.Node ): lua.CallExpression { - const body = statements ? statements.slice(0) : []; - body.push(lua.createReturnStatement(Array.isArray(result) ? result : [result])); + const body = [...statements, lua.createReturnStatement(castArray(result))]; const flags = statements.length === 0 ? lua.FunctionExpressionFlags.Inline : lua.FunctionExpressionFlags.None; - const iife = lua.createFunctionExpression(lua.createBlock(body), undefined, undefined, undefined, flags); + const iife = lua.createFunctionExpression(lua.createBlock(body), undefined, undefined, flags); return lua.createCallExpression(iife, [], tsOriginal); } @@ -121,7 +114,7 @@ export function createLocalOrExportedOrGlobalDeclaration( const isFunctionDeclaration = tsOriginal !== undefined && ts.isFunctionDeclaration(tsOriginal); - const identifiers = Array.isArray(lhs) ? lhs : [lhs]; + const identifiers = castArray(lhs); if (identifiers.length === 0) { return []; } diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 90ae8e4b0..bd8099afa 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -2,7 +2,7 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { transformBuiltinCallExpression } from "../builtins"; import { FunctionVisitor, TransformationContext } from "../context"; -import { isInTupleReturnFunction, isTupleReturnCall, isVarArgType } from "../utils/annotations"; +import { isInTupleReturnFunction, isTupleReturnCall, isVarargType } from "../utils/annotations"; import { validateAssignment } from "../utils/assignment-validation"; import { ContextType, getDeclarationContextType } from "../utils/function-context"; import { createImmediatelyInvokedFunctionExpression, createUnpackCall, wrapInTable } from "../utils/lua-ast"; @@ -255,7 +255,7 @@ export const transformSpreadElement: FunctionVisitor = (node, return innerExpression; } - if (ts.isIdentifier(node.expression) && isVarArgType(context, node.expression)) { + if (ts.isIdentifier(node.expression) && isVarargType(context, node.expression)) { return lua.createDotsLiteral(node); } diff --git a/src/transformation/visitors/class/index.ts b/src/transformation/visitors/class/index.ts index 33024923e..0736eec25 100644 --- a/src/transformation/visitors/class/index.ts +++ b/src/transformation/visitors/class/index.ts @@ -266,7 +266,6 @@ export function transformClassDeclaration( lua.createBlock(constructorBody), [createSelfIdentifier()], lua.createDotsLiteral(), - undefined, lua.FunctionExpressionFlags.Declaration ); result.push( diff --git a/src/transformation/visitors/class/members/accessors.ts b/src/transformation/visitors/class/members/accessors.ts index 50cc8a10f..9d13deafa 100644 --- a/src/transformation/visitors/class/members/accessors.ts +++ b/src/transformation/visitors/class/members/accessors.ts @@ -10,13 +10,7 @@ import { getExtendedType, isStaticNode } from "../utils"; function transformAccessor(context: TransformationContext, node: ts.AccessorDeclaration): lua.FunctionExpression { const [params, dot, restParam] = transformParameters(context, node.parameters, createSelfIdentifier()); const body = node.body ? transformFunctionBody(context, node.parameters, node.body, restParam)[0] : []; - return lua.createFunctionExpression( - lua.createBlock(body), - params, - dot, - restParam, - lua.FunctionExpressionFlags.Declaration - ); + return lua.createFunctionExpression(lua.createBlock(body), params, dot, lua.FunctionExpressionFlags.Declaration); } export function transformAccessorDeclarations( diff --git a/src/transformation/visitors/class/members/constructor.ts b/src/transformation/visitors/class/members/constructor.ts index 27be76333..d0c611465 100644 --- a/src/transformation/visitors/class/members/constructor.ts +++ b/src/transformation/visitors/class/members/constructor.ts @@ -87,13 +87,7 @@ export function transformConstructorDeclaration( return lua.createAssignmentStatement( createConstructorName(className), - lua.createFunctionExpression( - block, - params, - dotsLiteral, - restParamName, - lua.FunctionExpressionFlags.Declaration - ), + lua.createFunctionExpression(block, params, dotsLiteral, lua.FunctionExpressionFlags.Declaration), constructorWasGenerated ? classDeclaration : statement ); } diff --git a/src/transformation/visitors/class/members/method.ts b/src/transformation/visitors/class/members/method.ts index fe4d15661..4d9610d9b 100644 --- a/src/transformation/visitors/class/members/method.ts +++ b/src/transformation/visitors/class/members/method.ts @@ -33,7 +33,6 @@ export function transformMethodDeclaration( lua.createBlock(body), paramNames, dots, - restParamName, lua.FunctionExpressionFlags.Declaration, node.body ); diff --git a/src/transformation/visitors/conditional.ts b/src/transformation/visitors/conditional.ts index d09616abc..2fab7494c 100644 --- a/src/transformation/visitors/conditional.ts +++ b/src/transformation/visitors/conditional.ts @@ -34,7 +34,6 @@ function wrapInFunctionCall(expression: lua.Expression): lua.FunctionExpression lua.createBlock([returnStatement]), undefined, undefined, - undefined, lua.FunctionExpressionFlags.Inline ); } diff --git a/src/transformation/visitors/errors.ts b/src/transformation/visitors/errors.ts index ed4cccf9f..6d683c475 100644 --- a/src/transformation/visitors/errors.ts +++ b/src/transformation/visitors/errors.ts @@ -46,7 +46,7 @@ export const transformTryStatement: FunctionVisitor = (statemen if ((tryScope.functionReturned || catchScope.functionReturned) && returnedIdentifier) { // Wrap catch in function if try or catch has return - const catchCall = lua.createCallExpression(lua.createFunctionExpression(catchBlock)); + const catchCall = lua.createCallExpression(lua.createFunctionExpression(catchBlock), []); const catchAssign = lua.createAssignmentStatement( [lua.cloneIdentifier(returnedIdentifier), lua.cloneIdentifier(returnValueIdentifier)], catchCall diff --git a/src/transformation/visitors/function.ts b/src/transformation/visitors/function.ts index 005c63a5b..9cee9cc9e 100644 --- a/src/transformation/visitors/function.ts +++ b/src/transformation/visitors/function.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; -import { isVarArgType } from "../utils/annotations"; +import { isVarargType } from "../utils/annotations"; import { createDefaultExportStringLiteral, hasDefaultExportModifier } from "../utils/export"; import { ContextType, getFunctionContextType } from "../utils/function-context"; import { @@ -48,7 +48,7 @@ function isRestParameterReferenced(context: TransformationContext, identifier: l return false; } // Ignore references to @vararg types in spread elements - return references.some(r => !r.parent || !ts.isSpreadElement(r.parent) || !isVarArgType(context, r)); + return references.some(r => !r.parent || !ts.isSpreadElement(r.parent) || !isVarargType(context, r)); } export function transformFunctionBodyStatements(context: TransformationContext, body: ts.Block): lua.Statement[] { @@ -205,7 +205,6 @@ export function transformFunctionLikeDeclaration( lua.createBlock(transformedBody), paramNames, dotsLiteral, - spreadIdentifier, flags, node ); @@ -253,7 +252,6 @@ export const transformFunctionDeclaration: FunctionVisitor 0) { block.statements.unshift( lua.createAssignmentStatement( - castEach( - statement.initializer.elements.map(e => context.transformExpression(e)), - lua.isAssignmentLeftHandSideExpression + statement.initializer.elements.map(e => + cast(context.transformExpression(e), lua.isAssignmentLeftHandSideExpression) ), identifiers ) diff --git a/src/utils.ts b/src/utils.ts index 822c5df3a..cb0651007 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,12 +2,17 @@ import * as ts from "typescript"; import * as nativeAssert from "assert"; import * as path from "path"; -export const createDiagnosticFactoryWithCode = < - T extends (...args: any) => Partial & Pick ->( - code: number, - create: T -) => { +export function castArray(value: T | T[]): T[]; +export function castArray(value: T | readonly T[]): readonly T[]; +export function castArray(value: T | readonly T[]): readonly T[] { + return Array.isArray(value) ? value : [value]; +} + +export const intersperse = (values: T[], separator: T): T[] => + values.flatMap((value, index) => (index === 0 ? [value] : [separator, value])); + +type DiagnosticFactory = (...args: any) => Partial & Pick; +export const createDiagnosticFactoryWithCode = (code: number, create: T) => { return Object.assign( (...args: Parameters): ts.Diagnostic => ({ file: undefined, @@ -23,11 +28,8 @@ export const createDiagnosticFactoryWithCode = < }; let serialDiagnosticCodeCounter = 100000; -export const createSerialDiagnosticFactory = < - T extends (...args: any) => Partial & Pick ->( - create: T -) => createDiagnosticFactoryWithCode(serialDiagnosticCodeCounter++, create); +export const createSerialDiagnosticFactory = (create: T) => + createDiagnosticFactoryWithCode(serialDiagnosticCodeCounter++, create); export const normalizeSlashes = (filePath: string) => filePath.replace(/\\/g, "/"); export const trimExtension = (filePath: string) => filePath.slice(0, -path.extname(filePath).length); @@ -70,17 +72,6 @@ export function cast( } } -export function castEach( - items: TOriginal[], - cast: (item: TOriginal) => item is TCast -): TCast[] { - if (items.every(cast)) { - return items as TCast[]; - } else { - throw new Error(`Failed to cast all elements to expected type using ${cast.name}.`); - } -} - export function assert(value: any, message?: string | Error): asserts value { nativeAssert(value, message); } diff --git a/test/legacy-utils.ts b/test/legacy-utils.ts index 35918aabe..d35a2288a 100644 --- a/test/legacy-utils.ts +++ b/test/legacy-utils.ts @@ -19,7 +19,7 @@ export function transpileString( return file.lua!.trim(); } -export function transpileStringsAsProject( +function transpileStringsAsProject( input: Record, options: tstl.CompilerOptions = {} ): tstl.TranspileResult { @@ -36,7 +36,7 @@ export function transpileStringsAsProject( return tstl.transpileVirtualProject(input, optionsWithDefaults); } -export function transpileStringResult( +function transpileStringResult( input: string | Record, options: tstl.CompilerOptions = {} ): Required { diff --git a/test/tsconfig.json b/test/tsconfig.json index 5388f9434..de0b21bdd 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,18 +1,10 @@ { + "extends": "../tsconfig.json", "compilerOptions": { - "baseUrl": ".", - "paths": { "*": ["types/*"] }, - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "newLine": "LF", - "target": "es2019", - "lib": ["es2019"], + "rootDir": "..", "types": ["node", "jest"], - "experimentalDecorators": true, - - "noEmit": true, - "module": "commonjs" + "baseUrl": ".", + "paths": { "*": ["types/*"] } }, "include": [".", "../src"], "exclude": [ diff --git a/test/unit/__snapshots__/assignments.spec.ts.snap b/test/unit/__snapshots__/assignments.spec.ts.snap index 6f9f553ff..f28bc871c 100644 --- a/test/unit/__snapshots__/assignments.spec.ts.snap +++ b/test/unit/__snapshots__/assignments.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`var declaration in for loop is disallowed: code 1`] = ` +exports[`var is disallowed for loop: code 1`] = ` "local ____exports = {} function ____exports.__main(self) do @@ -12,9 +12,9 @@ end return ____exports" `; -exports[`var declaration in for loop is disallowed: diagnostics 1`] = `"main.ts(2,14): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; +exports[`var is disallowed for loop: diagnostics 1`] = `"main.ts(2,18): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; -exports[`var declaration in for...in loop is disallowed: code 1`] = ` +exports[`var is disallowed for...in loop: code 1`] = ` "local ____exports = {} function ____exports.__main(self) for foo in pairs({}) do @@ -23,9 +23,9 @@ end return ____exports" `; -exports[`var declaration in for...in loop is disallowed: diagnostics 1`] = `"main.ts(2,14): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; +exports[`var is disallowed for...in loop: diagnostics 1`] = `"main.ts(2,18): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; -exports[`var declaration in for...of loop is disallowed: code 1`] = ` +exports[`var is disallowed for...of loop: code 1`] = ` "local ____exports = {} function ____exports.__main(self) for ____, foo in ipairs({}) do @@ -34,9 +34,9 @@ end return ____exports" `; -exports[`var declaration in for...of loop is disallowed: diagnostics 1`] = `"main.ts(2,14): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; +exports[`var is disallowed for...of loop: diagnostics 1`] = `"main.ts(2,18): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; -exports[`var declaration is disallowed: code 1`] = ` +exports[`var is disallowed var declaration: code 1`] = ` "local ____exports = {} function ____exports.__main(self) local foo = true @@ -44,4 +44,4 @@ end return ____exports" `; -exports[`var declaration is disallowed: diagnostics 1`] = `"main.ts(2,9): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; +exports[`var is disallowed var declaration: diagnostics 1`] = `"main.ts(2,13): error TSTL: \`var\` declarations are not supported. Use \`let\` or \`const\` instead."`; diff --git a/test/unit/annotations/vararg.spec.ts b/test/unit/annotations/vararg.spec.ts index 982109261..1635ef13a 100644 --- a/test/unit/annotations/vararg.spec.ts +++ b/test/unit/annotations/vararg.spec.ts @@ -2,17 +2,17 @@ import * as util from "../../util"; const varargDeclaration = ` /** @vararg */ - type LuaVarArg = A & { __luaVararg?: never }; + type LuaVararg = A & { __luaVararg?: never }; `; test("@vararg", () => { util.testFunction` ${varargDeclaration} - function foo(a: unknown, ...b: LuaVarArg) { + function foo(a: unknown, ...b: LuaVararg) { const c = [...b]; return c.join(""); } - function bar(a: unknown, ...b: LuaVarArg) { + function bar(a: unknown, ...b: LuaVararg) { return foo(a, ...b); } return bar("A", "B", "C", "D"); @@ -25,7 +25,7 @@ test("@vararg", () => { test("@vararg array access", () => { util.testFunction` ${varargDeclaration} - function foo(a: unknown, ...b: LuaVarArg) { + function foo(a: unknown, ...b: LuaVararg) { const c = [...b]; return c.join("") + b[0]; } @@ -36,7 +36,7 @@ test("@vararg array access", () => { test("@vararg global", () => { util.testModule` ${varargDeclaration} - declare const arg: LuaVarArg; + declare const arg: LuaVararg; export const result = [...arg].join(""); ` .setLuaFactory(code => `return (function(...) ${code} end)("A", "B", "C", "D")`) diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index 1a8665464..178d72bab 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -7,46 +7,56 @@ test.each(["const", "let"])("%s declaration not top-level is not global", declar ${declarationKind} foo = true; } // @ts-ignore - return (globalThis as any).foo; - `.expectToEqual(undefined); + return "foo" in globalThis; + `.expectToEqual(false); }); -test.each(["const", "let"])("%s declaration top-level is global", declarationKind => { - util.testModule` - ${declarationKind} foo = true; +test.each(["const", "let"])("top-level %s declaration is global", declarationKind => { + // TODO [typescript@>=3.9]: Remove `@ts-ignore` comments before module imports + util.testBundle` // @ts-ignore - return (globalThis as any).foo; - `.expectToEqual(true); + import './a'; + export const result = foo; + ` + .addExtraFile("a.ts", `${declarationKind} foo = true;`) + .expectToEqual({ result: true }); }); -test("var declaration is disallowed", () => { - util.testFunction` - var foo = true; - `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); -}); +describe("var is disallowed", () => { + test("var declaration", () => { + util.testFunction` + var foo = true; + `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); + }); -test("var declaration in for loop is disallowed", () => { - util.testFunction` - for (var foo = 0;;) {} - `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); -}); + test("for loop", () => { + util.testFunction` + for (var foo = 0;;) {} + `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); + }); -test("var declaration in for...in loop is disallowed", () => { - util.testFunction` - for (var foo in {}) {} - `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); -}); + test("for...in loop", () => { + util.testFunction` + for (var foo in {}) {} + `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); + }); -test("var declaration in for...of loop is disallowed", () => { - util.testFunction` - for (var foo of []) {} - `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); + test("for...of loop", () => { + util.testFunction` + for (var foo of []) {} + `.expectDiagnosticsToMatchSnapshot([unsupportedVarDeclaration.code]); + }); }); -test.each(["let myvar;", "const myvar = null;", "const myvar = undefined;"])("Null assignments (%p)", declaration => { - const result = util.transpileAndExecute(declaration + " return myvar;"); - expect(result).toBe(undefined); -}); +test.each(["let result;", "const result = null;", "const result = undefined;"])( + "Null assignments (%p)", + declaration => { + util.testFunction` + ${declaration} + return result; + `.expectToEqual(undefined); + } +); test.each(["x = y", "x += y"])("Assignment expressions (%p)", expression => { util.testFunction` diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index d664ab061..f1235326e 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -502,3 +502,14 @@ test("missing declaration name", () => { function () {} `.expectDiagnosticsToMatchSnapshot([1003]); }); + +test("top-level function declaration is global", () => { + // TODO [typescript@>=3.9]: Remove `@ts-ignore` comments before module imports + util.testBundle` + // @ts-ignore + import './a'; + export const result = foo(); + ` + .addExtraFile("a.ts", `function foo() { return "foo" }`) + .expectToEqual({ result: "foo" }); +}); diff --git a/test/unit/functions/noImplicitSelfOption.spec.ts b/test/unit/functions/noImplicitSelfOption.spec.ts index 906d80a65..f61e37cb2 100644 --- a/test/unit/functions/noImplicitSelfOption.spec.ts +++ b/test/unit/functions/noImplicitSelfOption.spec.ts @@ -1,6 +1,6 @@ import * as util from "../../util"; -test("enables noSelfInFile behaviour for functions", () => { +test("enables noSelfInFile behavior for functions", () => { util.testFunction` function fooBar() {} const test: (this: void) => void = fooBar; @@ -9,7 +9,7 @@ test("enables noSelfInFile behaviour for functions", () => { .expectToHaveNoDiagnostics(); }); -test("enables noSelfInFile behaviour for methods", () => { +test("enables noSelfInFile behavior for methods", () => { util.testFunction` class FooBar { fooBar() {} @@ -22,19 +22,19 @@ test("enables noSelfInFile behaviour for methods", () => { }); test("generates declaration files with @noSelfInFile", () => { - const builder = util.testModule` + const fooBuilder = util.testModule` export function bar() {} ` .setOptions({ declaration: true, noImplicitSelf: true }) .expectToHaveNoDiagnostics(); - const declarationFile = builder.getLuaResult().transpiledFiles.find(f => f.declaration); - if (!util.expectToBeDefined(declarationFile) || !util.expectToBeDefined(declarationFile.declaration)) return; + const fooDeclaration = fooBuilder.getLuaResult().transpiledFiles.find(f => f.declaration)?.declaration; + util.assert(fooDeclaration !== undefined); util.testModule` import { bar } from "./foo.d"; const test: (this: void) => void = bar; ` - .addExtraFile("foo.d.ts", declarationFile.declaration) + .addExtraFile("foo.d.ts", fooDeclaration) .expectToHaveNoDiagnostics(); }); diff --git a/test/unit/modules/resolution.spec.ts b/test/unit/modules/resolution.spec.ts index 1b0a6a8f9..94199e09f 100644 --- a/test/unit/modules/resolution.spec.ts +++ b/test/unit/modules/resolution.spec.ts @@ -4,10 +4,8 @@ import * as util from "../../util"; const requireRegex = /require\("(.*?)"\)/; const expectToRequire = (expected: string): util.TapCallback => builder => { - const match = requireRegex.exec(builder.getMainLuaCodeChunk()); - if (util.expectToBeDefined(match)) { - expect(match[1]).toBe(expected); - } + const [, requiredPath] = builder.getMainLuaCodeChunk().match(requireRegex) ?? []; + expect(requiredPath).toBe(expected); }; test.each([ diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts index fe17f6da4..bb8c8db78 100644 --- a/test/unit/namespaces.spec.ts +++ b/test/unit/namespaces.spec.ts @@ -54,7 +54,19 @@ test("context in namespace function", () => { `.expectToMatchJsResult(); }); -test("namespace merged with interface", () => { +test("namespace merging", () => { + util.testModule` + export namespace Foo { + export const a = 1; + } + + export namespace Foo { + export const b = 2; + } + `.expectToMatchJsResult(); +}); + +test("namespace merging with interface", () => { util.testModule` interface Foo {} namespace Foo { @@ -65,8 +77,8 @@ test("namespace merged with interface", () => { `.expectToMatchJsResult(); }); -test("namespace merged across files", () => { - const testA = ` +test("namespace merging across files", () => { + const a = ` namespace NS { export namespace Inner { export const foo = "foo"; @@ -74,7 +86,7 @@ test("namespace merged across files", () => { } `; - const testB = ` + const b = ` namespace NS { export namespace Inner { export const bar = "bar"; @@ -82,9 +94,18 @@ test("namespace merged across files", () => { } `; - const { transpiledFiles } = util.transpileStringsAsProject({ "testA.ts": testA, "testB.ts": testB }); - const lua = transpiledFiles.map(f => f.lua).join("\n") + "\nreturn NS.Inner.foo .. NS.Inner.bar"; - expect(util.executeLua(lua)).toBe("foobar"); + // TODO [typescript@>=3.9]: Remove `@ts-ignore` comments before module imports + util.testBundle` + // @ts-ignore + import './a'; + // @ts-ignore + import './b'; + + export const result = NS.Inner; + ` + .addExtraFile("a.ts", a) + .addExtraFile("b.ts", b) + .expectToEqual({ result: { foo: "foo", bar: "bar" } }); }); test("declared namespace function call", () => { diff --git a/test/unit/printer/__snapshots__/semicolons.spec.ts.snap b/test/unit/printer/__snapshots__/semicolons.spec.ts.snap new file mode 100644 index 000000000..af1deb303 --- /dev/null +++ b/test/unit/printer/__snapshots__/semicolons.spec.ts.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`semicolon insertion ("{}") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local result = \\"\\" + local function foo(self) + result = \\"foo\\" + end + do + end + (nil or foo)(nil) + return result +end +return ____exports" +`; + +exports[`semicolon insertion ("const a = 1; const b = a;") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local result = \\"\\" + local function foo(self) + result = \\"foo\\" + end + local a = 1 + local b = a; + (nil or foo)(nil) + return result +end +return ____exports" +`; + +exports[`semicolon insertion ("const a = 1; let b: number; b = a;") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local result = \\"\\" + local function foo(self) + result = \\"foo\\" + end + local a = 1 + local b + b = a; + (nil or foo)(nil) + return result +end +return ____exports" +`; + +exports[`semicolon insertion ("function bar() {} bar();") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local result = \\"\\" + local function foo(self) + result = \\"foo\\" + end + local function bar(self) + end + bar(nil); + (nil or foo)(nil) + return result +end +return ____exports" +`; diff --git a/test/unit/printer/semicolons.spec.ts b/test/unit/printer/semicolons.spec.ts index ae3a79244..addb66e53 100644 --- a/test/unit/printer/semicolons.spec.ts +++ b/test/unit/printer/semicolons.spec.ts @@ -3,13 +3,14 @@ import * as util from "../../util"; test.each(["const a = 1; const b = a;", "const a = 1; let b: number; b = a;", "{}", "function bar() {} bar();"])( "semicolon insertion (%p)", leadingStatement => { - const code = ` + util.testFunction` let result = ""; function foo() { result = "foo"; } ${leadingStatement} - (foo)(); + (undefined || foo)(); return result; - `; - expect(util.transpileAndExecute(code)).toEqual("foo"); + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); } ); diff --git a/test/unit/printer/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts index c12a89b69..58873690c 100644 --- a/test/unit/printer/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -262,11 +262,10 @@ test("Inline sourcemaps", () => { .expectToMatchJsResult() .getMainLuaFileResult(); - const inlineSourceMapMatch = file.lua.match(/--# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)/); - if (util.expectToBeDefined(inlineSourceMapMatch)) { - const inlineSourceMap = Buffer.from(inlineSourceMapMatch[1], "base64").toString(); - expect(file.sourceMap).toBe(inlineSourceMap); - } + const [, inlineSourceMapMatch] = + file.lua.match(/--# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)/) ?? []; + const inlineSourceMap = Buffer.from(inlineSourceMapMatch, "base64").toString(); + expect(file.sourceMap).toBe(inlineSourceMap); }); // Helper functions diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 0a54e3a41..dec923d83 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -14,17 +14,8 @@ const arrayLiteralCases = [ "1, 2, ...[3, 4], ...[5, 6]", ]; -const tupleReturnDefinition = ` -/** @tupleReturn */ -function tuple(...args: any[]) { - return args; -}`; - describe.each(["function call", "array literal"] as const)("in %s", kind => { - // prettier-ignore - const factory = (code: string) => kind === "function call" - ? `((...args: any[]) => args)(${code})` - : `[${code}]`; + const factory = (code: string) => (kind === "function call" ? `((...args: any[]) => args)(${code})` : `[${code}]`); test.each(arrayLiteralCases)("of array literal (%p)", expression => { util.testExpression(factory(expression)).expectToMatchJsResult(); @@ -32,7 +23,11 @@ describe.each(["function call", "array literal"] as const)("in %s", kind => { test.each(arrayLiteralCases)("of tuple return call (%p)", expression => { util.testFunction` - ${tupleReturnDefinition} + /** @tupleReturn */ + function tuple(...args: any[]) { + return args; + } + return ${factory(`...tuple(${expression})`)}; `.expectToMatchJsResult(); }); diff --git a/test/util.ts b/test/util.ts index 6a8909e18..552351de9 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,3 +1,4 @@ +import * as nativeAssert from "assert"; import { lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari"; import * as fs from "fs"; import { stringify } from "javascript-stringify"; @@ -9,40 +10,8 @@ import * as tstl from "../src"; export * from "./legacy-utils"; -export const nodeStub = ts.createNode(ts.SyntaxKind.Unknown, 0, 0); - -export function parseTypeScript( - typescript: string, - target: tstl.LuaTarget = tstl.LuaTarget.Lua53 -): [ts.SourceFile, ts.TypeChecker] { - const program = tstl.createVirtualProgram({ "main.ts": typescript }, { luaTarget: target }); - const sourceFile = program.getSourceFile("main.ts"); - - if (sourceFile === undefined) { - throw new Error("Could not find source file main.ts in program."); - } - - return [sourceFile, program.getTypeChecker()]; -} - -export function findFirstChild(node: ts.Node, predicate: (node: ts.Node) => boolean): ts.Node | undefined { - for (const child of node.getChildren()) { - if (predicate(child)) { - return child; - } - - const childChild = findFirstChild(child, predicate); - if (childChild !== undefined) { - return childChild; - } - } - return undefined; -} - -export function expectToBeDefined(subject: T | null | undefined): subject is T { - expect(subject).toBeDefined(); - expect(subject).not.toBeNull(); - return true; // If this was false the expect would have thrown an error +export function assert(value: any, message?: string | Error): asserts value { + nativeAssert(value, message); } export const formatCode = (...values: unknown[]) => values.map(e => stringify(e)).join(", "); @@ -330,11 +299,11 @@ export abstract class TestBuilder { @memoize protected getMainJsCodeChunk(): string { const { transpiledFiles } = this.getJsResult(); - const mainFile = transpiledFiles.find(x => x.fileName === this.mainFileName); - expect(mainFile).toBeDefined(); + const code = transpiledFiles.find(x => x.fileName === this.mainFileName)?.js; + assert(code !== undefined); const header = this.jsHeader ? `${this.jsHeader.trimRight()}\n` : ""; - return header + mainFile!.js!; + return header + code; } protected abstract getJsCodeWithWrapper(): string; diff --git a/tsconfig.json b/tsconfig.json index d16784b37..bf121bd20 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,21 @@ { "compilerOptions": { - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "rootDir": "./src", - "outDir": "./dist", - "declaration": true, - "sourceMap": true, "target": "es2019", "lib": ["es2019"], + "types": ["node"], "module": "commonjs", - "stripInternal": true + "experimentalDecorators": true, + + "rootDir": "src", + "outDir": "dist", + "declaration": true, + "sourceMap": true, + "newLine": "LF", + "stripInternal": true, + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true }, "include": ["src"], "exclude": ["src/lualib"]