Skip to content

Commit 95da8fe

Browse files
tomblindPerryvw
authored andcommitted
Non-module namespace merge (#646)
* Fixed non-module namespace merging * cleanup and comments
1 parent 3fa2065 commit 95da8fe

File tree

4 files changed

+68
-19
lines changed

4 files changed

+68
-19
lines changed

src/LuaTransformer.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,13 @@ export class LuaTransformer {
15851585

15861586
const symbol = this.checker.getSymbolAtLocation(statement.name);
15871587
const hasExports = symbol !== undefined && this.checker.getExportsOfModule(symbol).length > 0;
1588+
const nameIdentifier = this.transformIdentifier(statement.name as ts.Identifier);
1589+
const exportScope = this.getIdentifierExportScope(nameIdentifier);
1590+
1591+
// Non-module namespace could be merged if:
1592+
// - is top level
1593+
// - is nested and exported
1594+
const isNonModuleMergeable = !this.isModule && (!this.currentNamespace || exportScope);
15881595

15891596
// This is NOT the first declaration if:
15901597
// - declared as a module before this (ignore interfaces with same name)
@@ -1594,27 +1601,41 @@ export class LuaTransformer {
15941601
(symbol.declarations.findIndex(d => ts.isClassLike(d) || ts.isFunctionDeclaration(d)) === -1 &&
15951602
statement === symbol.declarations.find(ts.isModuleDeclaration));
15961603

1597-
const nameIdentifier = this.transformIdentifier(statement.name as ts.Identifier);
1604+
if (isNonModuleMergeable) {
1605+
// 'local NS = NS or {}' or 'exportTable.NS = exportTable.NS or {}'
1606+
const localDeclaration = this.createLocalOrExportedOrGlobalDeclaration(
1607+
nameIdentifier,
1608+
tstl.createBinaryExpression(
1609+
this.addExportToIdentifier(nameIdentifier),
1610+
tstl.createTableExpression(),
1611+
tstl.SyntaxKind.OrOperator
1612+
)
1613+
);
15981614

1599-
if (isFirstDeclaration) {
1615+
result.push(...localDeclaration);
1616+
} else if (isFirstDeclaration) {
16001617
// local NS = {} or exportTable.NS = {}
16011618
const localDeclaration = this.createLocalOrExportedOrGlobalDeclaration(
16021619
nameIdentifier,
16031620
tstl.createTableExpression()
16041621
);
16051622

16061623
result.push(...localDeclaration);
1624+
}
16071625

1608-
const exportScope = this.getIdentifierExportScope(nameIdentifier);
1609-
if (exportScope && hasExports && tsHelper.moduleHasEmittedBody(statement)) {
1610-
// local NS = exportTable.NS
1611-
const localDeclaration = this.createHoistableVariableDeclarationStatement(
1612-
this.createModuleLocalNameIdentifier(statement),
1613-
this.createExportedIdentifier(nameIdentifier, exportScope)
1614-
);
1626+
if (
1627+
(isNonModuleMergeable || isFirstDeclaration) &&
1628+
exportScope &&
1629+
hasExports &&
1630+
tsHelper.moduleHasEmittedBody(statement)
1631+
) {
1632+
// local NS = exportTable.NS
1633+
const localDeclaration = this.createHoistableVariableDeclarationStatement(
1634+
this.createModuleLocalNameIdentifier(statement),
1635+
this.createExportedIdentifier(nameIdentifier, exportScope)
1636+
);
16151637

1616-
result.push(localDeclaration);
1617-
}
1638+
result.push(localDeclaration);
16181639
}
16191640

16201641
// Set current namespace for nested NS

test/translation/__snapshots__/transformation.spec.ts.snap

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ end
455455
return ____exports"
456456
`;
457457

458-
exports[`Transformation (modulesNamespaceNoExport) 1`] = `"TestSpace = {}"`;
458+
exports[`Transformation (modulesNamespaceNoExport) 1`] = `"TestSpace = TestSpace or {}"`;
459459

460460
exports[`Transformation (modulesNamespaceWithMemberExport) 1`] = `
461461
"local ____exports = {}
@@ -487,7 +487,7 @@ return ____exports"
487487
exports[`Transformation (modulesVariableNoExport) 1`] = `"local foo = \\"bar\\""`;
488488

489489
exports[`Transformation (namespace) 1`] = `
490-
"myNamespace = {}
490+
"myNamespace = myNamespace or {}
491491
do
492492
local function nsMember(self)
493493
end
@@ -521,6 +521,7 @@ function MergedClass.prototype.methodB(self)
521521
self:methodA()
522522
self:propertyFunc()
523523
end
524+
MergedClass = MergedClass or {}
524525
do
525526
function MergedClass.namespaceFunc(self)
526527
end
@@ -533,7 +534,7 @@ MergedClass:namespaceFunc()"
533534
`;
534535

535536
exports[`Transformation (namespaceNested) 1`] = `
536-
"myNamespace = {}
537+
"myNamespace = myNamespace or {}
537538
do
538539
local myNestedNamespace = {}
539540
do

test/unit/modules.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,23 @@ test("Module merged with interface", () => {
160160
const code = `return Foo.bar();`;
161161
expect(util.transpileAndExecute(code, undefined, undefined, header)).toBe("foobar");
162162
});
163+
164+
test("module merged across files", () => {
165+
const testA = `
166+
namespace NS {
167+
export namespace Inner {
168+
export const foo = "foo";
169+
}
170+
}
171+
`;
172+
const testB = `
173+
namespace NS {
174+
export namespace Inner {
175+
export const bar = "bar";
176+
}
177+
}
178+
`;
179+
const { transpiledFiles } = util.transpileStringsAsProject({ "testA.ts": testA, "testB.ts": testB });
180+
const lua = transpiledFiles.map(f => f.lua).join("\n") + "\nreturn NS.Inner.foo .. NS.Inner.bar";
181+
expect(util.executeLua(lua)).toBe("foobar");
182+
});

test/util.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ export function transpileString(
2020
return file.lua.trim();
2121
}
2222

23-
export function transpileStringResult(
24-
input: string | Record<string, string>,
23+
export function transpileStringsAsProject(
24+
input: Record<string, string>,
2525
options: tstl.CompilerOptions = {}
26-
): Required<tstl.TranspileStringResult> {
26+
): tstl.TranspileResult {
2727
const optionsWithDefaults = {
2828
luaTarget: tstl.LuaTarget.Lua53,
2929
noHeader: true,
@@ -34,9 +34,16 @@ export function transpileStringResult(
3434
...options,
3535
};
3636

37-
const { diagnostics, transpiledFiles } = tstl.transpileVirtualProject(
37+
return tstl.transpileVirtualProject(input, optionsWithDefaults);
38+
}
39+
40+
export function transpileStringResult(
41+
input: string | Record<string, string>,
42+
options: tstl.CompilerOptions = {}
43+
): Required<tstl.TranspileStringResult> {
44+
const { diagnostics, transpiledFiles } = transpileStringsAsProject(
3845
typeof input === "string" ? { "main.ts": input } : input,
39-
optionsWithDefaults
46+
options
4047
);
4148

4249
const file = transpiledFiles.find(({ fileName }) => /\bmain\.[a-z]+$/.test(fileName));

0 commit comments

Comments
 (0)