From 2cfe7ea73450c5ac799f1c3fb264fac12d8f9c52 Mon Sep 17 00:00:00 2001 From: Agyei Holy Date: Tue, 5 May 2026 16:28:17 -0500 Subject: [PATCH] Fix bracket-notation handling in no-printing-tojson rule --- .../rules/no-printing-tojson.js | 47 +++++++++++----- .../tests/no-printing-tojson.test.mjs | 53 +++++++++++++++++++ 2 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 buildscripts/eslint-plugin-mongodb/tests/no-printing-tojson.test.mjs diff --git a/buildscripts/eslint-plugin-mongodb/rules/no-printing-tojson.js b/buildscripts/eslint-plugin-mongodb/rules/no-printing-tojson.js index 0753877985519..7caf9ca38800f 100644 --- a/buildscripts/eslint-plugin-mongodb/rules/no-printing-tojson.js +++ b/buildscripts/eslint-plugin-mongodb/rules/no-printing-tojson.js @@ -1,4 +1,4 @@ -const print_fns = [ +const printFunctions = [ "jsTestLog", "jsTest.log", "jsTest.log.info", @@ -8,14 +8,39 @@ const print_fns = [ "print", ]; -function flattenMemberExpressionName(expr) { - if (expr.object.type == "MemberExpression") { - return flattenMemberExpressionName(expr.object) + "." + expr.property.name; - } else if (expr.object.type == "Identifier") { - return expr.object.name + "." + expr.property.name; - } else { +function getPropertyName(expr) { + if (!expr.computed && expr.property.type == "Identifier") { + return expr.property.name; + } + + if (expr.computed && expr.property.type == "Literal" && typeof expr.property.value == "string") { + return expr.property.value; + } + + return ""; +} + +function flattenCallTargetName(expr) { + if (expr.type == "ChainExpression") { + return flattenCallTargetName(expr.expression); + } + + if (expr.type == "Identifier") { + return expr.name; + } + + if (expr.type != "MemberExpression") { return ""; } + + const objectName = flattenCallTargetName(expr.object); + const propertyName = getPropertyName(expr); + + if (!objectName || !propertyName) { + return ""; + } + + return `${objectName}.${propertyName}`; } export default { @@ -30,11 +55,9 @@ export default { create(context) { return { CallExpression: function (node) { - if (node.callee.type == "MemberExpression") { - node.callee.name = flattenMemberExpressionName(node.callee); - } else if (node.callee.type != "Identifier") return; + const calleeName = flattenCallTargetName(node.callee); - if (print_fns.every((name) => name != node.callee.name)) return; + if (printFunctions.every((name) => name != calleeName)) return; node.arguments.forEach((arg) => { if (arg.type != "CallExpression") return; @@ -46,7 +69,7 @@ export default { context.report({ node, message: `Calling ${arg.callee.name}() as a parameter of ${ - node.callee.name + calleeName }(). Consider using toJsonForLog() instead or disable this rule by adding '// eslint-disable-next-line mongodb/no-printing-tojson'`, fix(fixer) { return fixer.replaceTextRange([arg.callee.start, arg.callee.end], "toJsonForLog"); diff --git a/buildscripts/eslint-plugin-mongodb/tests/no-printing-tojson.test.mjs b/buildscripts/eslint-plugin-mongodb/tests/no-printing-tojson.test.mjs new file mode 100644 index 0000000000000..2b4b65ba4202e --- /dev/null +++ b/buildscripts/eslint-plugin-mongodb/tests/no-printing-tojson.test.mjs @@ -0,0 +1,53 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import {Linter} from "eslint"; + +import rule from "../rules/no-printing-tojson.js"; + +const config = [ + { + plugins: { + mongodb: { + rules: { + "no-printing-tojson": rule, + }, + }, + }, + rules: { + "mongodb/no-printing-tojson": "error", + }, + languageOptions: { + ecmaVersion: 2022, + }, + }, +]; + +function verify(code) { + return new Linter().verify(code, config, "test.js"); +} + +function verifyAndFix(code) { + return new Linter().verifyAndFix(code, config, "test.js"); +} + +test("flags direct jsTest.log.info calls with tojson arguments", () => { + const messages = verify("jsTest.log.info(tojson(doc));"); + + assert.equal(messages.length, 1); + assert.match(messages[0].message, /jsTest\.log\.info\(\)/); +}); + +test("flags bracket-notation log helpers with tojson arguments", () => { + const messages = verify('jsTest["log"].info(tojson(doc));'); + + assert.equal(messages.length, 1); + assert.match(messages[0].message, /jsTest\.log\.info\(\)/); +}); + +test("autofixes bracket-notation log helpers to use toJsonForLog", () => { + const result = verifyAndFix('jsTest.log["info"](tojson(doc));'); + + assert.equal(result.fixed, true); + assert.equal(result.output, 'jsTest.log["info"](toJsonForLog(doc));'); +});