From fe3a60d0128a834c49b83b21525bcf2963d191af Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Tue, 24 Nov 2020 13:38:03 +1000 Subject: [PATCH 1/5] Fix export all bug #927 --- src/transformation/visitors/modules/export.ts | 14 ++++++++++++-- test/unit/modules/modules.spec.ts | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/transformation/visitors/modules/export.ts b/src/transformation/visitors/modules/export.ts index 5e12c6c5e..18bbc2694 100644 --- a/src/transformation/visitors/modules/export.ts +++ b/src/transformation/visitors/modules/export.ts @@ -51,10 +51,20 @@ function transformExportAllFrom(context: TransformationContext, node: ts.ExportD const forKey = lua.createIdentifier("____exportKey"); const forValue = lua.createIdentifier("____exportValue"); - const body = lua.createBlock([ - lua.createAssignmentStatement(lua.createTableIndexExpression(createExportsIdentifier(), forKey), forValue), + const ifBody = lua.createBlock([ + lua.createAssignmentStatement( + lua.createTableIndexExpression(createExportsIdentifier(), lua.cloneIdentifier(forKey)), + forValue + ), ]); + const ifStatement = lua.createIfStatement( + lua.createBinaryExpression(forKey, lua.createStringLiteral("default"), lua.SyntaxKind.InequalityOperator), + ifBody + ); + + const body = lua.createBlock([ifStatement]); + const pairsIdentifier = lua.createIdentifier("pairs"); const forIn = lua.createForInStatement( body, diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index 885f24541..d5c9fb2ac 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -275,3 +275,17 @@ test("export default function with future reference", () => { .setReturnExport("result") .expectToMatchJsResult(); }); + +test("export all does not include default", () => { + util.testBundle` + export * from "./module"; + ` + .addExtraFile( + "module.ts", + ` + export default true; + export const foo = "bar"; + ` + ) + .expectToEqual({ foo: "bar" }); +}); From a1d11bd4375e9e58803e0983149968f89fe95e1b Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Tue, 24 Nov 2020 14:26:11 +1000 Subject: [PATCH 2/5] Support NamespaceExport #928 --- src/transformation/visitors/modules/export.ts | 55 ++++++++++++++----- test/unit/modules/modules.spec.ts | 21 ++++--- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/transformation/visitors/modules/export.ts b/src/transformation/visitors/modules/export.ts index 18bbc2694..6a600a886 100644 --- a/src/transformation/visitors/modules/export.ts +++ b/src/transformation/visitors/modules/export.ts @@ -8,6 +8,7 @@ import { createExportedIdentifier, } from "../../utils/export"; import { createExportsIdentifier } from "../../utils/lua-ast"; +import { createSafeName } from "../../utils/safe-names"; import { ScopeType } from "../../utils/scope"; import { transformScopeBlock } from "../block"; import { transformIdentifier } from "../identifier"; @@ -36,7 +37,7 @@ export const transformExportAssignment: FunctionVisitor = ( } }; -function transformExportAllFrom(context: TransformationContext, node: ts.ExportDeclaration): lua.Statement | undefined { +function transformExportAll(context: TransformationContext, node: ts.ExportDeclaration): lua.Statement | undefined { assert(node.moduleSpecifier); if (!context.resolver.moduleExportsSomeValue(node.moduleSpecifier)) { @@ -46,34 +47,62 @@ function transformExportAllFrom(context: TransformationContext, node: ts.ExportD const moduleRequire = createModuleRequire(context, node.moduleSpecifier); const tempModuleIdentifier = lua.createIdentifier("____export"); + const result: lua.Statement[] = []; + const declaration = lua.createVariableDeclarationStatement(tempModuleIdentifier, moduleRequire); + result.push(declaration); + + if (node.exportClause && ts.isNamespaceExport(node.exportClause)) { + const namespaceIdentifier = lua.createIdentifier(createSafeName(node.exportClause.name.text)); + const namespaceDeclaration = lua.createVariableDeclarationStatement( + namespaceIdentifier, + lua.createTableExpression() + ); + result.push(namespaceDeclaration); + } const forKey = lua.createIdentifier("____exportKey"); const forValue = lua.createIdentifier("____exportValue"); - const ifBody = lua.createBlock([ - lua.createAssignmentStatement( - lua.createTableIndexExpression(createExportsIdentifier(), lua.cloneIdentifier(forKey)), - forValue - ), - ]); + const left = + node.exportClause && ts.isNamespaceExport(node.exportClause) + ? lua.createIdentifier(createSafeName(node.exportClause.name.text)) + : createExportsIdentifier(); + + const leftAssignment = lua.createAssignmentStatement( + lua.createTableIndexExpression(lua.cloneIdentifier(left), lua.cloneIdentifier(forKey)), + forValue + ); + + const ifBody = lua.createBlock([leftAssignment]); const ifStatement = lua.createIfStatement( lua.createBinaryExpression(forKey, lua.createStringLiteral("default"), lua.SyntaxKind.InequalityOperator), ifBody ); - const body = lua.createBlock([ifStatement]); - const pairsIdentifier = lua.createIdentifier("pairs"); const forIn = lua.createForInStatement( - body, + lua.createBlock([ifStatement]), [lua.cloneIdentifier(forKey), lua.cloneIdentifier(forValue)], [lua.createCallExpression(pairsIdentifier, [lua.cloneIdentifier(tempModuleIdentifier)])] ); + result.push(forIn); + + if (node.exportClause && ts.isNamespaceExport(node.exportClause)) { + const assignToExports = lua.createAssignmentStatement( + lua.createTableIndexExpression( + createExportsIdentifier(), + lua.createStringLiteral(node.exportClause.name.text) + ), + lua.createIdentifier(createSafeName(node.exportClause.name.text)) + ); + result.push(assignToExports); + } + // Wrap this in a DoStatement to prevent polluting the scope. - return lua.createDoStatement([declaration, forIn], node); + return lua.createDoStatement(result, node); } const isDefaultExportSpecifier = (node: ts.ExportSpecifier) => @@ -137,7 +166,7 @@ export const getExported = (context: TransformationContext, exportSpecifiers: ts export const transformExportDeclaration: FunctionVisitor = (node, context) => { if (!node.exportClause) { // export * from "..."; - return transformExportAllFrom(context, node); + return transformExportAll(context, node); } if (!context.resolver.isValueAliasDeclaration(node)) { @@ -146,7 +175,7 @@ export const transformExportDeclaration: FunctionVisitor = if (ts.isNamespaceExport(node.exportClause)) { // export * as ns from "..."; - throw new Error("NamespaceExport is not supported"); + return transformExportAll(context, node); } const exportSpecifiers = getExported(context, node.exportClause); diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index d5c9fb2ac..340810a2a 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -276,16 +276,23 @@ test("export default function with future reference", () => { .expectToMatchJsResult(); }); +const moduleFile = ` +export default true; +export const foo = "bar"; +`; + test("export all does not include default", () => { util.testBundle` export * from "./module"; ` - .addExtraFile( - "module.ts", - ` - export default true; - export const foo = "bar"; - ` - ) + .addExtraFile("module.ts", moduleFile) .expectToEqual({ foo: "bar" }); }); + +test("namespace export does not include default", () => { + util.testBundle` + export * as result from "./module"; + ` + .addExtraFile("module.ts", moduleFile) + .expectToEqual({ result: { foo: "bar" } }); +}); From 9dae80f0702936235c8098912e35914dc966c5ea Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Tue, 24 Nov 2020 14:30:53 +1000 Subject: [PATCH 3/5] Update snapshots --- test/translation/__snapshots__/transformation.spec.ts.snap | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 942f17aa2..a299f2640 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -64,7 +64,9 @@ ____exports.uwv = xyz do local ____export = require(\\"xyz\\") for ____exportKey, ____exportValue in pairs(____export) do - ____exports[____exportKey] = ____exportValue + if ____exportKey ~= \\"default\\" then + ____exports[____exportKey] = ____exportValue + end end end do From 7f72826c1c1d6ae625ba583dd9969d6899ae3a6c Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Tue, 24 Nov 2020 14:32:26 +1000 Subject: [PATCH 4/5] Add unsafe Lua name NamespaceExport test --- test/unit/modules/modules.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index 340810a2a..0951acc9f 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -296,3 +296,11 @@ test("namespace export does not include default", () => { .addExtraFile("module.ts", moduleFile) .expectToEqual({ result: { foo: "bar" } }); }); + +test("namespace export with unsafe Lua name", () => { + util.testBundle` + export * as $$$ from "./module"; + ` + .addExtraFile("module.ts", moduleFile) + .expectToEqual({ $$$: { foo: "bar" } }); +}); From befe9dc6f177df4b0785fa75897d8f1ac3ccf0d4 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 14 Dec 2020 08:08:56 +1000 Subject: [PATCH 5/5] Add comments to transformExportAll and add export default if NamespaceExport --- src/transformation/visitors/modules/export.ts | 61 +++++++++---------- test/unit/modules/modules.spec.ts | 4 +- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/transformation/visitors/modules/export.ts b/src/transformation/visitors/modules/export.ts index 6a600a886..0fa5d9ee4 100644 --- a/src/transformation/visitors/modules/export.ts +++ b/src/transformation/visitors/modules/export.ts @@ -8,7 +8,6 @@ import { createExportedIdentifier, } from "../../utils/export"; import { createExportsIdentifier } from "../../utils/lua-ast"; -import { createSafeName } from "../../utils/safe-names"; import { ScopeType } from "../../utils/scope"; import { transformScopeBlock } from "../block"; import { transformIdentifier } from "../identifier"; @@ -45,42 +44,53 @@ function transformExportAll(context: TransformationContext, node: ts.ExportDecla } const moduleRequire = createModuleRequire(context, node.moduleSpecifier); - const tempModuleIdentifier = lua.createIdentifier("____export"); + // export * as ns from "..."; + // exports.ns = require(...) + if (node.exportClause && ts.isNamespaceExport(node.exportClause)) { + const assignToExports = lua.createAssignmentStatement( + lua.createTableIndexExpression( + createExportsIdentifier(), + lua.createStringLiteral(node.exportClause.name.text) + ), + moduleRequire + ); + return assignToExports; + } + + // export * from "..."; + // exports all values EXCEPT "default" from "..." const result: lua.Statement[] = []; + // local ____export = require(...) + const tempModuleIdentifier = lua.createIdentifier("____export"); const declaration = lua.createVariableDeclarationStatement(tempModuleIdentifier, moduleRequire); result.push(declaration); - if (node.exportClause && ts.isNamespaceExport(node.exportClause)) { - const namespaceIdentifier = lua.createIdentifier(createSafeName(node.exportClause.name.text)); - const namespaceDeclaration = lua.createVariableDeclarationStatement( - namespaceIdentifier, - lua.createTableExpression() - ); - result.push(namespaceDeclaration); - } - + // ____exports[____exportKey] = ____exportValue const forKey = lua.createIdentifier("____exportKey"); const forValue = lua.createIdentifier("____exportValue"); - - const left = - node.exportClause && ts.isNamespaceExport(node.exportClause) - ? lua.createIdentifier(createSafeName(node.exportClause.name.text)) - : createExportsIdentifier(); - const leftAssignment = lua.createAssignmentStatement( - lua.createTableIndexExpression(lua.cloneIdentifier(left), lua.cloneIdentifier(forKey)), + lua.createTableIndexExpression(createExportsIdentifier(), forKey), forValue ); + // if key ~= "default" then + // -- export the value, do not export "default" values + // end const ifBody = lua.createBlock([leftAssignment]); - const ifStatement = lua.createIfStatement( - lua.createBinaryExpression(forKey, lua.createStringLiteral("default"), lua.SyntaxKind.InequalityOperator), + lua.createBinaryExpression( + lua.cloneIdentifier(forKey), + lua.createStringLiteral("default"), + lua.SyntaxKind.InequalityOperator + ), ifBody ); + // for ____exportKey, ____exportValue in ____export do + // -- export ____exportValue, unless ____exportKey is "default" + // end const pairsIdentifier = lua.createIdentifier("pairs"); const forIn = lua.createForInStatement( lua.createBlock([ifStatement]), @@ -90,17 +100,6 @@ function transformExportAll(context: TransformationContext, node: ts.ExportDecla result.push(forIn); - if (node.exportClause && ts.isNamespaceExport(node.exportClause)) { - const assignToExports = lua.createAssignmentStatement( - lua.createTableIndexExpression( - createExportsIdentifier(), - lua.createStringLiteral(node.exportClause.name.text) - ), - lua.createIdentifier(createSafeName(node.exportClause.name.text)) - ); - result.push(assignToExports); - } - // Wrap this in a DoStatement to prevent polluting the scope. return lua.createDoStatement(result, node); } diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index 0951acc9f..b6aefdc0c 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -294,7 +294,7 @@ test("namespace export does not include default", () => { export * as result from "./module"; ` .addExtraFile("module.ts", moduleFile) - .expectToEqual({ result: { foo: "bar" } }); + .expectToEqual({ result: { default: true, foo: "bar" } }); }); test("namespace export with unsafe Lua name", () => { @@ -302,5 +302,5 @@ test("namespace export with unsafe Lua name", () => { export * as $$$ from "./module"; ` .addExtraFile("module.ts", moduleFile) - .expectToEqual({ $$$: { foo: "bar" } }); + .expectToEqual({ $$$: { default: true, foo: "bar" } }); });