diff --git a/src/transformation/context/context.ts b/src/transformation/context/context.ts index 07d16f143..e192348b3 100644 --- a/src/transformation/context/context.ts +++ b/src/transformation/context/context.ts @@ -2,7 +2,6 @@ import * as ts from "typescript"; import { CompilerOptions, LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { unwrapVisitorResult } from "../utils/lua-ast"; -import { isFileModule } from "../utils/typescript"; import { ExpressionLikeNode, ObjectVisitor, StatementLikeNode, VisitorMap } from "./visitors"; export interface EmitResolver { @@ -23,7 +22,7 @@ export class TransformationContext { public readonly options: CompilerOptions = this.program.getCompilerOptions(); public readonly luaTarget = this.options.luaTarget ?? LuaTarget.LuaJIT; - public readonly isModule = isFileModule(this.sourceFile); + public readonly isModule = ts.isExternalModule(this.sourceFile); public readonly isStrict = (this.options.alwaysStrict ?? this.options.strict) || (this.isModule && this.options.target !== undefined && this.options.target >= ts.ScriptTarget.ES2015); diff --git a/src/transformation/utils/export.ts b/src/transformation/utils/export.ts index ca1b4be69..d509504d2 100644 --- a/src/transformation/utils/export.ts +++ b/src/transformation/utils/export.ts @@ -4,7 +4,7 @@ import { TransformationContext } from "../context"; import { createModuleLocalNameIdentifier } from "../visitors/namespace"; import { createExportsIdentifier } from "./lua-ast"; import { getSymbolInfo } from "./symbols"; -import { findFirstNodeAbove, isFileModule } from "./typescript"; +import { findFirstNodeAbove } from "./typescript"; export function hasDefaultExportModifier(node: ts.Node): boolean { return (node.modifiers ?? []).some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword); @@ -75,17 +75,8 @@ export function getExportedSymbolsFromScope( context: TransformationContext, scope: ts.SourceFile | ts.ModuleDeclaration ): ts.Symbol[] { - if (ts.isSourceFile(scope) && !isFileModule(scope)) { - return []; - } - - let scopeSymbol = context.checker.getSymbolAtLocation(scope); - if (scopeSymbol === undefined) { - // TODO: Necessary? - scopeSymbol = context.checker.getTypeAtLocation(scope).getSymbol(); - } - - if (scopeSymbol === undefined || scopeSymbol.exports === undefined) { + const scopeSymbol = context.checker.getSymbolAtLocation(ts.isSourceFile(scope) ? scope : scope.name); + if (scopeSymbol?.exports === undefined) { return []; } diff --git a/src/transformation/utils/typescript/index.ts b/src/transformation/utils/typescript/index.ts index 3869a4635..a947a1457 100644 --- a/src/transformation/utils/typescript/index.ts +++ b/src/transformation/utils/typescript/index.ts @@ -1,28 +1,11 @@ import * as ts from "typescript"; import { TransformationContext } from "../../context"; -import { isDeclaration } from "./nodes"; export * from "./nodes"; export * from "./types"; // TODO: Move to separate files? -export function isFileModule(sourceFile: ts.SourceFile): boolean { - return sourceFile.statements.some(isStatementExported); -} - -function isStatementExported(statement: ts.Statement): boolean { - if (ts.isExportAssignment(statement) || ts.isExportDeclaration(statement)) { - return true; - } - if (ts.isVariableStatement(statement)) { - return statement.declarationList.declarations.some( - declaration => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0 - ); - } - return isDeclaration(statement) && (ts.getCombinedModifierFlags(statement) & ts.ModifierFlags.Export) !== 0; -} - export function hasExportEquals(sourceFile: ts.SourceFile): boolean { return sourceFile.statements.some(node => ts.isExportAssignment(node) && node.isExportEquals); } diff --git a/src/transformation/utils/typescript/nodes.ts b/src/transformation/utils/typescript/nodes.ts index ed84c97a9..db0f85641 100644 --- a/src/transformation/utils/typescript/nodes.ts +++ b/src/transformation/utils/typescript/nodes.ts @@ -16,22 +16,6 @@ export function isAmbientNode(node: ts.Declaration): boolean { return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Ambient) !== 0; } -export function isDeclaration(node: ts.Node): node is ts.Declaration { - return ( - ts.isEnumDeclaration(node) || - ts.isClassDeclaration(node) || - ts.isExportDeclaration(node) || - ts.isImportDeclaration(node) || - ts.isMethodDeclaration(node) || - ts.isModuleDeclaration(node) || - ts.isFunctionDeclaration(node) || - ts.isVariableDeclaration(node) || - ts.isInterfaceDeclaration(node) || - ts.isTypeAliasDeclaration(node) || - ts.isNamespaceExportDeclaration(node) - ); -} - export function isInDestructingAssignment(node: ts.Node): boolean { return ( node.parent && diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 55ad7a99c..6d64751eb 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -126,18 +126,23 @@ end" `; exports[`Transformation (modulesImportAll) 1`] = ` -"local Test = require(\\"test\\") -local ____ = Test" +"local ____exports = {} +local Test = require(\\"test\\") +local ____ = Test +return ____exports" `; exports[`Transformation (modulesImportNamed) 1`] = ` -"local ____test = require(\\"test\\") +"local ____exports = {} +local ____test = require(\\"test\\") local TestClass = ____test.TestClass -local ____ = TestClass" +local ____ = TestClass +return ____exports" `; exports[`Transformation (modulesImportNamedSpecialChars) 1`] = ` -"local ____kebab_2Dmodule = require(\\"kebab-module\\") +"local ____exports = {} +local ____kebab_2Dmodule = require(\\"kebab-module\\") local TestClass1 = ____kebab_2Dmodule.TestClass1 local ____dollar_24module = require(\\"dollar$module\\") local TestClass2 = ____dollar_24module.TestClass2 @@ -151,17 +156,21 @@ local ____ = TestClass1 local ____ = TestClass2 local ____ = TestClass3 local ____ = TestClass4 -local ____ = TestClass5" +local ____ = TestClass5 +return ____exports" `; exports[`Transformation (modulesImportRenamed) 1`] = ` -"local ____test = require(\\"test\\") +"local ____exports = {} +local ____test = require(\\"test\\") local RenamedClass = ____test.TestClass -local ____ = RenamedClass" +local ____ = RenamedClass +return ____exports" `; exports[`Transformation (modulesImportRenamedSpecialChars) 1`] = ` -"local ____kebab_2Dmodule = require(\\"kebab-module\\") +"local ____exports = {} +local ____kebab_2Dmodule = require(\\"kebab-module\\") local RenamedClass1 = ____kebab_2Dmodule.TestClass local ____dollar_24module = require(\\"dollar$module\\") local RenamedClass2 = ____dollar_24module.TestClass @@ -175,10 +184,15 @@ local ____ = RenamedClass1 local ____ = RenamedClass2 local ____ = RenamedClass3 local ____ = RenamedClass4 -local ____ = RenamedClass5" +local ____ = RenamedClass5 +return ____exports" `; -exports[`Transformation (modulesImportWithoutFromClause) 1`] = `"require(\\"test\\")"`; +exports[`Transformation (modulesImportWithoutFromClause) 1`] = ` +"local ____exports = {} +require(\\"test\\") +return ____exports" +`; exports[`Transformation (modulesNamespaceExport) 1`] = ` "local ____exports = {} @@ -244,6 +258,8 @@ end" `; exports[`Transformation (unusedDefaultWithNamespaceImport) 1`] = ` -"local x = require(\\"module\\") -local ____ = x" +"local ____exports = {} +local x = require(\\"module\\") +local ____ = x +return ____exports" `; diff --git a/test/transpile/__snapshots__/project.spec.ts.snap b/test/transpile/__snapshots__/project.spec.ts.snap index 498b36d47..387a60fc6 100644 --- a/test/transpile/__snapshots__/project.spec.ts.snap +++ b/test/transpile/__snapshots__/project.spec.ts.snap @@ -15,10 +15,12 @@ return ____exports Object { "name": "index.lua", "text": "--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]] +local ____exports = {} local ____otherFile = require(\\"otherFile\\") local getNumber = ____otherFile.getNumber local myNumber = getNumber(nil) SetAPIValue(myNumber * 5) +return ____exports ", }, ] diff --git a/test/unit/hoisting.spec.ts b/test/unit/hoisting.spec.ts index e8ee28947..a54e28c6f 100644 --- a/test/unit/hoisting.spec.ts +++ b/test/unit/hoisting.spec.ts @@ -243,55 +243,45 @@ test.each([ }); test("Import hoisting (named)", () => { - const importCode = ` - const bar = foo; - import {foo} from "myMod";`; - const luaHeader = ` - package.loaded["myMod"] = {foo = "foobar"} - ${util.transpileString(importCode)}`; - const tsHeader = "declare const bar: any;"; - const code = "return bar;"; - expect(util.transpileAndExecute(code, undefined, luaHeader, tsHeader)).toBe("foobar"); + util.testBundle` + export const result = foo; + import { foo } from "./module"; + ` + .addExtraFile("module.ts", "export const foo = true;") + .expectToEqual({ result: true }); }); test("Import hoisting (namespace)", () => { - const importCode = ` - const bar = myMod.foo; - import * as myMod from "myMod";`; - const luaHeader = ` - package.loaded["myMod"] = {foo = "foobar"} - ${util.transpileString(importCode)}`; - const tsHeader = "declare const bar: any;"; - const code = "return bar;"; - expect(util.transpileAndExecute(code, undefined, luaHeader, tsHeader)).toBe("foobar"); + util.testBundle` + export const result = module.foo; + import * as module from "./module"; + ` + .addExtraFile("module.ts", "export const foo = true;") + .expectToEqual({ result: true }); }); test("Import hoisting (side-effect)", () => { - const importCode = ` - const bar = foo; - import "myMod";`; - const luaHeader = ` - package.loaded["myMod"] = {_ = (function() foo = "foobar" end)()} - ${util.transpileString(importCode)}`; - const tsHeader = "declare const bar: any;"; - const code = "return bar;"; - expect(util.transpileAndExecute(code, undefined, luaHeader, tsHeader)).toBe("foobar"); + util.testBundle` + export const result = (globalThis as any).result; + import "./module"; + ` + .addExtraFile("module.ts", "(globalThis as any).result = true; export {};") + .expectToEqual({ result: true }); }); test("Import hoisted before function", () => { - const importCode = ` - let bar: any; - import {foo} from "myMod"; + util.testBundle` + export let result: any; + baz(); function baz() { - bar = foo; - }`; - const luaHeader = ` - package.loaded["myMod"] = {foo = "foobar"} - ${util.transpileString(importCode)}`; - const tsHeader = "declare const bar: any;"; - const code = "return bar;"; - expect(util.transpileAndExecute(code, undefined, luaHeader, tsHeader)).toBe("foobar"); + result = foo; + } + + import { foo } from "./module"; + ` + .addExtraFile("module.ts", "export const foo = true;") + .expectToEqual({ result: true }); }); test("Hoisting Shorthand Property", () => {