-
Notifications
You must be signed in to change notification settings - Fork 75
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
base: main
Are you sure you want to change the base?
Changes from all commits
6eed483
463b6cc
3b03327
2d63956
6e24d64
3f10cbd
52ccf24
cbebf3e
8fc12bc
3bf00e6
c138c1e
2e76d9a
49dccbe
a262914
5a12fec
ad51a41
7064ad0
70cd5c9
00a1b19
515b7c5
6f5540e
c61e643
9e4d91b
bf35972
c70853e
384a3be
6a70bbe
820292c
cce6ddb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,26 +2,45 @@ | |
"name": "mongodb-mcp-server", | ||
"description": "MongoDB Model Context Protocol Server", | ||
"version": "0.1.3", | ||
"main": "dist/index.js", | ||
"type": "module", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/esm/lib.d.ts", | ||
"default": "./dist/esm/lib.js" | ||
}, | ||
"require": { | ||
"types": "./dist/cjs/lib.d.ts", | ||
"default": "./dist/cjs/lib.js" | ||
} | ||
} | ||
}, | ||
"main": "./dist/cjs/lib.js", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should add the |
||
"types": "./dist/cjs/lib.d.ts", | ||
"author": "MongoDB <[email protected]>", | ||
"homepage": "https://github.com/mongodb-js/mongodb-mcp-server", | ||
"repository": { | ||
"url": "https://github.com/mongodb-js/mongodb-mcp-server.git" | ||
}, | ||
"bin": { | ||
"mongodb-mcp-server": "dist/index.js" | ||
"mongodb-mcp-server": "dist/esm/index.js" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"type": "module", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"prepare": "npm run build", | ||
"build:clean": "rm -rf dist", | ||
"build:compile": "tsc --project tsconfig.build.json", | ||
"build:chmod": "chmod +x dist/index.js", | ||
"build": "npm run build:clean && npm run build:compile && npm run build:chmod", | ||
"inspect": "npm run build && mcp-inspector -- dist/index.js", | ||
"build:update-package-version": "tsx scripts/updatePackageVersion.ts", | ||
"build:esm": "tsc --project tsconfig.esm.json", | ||
"build:cjs": "tsc --project tsconfig.cjs.json", | ||
"build:universal-package": "tsx scripts/createUniversalPackage.ts", | ||
"build:chmod": "chmod +x dist/esm/index.js", | ||
"build": "npm run build:clean && npm run build:esm && npm run build:cjs && npm run build:universal-package && npm run build:chmod", | ||
"inspect": "npm run build && mcp-inspector -- dist/esm/index.js", | ||
"prettier": "prettier", | ||
"check": "npm run build && npm run check:types && npm run check:lint && npm run check:format", | ||
"check:lint": "eslint .", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#!/usr/bin/env tsx | ||
|
||
import { writeFileSync, mkdirSync } from "fs"; | ||
import { resolve } from "path"; | ||
|
||
const distDir = resolve("dist"); | ||
|
||
/** | ||
* Node uses the package.json to know whether files with a .js extensions | ||
* should be interpreted as CommonJS or ESM. | ||
*/ | ||
// ESM package.json | ||
const esmPath = resolve(distDir, "esm", "package.json"); | ||
mkdirSync(resolve(distDir, "esm"), { recursive: true }); | ||
writeFileSync(esmPath, JSON.stringify({ type: "module" })); | ||
|
||
// CJS package.json | ||
const cjsPath = resolve(distDir, "cjs", "package.json"); | ||
mkdirSync(resolve(distDir, "cjs"), { recursive: true }); | ||
writeFileSync(cjsPath, JSON.stringify({ type: "commonjs" })); | ||
|
||
// 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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I personally think its fine to have it. |
||
writeFileSync(indexPath, `import "./esm/index.js";`); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/usr/bin/env node | ||
|
||
import { readFileSync, writeFileSync } from "fs"; | ||
import { join } from "path"; | ||
|
||
// Read package.json | ||
const packageJsonPath = join(import.meta.dirname, "..", "package.json"); | ||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as { | ||
version: string; | ||
}; | ||
|
||
// Define the packageInfo.ts content | ||
const packageInfoContent = `// This file was generated by scripts/updatePackageVersion.ts - Do not edit it manually. | ||
export const packageInfo = { | ||
version: "${packageJson.version}", | ||
mcpServerName: "MongoDB MCP Server", | ||
}; | ||
`; | ||
|
||
// Write to packageInfo.ts | ||
const packageInfoPath = join(import.meta.dirname, "..", "src", "common", "packageInfo.ts"); | ||
writeFileSync(packageInfoPath, packageInfoContent); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
import packageJson from "../../package.json" with { type: "json" }; | ||
|
||
// This file was generated by scripts/updatePackageVersion.ts - Do not edit it manually. | ||
export const packageInfo = { | ||
version: packageJson.version, | ||
version: "0.1.3", | ||
mcpServerName: "MongoDB MCP Server", | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { Server, type ServerOptions } from "./server.js"; | ||
export { Telemetry } from "./telemetry/telemetry.js"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
export { Session, type SessionOptions } from "./common/session.js"; | ||
export type { UserConfig, ConnectOptions } from "./common/config.js"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { createRequire } from "module"; | ||
import path from "path"; | ||
import { describe, it, expect } from "vitest"; | ||
|
||
// Current directory where the test file is located | ||
const currentDir = import.meta.dirname; | ||
|
||
// Get project root (go up from tests/integration to project root) | ||
const projectRoot = path.resolve(currentDir, "../.."); | ||
|
||
const esmPath = path.resolve(projectRoot, "dist/esm/lib.js"); | ||
const cjsPath = path.resolve(projectRoot, "dist/cjs/lib.js"); | ||
|
||
describe("Build Test", () => { | ||
it("should successfully require CommonJS module", () => { | ||
const require = createRequire(__filename); | ||
|
||
const cjsModule = require(cjsPath) as Record<string, unknown>; | ||
|
||
expect(cjsModule).toBeDefined(); | ||
expect(typeof cjsModule).toBe("object"); | ||
}); | ||
|
||
it("should successfully import ESM module", async () => { | ||
const esmModule = (await import(esmPath)) as Record<string, unknown>; | ||
|
||
expect(esmModule).toBeDefined(); | ||
expect(typeof esmModule).toBe("object"); | ||
}); | ||
|
||
it("should have matching exports between CommonJS and ESM modules", async () => { | ||
// Import CommonJS module | ||
const require = createRequire(__filename); | ||
const cjsModule = require(cjsPath) as Record<string, unknown>; | ||
|
||
// Import ESM module | ||
const esmModule = (await import(esmPath)) as Record<string, unknown>; | ||
|
||
// Compare exports | ||
const cjsKeys = Object.keys(cjsModule).sort(); | ||
const esmKeys = Object.keys(esmModule).sort(); | ||
|
||
expect(cjsKeys).toEqual(esmKeys); | ||
expect(cjsKeys).toEqual(["Server", "Session", "Telemetry"]); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "./tsconfig.build.json", | ||
"compilerOptions": { | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"outDir": "./dist/cjs" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "./tsconfig.build.json", | ||
"compilerOptions": { | ||
"module": "esnext", | ||
"moduleResolution": "bundler", | ||
"outDir": "./dist/esm" | ||
} | ||
} |
There was a problem hiding this comment.
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 therequire
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.
There was a problem hiding this comment.
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.