From cc4d26b5a59d510f2c878e973fd245e8eff27c2a Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 22 Sep 2023 11:46:38 -0400 Subject: [PATCH] fix: Ensure deprecated context.parserServices warns (#17593) * fix: Ensure deprecated context.parserServices warns Updated RuleTester to emit a deprecation warning whenever `context.parserServices` is used. * Update lib/rule-tester/rule-tester.js Co-authored-by: Milos Djermanovic * Update lib/rule-tester/rule-tester.js Co-authored-by: Milos Djermanovic --------- Co-authored-by: Milos Djermanovic --- lib/rule-tester/rule-tester.js | 63 ++++++++++++++++------- tests/lib/rule-tester/rule-tester.js | 76 ++++++++++++++++------------ 2 files changed, 88 insertions(+), 51 deletions(-) diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js index a180ee570ae..d9f1a354b69 100644 --- a/lib/rule-tester/rule-tester.js +++ b/lib/rule-tester/rule-tester.js @@ -396,6 +396,22 @@ function emitCodePathCurrentSegmentsWarning(ruleName) { } } +/** + * Emit a deprecation warning if `context.parserServices` is used. + * @param {string} ruleName Name of the rule. + * @returns {void} + */ +function emitParserServicesWarning(ruleName) { + if (!emitParserServicesWarning[`warned-${ruleName}`]) { + emitParserServicesWarning[`warned-${ruleName}`] = true; + process.emitWarning( + `"${ruleName}" rule is using \`context.parserServices\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.parserServices\` instead.`, + "DeprecationWarning" + ); + } +} + + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ @@ -627,26 +643,37 @@ class RuleTester { freezeDeeply(context.settings); freezeDeeply(context.parserOptions); - const newContext = Object.freeze( - Object.create( - context, - Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [ - methodName, - { - value(...args) { - - // emit deprecation warning - emitDeprecatedContextMethodWarning(ruleName, methodName); - - // call the original method - return context[methodName].call(this, ...args); - }, - enumerable: true - } - ])) - ) + // wrap all deprecated methods + const newContext = Object.create( + context, + Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [ + methodName, + { + value(...args) { + + // emit deprecation warning + emitDeprecatedContextMethodWarning(ruleName, methodName); + + // call the original method + return context[methodName].call(this, ...args); + }, + enumerable: true + } + ])) ); + // emit warning about context.parserServices + const parserServices = context.parserServices; + + Object.defineProperty(newContext, "parserServices", { + get() { + emitParserServicesWarning(ruleName); + return parserServices; + } + }); + + Object.freeze(newContext); + return (typeof rule === "function" ? rule : rule.create)(newContext); } })); diff --git a/tests/lib/rule-tester/rule-tester.js b/tests/lib/rule-tester/rule-tester.js index 756444e4ea9..3e5c893cf71 100644 --- a/tests/lib/rule-tester/rule-tester.js +++ b/tests/lib/rule-tester/rule-tester.js @@ -1243,39 +1243,6 @@ describe("RuleTester", () => { }); }); - it("should pass-through services from parseForESLint to the rule", () => { - const enhancedParserPath = require.resolve("../../fixtures/parsers/enhanced-parser"); - const disallowHiRule = { - create: context => ({ - Literal(node) { - assert.strictEqual(context.parserServices, context.sourceCode.parserServices); - - const disallowed = context.sourceCode.parserServices.test.getMessage(); // returns "Hi!" - - if (node.value === disallowed) { - context.report({ node, message: `Don't use '${disallowed}'` }); - } - } - }) - }; - - ruleTester.run("no-hi", disallowHiRule, { - valid: [ - { - code: "'Hello!'", - parser: enhancedParserPath - } - ], - invalid: [ - { - code: "'Hi!'", - parser: enhancedParserPath, - errors: [{ message: "Don't use 'Hi!'" }] - } - ] - }); - }); - it("should prevent invalid options schemas", () => { assert.throws(() => { ruleTester.run("no-invalid-schema", require("../../fixtures/testers/rule-tester/no-invalid-schema"), { @@ -2515,6 +2482,48 @@ describe("RuleTester", () => { ); }); + it("should pass-through services from parseForESLint to the rule and log deprecation notice", () => { + const enhancedParserPath = require.resolve("../../fixtures/parsers/enhanced-parser"); + const disallowHiRule = { + create: context => ({ + Literal(node) { + assert.strictEqual(context.parserServices, context.sourceCode.parserServices); + + const disallowed = context.sourceCode.parserServices.test.getMessage(); // returns "Hi!" + + if (node.value === disallowed) { + context.report({ node, message: `Don't use '${disallowed}'` }); + } + } + }) + }; + + ruleTester.run("no-hi", disallowHiRule, { + valid: [ + { + code: "'Hello!'", + parser: enhancedParserPath + } + ], + invalid: [ + { + code: "'Hi!'", + parser: enhancedParserPath, + errors: [{ message: "Don't use 'Hi!'" }] + } + ] + }); + + assert.strictEqual(processStub.callCount, 1, "calls `process.emitWarning()` once"); + assert.deepStrictEqual( + processStub.getCall(0).args, + [ + "\"no-hi\" rule is using `context.parserServices`, which is deprecated and will be removed in ESLint v9. Please use `sourceCode.parserServices` instead.", + "DeprecationWarning" + ] + ); + + }); Object.entries({ getSource: "getText", getSourceLines: "getLines", @@ -2583,6 +2592,7 @@ describe("RuleTester", () => { }); + }); /**