diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index d5023bf3f..5c9700097 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -29,6 +29,7 @@ export interface TypeScriptToLuaOptions { luaTarget?: LuaTarget; luaLibImport?: LuaLibImportKind; luaPlugins?: LuaPluginImport[]; + noImplicitGlobalVariables?: boolean; noImplicitSelf?: boolean; noHeader?: boolean; noResolvePaths?: string[]; diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 4d113610f..fbb104459 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -58,6 +58,12 @@ export const optionDeclarations: CommandLineOption[] = [ type: "enum", choices: Object.values(LuaTarget), }, + { + name: "noImplicitGlobalVariables", + description: + 'Specify to prevent implicitly turning "normal" variants into global variables in the transpiled output.', + type: "boolean", + }, { name: "noImplicitSelf", description: 'If "this" is implicitly considered an any type, do not generate a self parameter.', diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index b894dba63..e2a3d2a9e 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -137,6 +137,8 @@ export function createLocalOrExportedOrGlobalDeclaration( let declaration: lua.VariableDeclarationStatement | undefined; let assignment: lua.AssignmentStatement | undefined; + const noImplicitGlobalVariables = context.options.noImplicitGlobalVariables === true; + const isFunctionDeclaration = tsOriginal !== undefined && ts.isFunctionDeclaration(tsOriginal); const identifiers = castArray(lhs); @@ -160,7 +162,7 @@ export function createLocalOrExportedOrGlobalDeclaration( const scope = peekScope(context); const isTopLevelVariable = scope.type === ScopeType.File; - if (context.isModule || !isTopLevelVariable) { + if (context.isModule || !isTopLevelVariable || noImplicitGlobalVariables) { const isLuaFunctionExpression = rhs && !Array.isArray(rhs) && lua.isFunctionExpression(rhs); const isSafeRecursiveFunctionDeclaration = isFunctionDeclaration && isLuaFunctionExpression; if (!isSafeRecursiveFunctionDeclaration && hasMultipleReferences(scope, lhs)) { diff --git a/test/unit/functions/noImplicitGlobalVariables.spec.ts b/test/unit/functions/noImplicitGlobalVariables.spec.ts new file mode 100644 index 000000000..00bd6195a --- /dev/null +++ b/test/unit/functions/noImplicitGlobalVariables.spec.ts @@ -0,0 +1,30 @@ +import * as util from "../../util"; + +test("normal TSTL creates global variables", () => { + const builder = util.testModule` + function foo() {} + const bar = 123; + `.expectToHaveNoDiagnostics(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).not.toContain("local"); +}); + +test("noImplicitGlobalVariables does not create any global variables", () => { + const builder = util.testModule` + function foo() {} + const bar = 123; + ` + .setOptions({ noImplicitGlobalVariables: true }) + .expectToHaveNoDiagnostics(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("local function foo("); + expect(lua).toContain("local bar ="); +});