diff --git a/.mocharc.js b/.mocharc.js index 13d28e27c2f..6ff6b3c09e3 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -5,8 +5,8 @@ const [major] = process.versions.node.split('.'); /** @type {import("mocha").MochaOptions} */ module.exports = { require: [ - 'source-map-support/register', 'ts-node/register', + 'test/tools/runner/source_map.cjs', 'test/tools/runner/throw_rejections.cjs', 'test/tools/runner/chai_addons.ts', 'test/tools/runner/ee_checker.ts' @@ -18,5 +18,8 @@ module.exports = { reporter: 'test/tools/reporter/mongodb_reporter.js', sort: true, color: true, - 'node-option': Number(major) >= 23 ? ['no-experimental-strip-types'] : undefined + 'node-option': + Number(major) >= 23 + ? ['enable-source-maps', 'no-experimental-strip-types'] + : ['enable-source-maps'] }; diff --git a/package-lock.json b/package-lock.json index d584873227e..3dbae71a69b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,6 @@ "@typescript-eslint/eslint-plugin": "^8.46.3", "@typescript-eslint/parser": "^8.31.1", "aws4": "^1.13.2", - "baseline-browser-mapping": "^2.10.0", "chai": "^4.4.1", "chai-subset": "^1.6.0", "chalk": "^4.1.2", diff --git a/package.json b/package.json index 290e0e8c706..dd6f7558d69 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "build:bundle": "npm run bundle:driver && npm run bundle:types && npm run build:runtime-barrel", "build:runtime-barrel": "node etc/build-runtime-barrel.mjs", "bundle:driver": "node etc/bundle-driver.mjs", - "bundle:types": "npx tsc --project ./tsconfig.json --declaration --emitDeclarationOnly --declarationDir test/tools/runner/bundle/types", + "bundle:types": "node ./node_modules/typescript/bin/tsc --project ./tsconfig.json --declaration --emitDeclarationOnly --declarationDir test/tools/runner/bundle/types", "fix:eslint": "npm run check:eslint -- --fix", "prepare": "node etc/prepare.js", "preview:docs": "ts-node etc/docs/preview.ts", diff --git a/test/manual/mocharc.js b/test/manual/mocharc.js index 431bbf9a343..79ebc4da639 100644 --- a/test/manual/mocharc.js +++ b/test/manual/mocharc.js @@ -7,6 +7,7 @@ const [major] = process.versions.node.split('.'); module.exports = { require: [ 'ts-node/register', + 'test/tools/runner/source_map.cjs', 'test/tools/runner/throw_rejections.cjs', 'test/tools/runner/chai_addons.ts' ], @@ -14,5 +15,8 @@ module.exports = { failZero: true, color: true, timeout: 10000, - 'node-option': Number(major) >= 23 ? ['no-experimental-strip-types'] : undefined + 'node-option': + Number(major) >= 23 + ? ['enable-source-maps', 'no-experimental-strip-types'] + : ['enable-source-maps'] }; diff --git a/test/mocha_lambda.js b/test/mocha_lambda.js index 962ab8a344b..9a87d2a2187 100644 --- a/test/mocha_lambda.js +++ b/test/mocha_lambda.js @@ -17,5 +17,8 @@ module.exports = { reporter: 'test/tools/reporter/mongodb_reporter.js', sort: true, color: true, - 'node-option': Number(major) >= 23 ? ['no-experimental-strip-types'] : undefined + 'node-option': + Number(major) >= 23 + ? ['enable-source-maps', 'no-experimental-strip-types'] + : ['enable-source-maps'] }; diff --git a/test/mocha_mongodb.js b/test/mocha_mongodb.js index d048520fea6..c86bf54f9a5 100644 --- a/test/mocha_mongodb.js +++ b/test/mocha_mongodb.js @@ -6,8 +6,8 @@ const [major] = process.versions.node.split('.'); /** @type {import("mocha").MochaOptions} */ module.exports = { require: [ - 'source-map-support/register', 'ts-node/register', + 'test/tools/runner/source_map.cjs', 'test/tools/runner/throw_rejections.cjs', 'test/tools/runner/chai_addons.ts', 'test/tools/runner/ee_checker.ts', @@ -31,5 +31,8 @@ module.exports = { 'test/integration/node-specific/examples/transactions.test.js', 'test/integration/node-specific/examples/versioned_api.js' ], - 'node-option': Number(major) >= 23 ? ['no-experimental-strip-types'] : undefined + 'node-option': + Number(major) >= 23 + ? ['enable-source-maps', 'no-experimental-strip-types'] + : ['enable-source-maps'] }; diff --git a/test/tools/runner/hooks/configuration.ts b/test/tools/runner/hooks/configuration.ts index 0220d81fb9b..db6a50344b2 100644 --- a/test/tools/runner/hooks/configuration.ts +++ b/test/tools/runner/hooks/configuration.ts @@ -1,10 +1,5 @@ /* eslint-disable simple-import-sort/imports */ -// eslint-disable-next-line @typescript-eslint/no-require-imports -require('source-map-support').install({ - hookRequire: true -}); - import * as process from 'process'; import * as os from 'os'; diff --git a/test/tools/runner/source_map.cjs b/test/tools/runner/source_map.cjs new file mode 100644 index 00000000000..2b8bab0621e --- /dev/null +++ b/test/tools/runner/source_map.cjs @@ -0,0 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-require-imports +require('source-map-support').install({ + hookRequire: true +}); diff --git a/test/tsconfig.json b/test/tsconfig.json index 25dc395eec4..9750969223a 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "strict": false, "allowJs": true, - "checkJs": false + "checkJs": false, + "sourceMap": true }, "include": [ "../node_modules/@types/mocha/index.d.ts", diff --git a/test/unit/source_map.test.ts b/test/unit/source_map.test.ts new file mode 100644 index 00000000000..acae1aa7d28 --- /dev/null +++ b/test/unit/source_map.test.ts @@ -0,0 +1,76 @@ +// These import-type lines are completely erased by the TypeScript compiler +// (no blank-line placeholder is left behind), so the compiled JS has fewer +// lines than this source file. That shifts every subsequent line upward in +// the JS output. +import type { Document as _Doc } from 'bson'; +import { expect } from 'chai'; + +import type { + AbstractCursor as _AbC, + AggregationCursor as _AC, + ChangeStream as _CS, + ClientSession as _Sess, + Collection as _Col, + Db as _Db, + FindCursor as _FC, + GridFSBucket as _GB, + MongoClient as _MC +} from '../../mongodb'; + +// ─── This Error is created at line 31 in the TypeScript source. ─────────── +// The twelve `import type` lines above are erased without replacement, so +// in the compiled JS this falls on line 31 − 12 = line 19. +// +// ts-node's bundled `@cspotcode/source-map-support` patches +// Error.prepareStackTrace so `error.stack` already shows the correct TS +// line. We DISABLE that patch to see what V8 reports natively. +// +// OLD commit (no --enable-source-maps): V8 says line 19 ← WRONG +// NEW commit ( --enable-source-maps): V8 says line 31 ← correct +const TS_SOURCE_LINE = 31; // must match the line below +const errorAtKnownLine = new Error('source-map probe'); // ← line 31 + +/** + * Capture a raw V8 stack frame by temporarily removing + * `@cspotcode/source-map-support`'s prepareStackTrace override (installed + * by ts-node) so we see exactly what V8 reports — with or without its own + * source-map awareness. + */ +function rawV8FrameOf(err: Error): { + file: string | null; + line: number | null; + col: number | null; +} { + const saved = Error.prepareStackTrace; + // Null → V8 uses its built-in formatter (honours --enable-source-maps). + Error.prepareStackTrace = undefined as unknown as typeof Error.prepareStackTrace; + const raw = err.stack ?? ''; // triggers V8 formatting with no hook + Error.prepareStackTrace = saved; + + // First "at …" line: " at Object. (/abs/path/file.ts:LINE:COL)" + const match = raw.split('\n')[1]?.match(/\((.+):(\d+):(\d+)\)$/); + if (!match) return { file: null, line: null, col: null }; + return { file: match[1], line: Number(match[2]), col: Number(match[3]) }; +} + +describe('Source maps', function () { + it('report the correct line number when enabled', function () { + const frame = rawV8FrameOf(errorAtKnownLine); + + if (process.env.VERBOSE) { + console.error('\n ── raw V8 frame (prepareStackTrace bypassed) ──'); + console.error(` file : ${frame.file}`); + console.error(` line : ${frame.line} (TypeScript source line is ${TS_SOURCE_LINE})`); + console.error(` col : ${frame.col}`); + console.error( + ` ${frame.line === TS_SOURCE_LINE ? '✔ line matches TS source' : `✘ line ${frame.line} ≠ TS source line ${TS_SOURCE_LINE} — source maps not applied by V8`}` + ); + } + expect(frame.line).to.equal( + TS_SOURCE_LINE, + `V8 reported line ${frame.line} but TypeScript source line is ${TS_SOURCE_LINE}. ` + + `This means --enable-source-maps is absent and V8 is reading the compiled-JS ` + + `line number instead of the original TypeScript line.` + ); + }); +});