From 926f623ac3318ec2dd803de8a6b195717028f79c Mon Sep 17 00:00:00 2001 From: ark120202 Date: Tue, 28 May 2019 07:19:31 +0500 Subject: [PATCH] Improve `import =` require behavior --- src/LuaTransformer.ts | 34 ++++++++++++++++++++++++++-------- test/unit/declarations.spec.ts | 15 ++++----------- test/unit/modules.spec.ts | 14 +++++++++++++- test/unit/require.spec.ts | 19 +++++++++++++++---- 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/LuaTransformer.ts b/src/LuaTransformer.ts index cd88c9553..bb269d570 100644 --- a/src/LuaTransformer.ts +++ b/src/LuaTransformer.ts @@ -42,6 +42,7 @@ interface Scope { export interface EmitResolver { isValueAliasDeclaration(node: ts.Node): boolean; isReferencedAliasDeclaration(node: ts.Node, checkChildren?: boolean): boolean; + isTopLevelValueImportEqualsWithEntityName(node: ts.ImportEqualsDeclaration): boolean; moduleExportsSomeValue(moduleReferenceExpression: ts.Expression): boolean; } @@ -462,9 +463,29 @@ export class LuaTransformer { public transformImportEqualsDeclaration(declaration: ts.ImportEqualsDeclaration): StatementVisitResult { const name = this.transformIdentifier(declaration.name); - const expression = ts.isExternalModuleReference(declaration.moduleReference) - ? this.transformExternalModuleReference(declaration.moduleReference) - : this.transformEntityName(declaration.moduleReference); + let expression: tstl.Expression; + if (ts.isExternalModuleReference(declaration.moduleReference)) { + if (!this.resolver.isReferencedAliasDeclaration(declaration)) { + return undefined; + } + + expression = this.transformExternalModuleReference(declaration.moduleReference); + } else { + if (this.currentSourceFile === undefined) { + throw TSTLErrors.MissingSourceFile(); + } + + const shouldEmit = + this.resolver.isReferencedAliasDeclaration(declaration) || + (!ts.isExternalModule(this.currentSourceFile) && + this.resolver.isTopLevelValueImportEqualsWithEntityName(declaration)); + + if (!shouldEmit) { + return undefined; + } + + expression = this.transformEntityName(declaration.moduleReference); + } return this.createHoistableVariableDeclarationStatement(name, expression, declaration); } @@ -472,11 +493,8 @@ export class LuaTransformer { public transformExternalModuleReference( externalModuleReference: ts.ExternalModuleReference ): ExpressionVisitResult { - return tstl.createCallExpression( - tstl.createIdentifier("require"), - [this.transformExpression(externalModuleReference.expression)], - externalModuleReference - ); + // TODO: Should `externalModuleReference` be original node? + return this.createModuleRequire(externalModuleReference.expression as ts.StringLiteral); } private transformEntityName(entityName: ts.EntityName): ExpressionVisitResult { diff --git a/test/unit/declarations.spec.ts b/test/unit/declarations.spec.ts index d0245dae0..415153e2c 100644 --- a/test/unit/declarations.spec.ts +++ b/test/unit/declarations.spec.ts @@ -100,7 +100,7 @@ test("ImportEquals declaration", () => { export function func() { return "foo" } } }; - + import importedFunc = outerNamespace.innerNamespace.func; `; @@ -117,12 +117,12 @@ test("ImportEquals declaration ambient", () => { function func(): string; } }; - + import importedFunc = outerNamespace.innerNamespace.func; `; - const luaHeader = `outerNamespace = { - innerNamespace = { + const luaHeader = `outerNamespace = { + innerNamespace = { func = function() return "foo" end } } @@ -133,10 +133,3 @@ test("ImportEquals declaration ambient", () => { const result = util.transpileAndExecute(execution, undefined, luaHeader, header); expect(result).toEqual("foo"); }); - -test("ImportEquals declaration require", () => { - const source = `import foo = require("bar");`; - - const result = util.transpileString(source); - expect(result.includes(`local foo = require("bar")`)).toBeTruthy(); -}); diff --git a/test/unit/modules.spec.ts b/test/unit/modules.spec.ts index c47409707..2c60c7e7a 100644 --- a/test/unit/modules.spec.ts +++ b/test/unit/modules.spec.ts @@ -1,3 +1,4 @@ +import * as ts from "typescript"; import * as tstl from "../../src"; import { TSTLErrors } from "../../src/TSTLErrors"; import * as util from "../util"; @@ -11,7 +12,11 @@ describe("module import/export elision", () => { `; const expectToElideImport = (code: string) => { - const lua = util.transpileString({ "module.d.ts": moduleDeclaration, "main.ts": code }, undefined, false); + const lua = util.transpileString( + { "module.d.ts": moduleDeclaration, "main.ts": code }, + { module: ts.ModuleKind.CommonJS }, + false + ); expect(() => util.executeLua(lua)).not.toThrow(); }; @@ -37,6 +42,13 @@ describe("module import/export elision", () => { `); }); + test("should elide `import =` declarations", () => { + expectToElideImport(` + import module = require("module"); + const foo: module.Type = "bar"; + `); + }); + test("should elide type exports", () => { const code = ` declare const _G: any; diff --git a/test/unit/require.spec.ts b/test/unit/require.spec.ts index bb9903a1b..b6db8f877 100644 --- a/test/unit/require.spec.ts +++ b/test/unit/require.spec.ts @@ -1,5 +1,8 @@ +import * as ts from "typescript"; import * as util from "../util"; +const requireRegex = /require\("(.*?)"\)/; + test.each([ { filePath: "main.ts", @@ -79,8 +82,7 @@ test.each([ expect(() => util.transpileString(input, options)).toThrow(); } else { const lua = util.transpileString(input, options); - const regex = /require\("(.*?)"\)/; - const match = regex.exec(lua); + const match = requireRegex.exec(lua); if (util.expectToBeDefined(match)) { expect(match[1]).toBe(expectedPath); @@ -96,11 +98,20 @@ test.each([{ comment: "", expectedPath: "src.fake" }, { comment: "/** @noResolut "src/main.ts": `import * as fake from "fake"; fake;`, "module.d.ts": `${comment} declare module "fake" {}`, }); - const regex = /require\("(.*?)"\)/; - const match = regex.exec(lua); + const match = requireRegex.exec(lua); if (util.expectToBeDefined(match)) { expect(match[1]).toBe(expectedPath); } } ); + +test("ImportEquals declaration require", () => { + const input = `import foo = require("./foo/bar"); foo;`; + + const lua = util.transpileString(input, { module: ts.ModuleKind.CommonJS }); + const match = requireRegex.exec(lua); + if (util.expectToBeDefined(match)) { + expect(match[1]).toBe("foo.bar"); + } +});