Skip to content

chore(build): build a universal ESM and CommonJS package #371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Aug 7, 2025

Conversation

gagik
Copy link
Collaborator

@gagik gagik commented Jul 15, 2025

Adds ESM and CommonJS-based exports to the package.

The motivation is largely to add support for VSCode to use the library.

Possible Alternatives

  • Target cjs and use gen-esm-wrapper - might be simpler in a sense and more consistent with how we deal with it in i.e. mongosh and other packages. But because we were distributing an ESM package until now, the potential breaking change is a bit worrying.

Inspired by #325

Relevant reading:

const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as PackageJson;

// Define the packageInfo.ts content
const packageInfoContent = `// This file was generated by scripts/update-package-info.ts - Do not edit it manually.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no good way to have __dirname / import.meta.dirname as explored in https://evertpot.com/universal-commonjs-esm-typescript-packages/ and I couldn't think of anything better either. so instead we'll generate the file on-demand. This is similar to how it is done in github.com/mongodb-js/mongosh/

@coveralls
Copy link
Collaborator

coveralls commented Jul 15, 2025

Pull Request Test Coverage Report for Build 16804714278

Details

  • 79 of 90 (87.78%) changed or added relevant lines in 5 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage increased (+0.1%) to 81.376%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/lib.ts 0 3 0.0%
src/resources/resource.ts 41 49 83.67%
Files with Coverage Reduction New Missed Lines %
src/resources/common/config.ts 1 58.14%
Totals Coverage Status
Change from base Build 16790900568: 0.1%
Covered Lines: 3538
Relevant Lines: 4306

💛 - Coveralls

package.json Outdated
"scripts": {
"prepare": "npm run build",
"build:clean": "rm -rf dist",
"build:compile": "tsc --project tsconfig.build.json",
"build:update-package-info": "tsx scripts/update-package-info.ts",
"build:esm": "tsc --project tsconfig.esm.json && echo '{\"type\":\"module\"}' > dist/package.json",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source: As you can also see that both our cjs and esm packages get a 1-line package.json file with a type property.

This is required because Node needs to know wether files with a .js extensions should be interpreted as CommonJS or ESM. It can’t figure it out after opening it.

Node will look at the nearest package.json to see if the type property was specified.

@gagik gagik changed the title WIP ESM-CJS targetting chore(build): build a universal ESM and CommonJS package Jul 15, 2025
@@ -55,4 +55,4 @@ jobs:
rm -rf node_modules
npm pkg set scripts.prepare="exit 0"
npm install --omit=dev
- run: npx -y @modelcontextprotocol/inspector --cli --method tools/list -- node dist/index.js --connectionString "mongodb://localhost"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm setting to ESM to keep the changes minimal. Doing cjs default won't change our issues with the oidc-plugin, I think the only way to deal with that is to make some changes on the plugin itself to not depend on the require of an ESM module which is a higher Node version feature.

Generally worth checking if we're going to have any problems with dist/index.js going away.

I'd expect the worst being our dev environment configurations no longer being correct.

Otherwise we can also create a index.js alias to point to esm.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is solved, now that you have dist/index.js pointing to esm.

@gagik gagik marked this pull request as ready for review July 15, 2025 15:41
@gagik gagik requested a review from a team as a code owner July 15, 2025 15:41
@@ -0,0 +1,4 @@
export { Server, type ServerOptions } from "./server.js";
export { Telemetry } from "./telemetry/telemetry.js";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can consider exporting a single class which would make the setup easier but I'll leave that to future followups


// Create a dist/index.js file that imports the ESM index.js file
// To minimize breaking changes from pre-universal package time.
const indexPath = resolve(distDir, "index.js");
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just out of precaution in case some of our deployments end up looking for dist/index.js... can be removed though

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think its fine to have it.

Copy link
Collaborator

@himanshusinghs himanshusinghs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks perfect. Tested it locally with vscode and works as expected 🚀

Totally unrelated but you might wanna cherry-pick the changes in this commit because currently we are shipping some unnecessary files in our final dist.

@@ -55,4 +55,4 @@ jobs:
rm -rf node_modules
npm pkg set scripts.prepare="exit 0"
npm install --omit=dev
- run: npx -y @modelcontextprotocol/inspector --cli --method tools/list -- node dist/index.js --connectionString "mongodb://localhost"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is solved, now that you have dist/index.js pointing to esm.


// Create a dist/index.js file that imports the ESM index.js file
// To minimize breaking changes from pre-universal package time.
const indexPath = resolve(distDir, "index.js");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think its fine to have it.

@Copilot Copilot AI review requested due to automatic review settings August 7, 2025 10:18
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request implements universal ESM and CommonJS package support for the MongoDB MCP server, enabling compatibility with both module systems while maintaining backwards compatibility.

  • Creates separate ESM and CommonJS build configurations with dual TypeScript compilation
  • Adds a new library entry point with explicit exports for better module resolution
  • Updates all references from the old build output path to the new ESM path for consistency

Reviewed Changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tsconfig.esm.json New ESM-specific TypeScript configuration targeting esnext modules
tsconfig.cjs.json New CommonJS-specific TypeScript configuration targeting commonjs modules
tsconfig.build.json Updated base build config to enable declaration files and maps
tests/integration/build.test.ts Integration tests to verify both module formats work correctly
src/lib.ts New library entry point exporting all public APIs
src/common/packageInfo.ts Generated file with hardcoded version to avoid JSON import issues
scripts/updatePackageVersion.ts Script to generate packageInfo.ts with current package version
scripts/createUniversalPackage.ts Script to create package.json files for each module format
package.json Updated with dual exports configuration and new build scripts
CONTRIBUTING.md Updated documentation paths to reference ESM build
.smithery/smithery.yaml Updated command path to ESM build
.smithery/Dockerfile Updated Docker command to use ESM build
.github/workflows/prepare_release.yaml Added version update step to release workflow
.github/workflows/check.yml Updated CI command path to ESM build

@gagik gagik force-pushed the gagik/cjs-target branch 2 times, most recently from dfcc23c to f8bc161 Compare August 7, 2025 10:21
@gagik gagik force-pushed the gagik/cjs-target branch 2 times, most recently from ed8766c to 1ce9aa0 Compare August 7, 2025 10:37
@gagik gagik force-pushed the gagik/cjs-target branch from a693929 to f7942de Compare August 7, 2025 10:39
});
}
}
this.setupEventListeners();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is technically a side effect in a constructor which we generally avoid but won't tackle this here.

@gagik gagik merged commit 91b1079 into main Aug 7, 2025
18 checks passed
@gagik gagik deleted the gagik/cjs-target branch August 7, 2025 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants