From 60fbeed090b6222bb0995de74de5e9d79cd33b47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 Aug 2025 16:23:38 +0000 Subject: [PATCH 1/2] Initial plan From f2ff609bb3accb048ac7173ca3a70c35fb50df35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 Aug 2025 16:36:35 +0000 Subject: [PATCH 2/2] Implement object literal exports transformation for module.exports Co-authored-by: bennycode <469989+bennycode@users.noreply.github.com> --- src/converter/convertFile.test.ts | 12 ++++++ .../replacer/replaceModuleExports.ts | 37 +++++++++++++++++++ .../src/object-literal-aliased.snap.ts | 4 ++ .../src/object-literal-aliased.ts | 7 ++++ .../src/object-literal-export.snap.ts | 4 ++ .../src/object-literal-export.ts | 7 ++++ .../src/object-literal-mixed.snap.ts | 4 ++ .../src/object-literal-mixed.ts | 7 ++++ 8 files changed, 82 insertions(+) create mode 100644 src/test/fixtures/module-exports/src/object-literal-aliased.snap.ts create mode 100644 src/test/fixtures/module-exports/src/object-literal-aliased.ts create mode 100644 src/test/fixtures/module-exports/src/object-literal-export.snap.ts create mode 100644 src/test/fixtures/module-exports/src/object-literal-export.ts create mode 100644 src/test/fixtures/module-exports/src/object-literal-mixed.snap.ts create mode 100644 src/test/fixtures/module-exports/src/object-literal-mixed.ts diff --git a/src/converter/convertFile.test.ts b/src/converter/convertFile.test.ts index 53b0ef0..09c2e24 100644 --- a/src/converter/convertFile.test.ts +++ b/src/converter/convertFile.test.ts @@ -62,6 +62,18 @@ describe('convertFile', () => { await testFileConversion('module-exports', 'multiple-named-exports'); }); + it('converts object literal exports', async () => { + await testFileConversion('module-exports', 'object-literal-export'); + }); + + it('converts object literal exports with aliases', async () => { + await testFileConversion('module-exports', 'object-literal-aliased'); + }); + + it('converts object literal exports with mixed properties', async () => { + await testFileConversion('module-exports', 'object-literal-mixed'); + }); + it('handles functions exported as default from plain JavaScript files', async () => { await testFileConversion('module-exports-function-js', 'build-example-index', 'js'); await testFileConversion('module-exports-function-js', 'build-example-index-markdown', 'js'); diff --git a/src/converter/replacer/replaceModuleExports.ts b/src/converter/replacer/replaceModuleExports.ts index 6aa32e2..61cf8cf 100644 --- a/src/converter/replacer/replaceModuleExports.ts +++ b/src/converter/replacer/replaceModuleExports.ts @@ -36,6 +36,7 @@ export function replaceModuleExports(sourceFile: SourceFile) { const isNamedExport = leftText.startsWith('module.exports.'); const isExportingFunction = right.getKind() === SyntaxKind.FunctionExpression; const isExportingIdentifier = right.getKind() === SyntaxKind.Identifier; + const isExportingObjectLiteral = right.getKind() === SyntaxKind.ObjectLiteralExpression; const {comment} = NodeUtil.extractComment(left); @@ -52,6 +53,42 @@ export function replaceModuleExports(sourceFile: SourceFile) { } else if (isExportingFunction) { // @see https://github.com/dsherret/ts-morph/issues/1586 sourceFile.insertStatements(position, `${comment}export default ${rightText};`); + } else if (isExportingObjectLiteral) { + // Handle object literal expressions like module.exports = { c0, c1 } + foundNamedExport = true; + const objectLiteral = right.asKind(SyntaxKind.ObjectLiteralExpression); + if (objectLiteral) { + const exportNames: string[] = []; + const properties = objectLiteral.getProperties(); + + for (const property of properties) { + if (property.getKind() === SyntaxKind.ShorthandPropertyAssignment) { + // For shorthand properties like { c0, c1 }, export the name directly + const shorthandProperty = property.asKind(SyntaxKind.ShorthandPropertyAssignment); + if (shorthandProperty) { + const name = shorthandProperty.getName(); + exportNames.push(name); + } + } else if (property.getKind() === SyntaxKind.PropertyAssignment) { + // For regular properties like { a: c0, b: c1 }, export with alias + const propAssignment = property.asKind(SyntaxKind.PropertyAssignment); + if (propAssignment) { + const name = propAssignment.getName(); + const initializer = propAssignment.getInitializer(); + if (initializer && initializer.getKind() === SyntaxKind.Identifier) { + const identifier = initializer.getText(); + exportNames.push(`${identifier} as ${name}`); + } + } + } + } + + if (exportNames.length > 0) { + sourceFile.insertExportDeclaration(position, { + namedExports: exportNames, + }); + } + } } expressionStatement.remove(); diff --git a/src/test/fixtures/module-exports/src/object-literal-aliased.snap.ts b/src/test/fixtures/module-exports/src/object-literal-aliased.snap.ts new file mode 100644 index 0000000..97190f9 --- /dev/null +++ b/src/test/fixtures/module-exports/src/object-literal-aliased.snap.ts @@ -0,0 +1,4 @@ +const valueA = 0; +const valueB = 1; + +export { valueA as a, valueB as b }; \ No newline at end of file diff --git a/src/test/fixtures/module-exports/src/object-literal-aliased.ts b/src/test/fixtures/module-exports/src/object-literal-aliased.ts new file mode 100644 index 0000000..d607261 --- /dev/null +++ b/src/test/fixtures/module-exports/src/object-literal-aliased.ts @@ -0,0 +1,7 @@ +const valueA = 0; +const valueB = 1; + +module.exports = { + a: valueA, + b: valueB, +}; \ No newline at end of file diff --git a/src/test/fixtures/module-exports/src/object-literal-export.snap.ts b/src/test/fixtures/module-exports/src/object-literal-export.snap.ts new file mode 100644 index 0000000..583e88b --- /dev/null +++ b/src/test/fixtures/module-exports/src/object-literal-export.snap.ts @@ -0,0 +1,4 @@ +const c0 = 0; +const c1 = 1; + +export { c0, c1 }; \ No newline at end of file diff --git a/src/test/fixtures/module-exports/src/object-literal-export.ts b/src/test/fixtures/module-exports/src/object-literal-export.ts new file mode 100644 index 0000000..446a6c8 --- /dev/null +++ b/src/test/fixtures/module-exports/src/object-literal-export.ts @@ -0,0 +1,7 @@ +const c0 = 0; +const c1 = 1; + +module.exports = { + c0, + c1, +}; \ No newline at end of file diff --git a/src/test/fixtures/module-exports/src/object-literal-mixed.snap.ts b/src/test/fixtures/module-exports/src/object-literal-mixed.snap.ts new file mode 100644 index 0000000..bcc5937 --- /dev/null +++ b/src/test/fixtures/module-exports/src/object-literal-mixed.snap.ts @@ -0,0 +1,4 @@ +const x = 1; +const y = 2; + +export { x, y as z }; \ No newline at end of file diff --git a/src/test/fixtures/module-exports/src/object-literal-mixed.ts b/src/test/fixtures/module-exports/src/object-literal-mixed.ts new file mode 100644 index 0000000..76c5b2e --- /dev/null +++ b/src/test/fixtures/module-exports/src/object-literal-mixed.ts @@ -0,0 +1,7 @@ +const x = 1; +const y = 2; + +module.exports = { + x, + z: y, +}; \ No newline at end of file