Skip to content

Commit 32b2327

Browse files
authored
feat: Emit deprecation warnings in RuleTester (#17527)
* feat: Emit deprecation warnings in RuleTester Emits deprecation warnings when using methods on `context` that are deprecated. Refs #17520 * Revert flat-rule-tester * Fix linting error
1 parent acb7df3 commit 32b2327

2 files changed

Lines changed: 127 additions & 1 deletion

File tree

lib/rule-tester/rule-tester.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,30 @@ const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters
164164

165165
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
166166

167+
const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
168+
getSource: "getText",
169+
getSourceLines: "getLines",
170+
getAllComments: "getAllComments",
171+
getNodeByRangeIndex: "getNodeByRangeIndex",
172+
173+
// getComments: "getComments", -- already handled by a separate error
174+
getCommentsBefore: "getCommentsBefore",
175+
getCommentsAfter: "getCommentsAfter",
176+
getCommentsInside: "getCommentsInside",
177+
getJSDocComment: "getJSDocComment",
178+
getFirstToken: "getFirstToken",
179+
getFirstTokens: "getFirstTokens",
180+
getLastToken: "getLastToken",
181+
getLastTokens: "getLastTokens",
182+
getTokenAfter: "getTokenAfter",
183+
getTokenBefore: "getTokenBefore",
184+
getTokenByRangeStart: "getTokenByRangeStart",
185+
getTokens: "getTokens",
186+
getTokensAfter: "getTokensAfter",
187+
getTokensBefore: "getTokensBefore",
188+
getTokensBetween: "getTokensBetween"
189+
};
190+
167191
/**
168192
* Clones a given value deeply.
169193
* Note: This ignores `parent` property.
@@ -335,6 +359,22 @@ function emitMissingSchemaWarning(ruleName) {
335359
}
336360
}
337361

362+
/**
363+
* Emit a deprecation warning if a rule uses a deprecated `context` method.
364+
* @param {string} ruleName Name of the rule.
365+
* @param {string} methodName The name of the method on `context` that was used.
366+
* @returns {void}
367+
*/
368+
function emitDeprecatedContextMethodWarning(ruleName, methodName) {
369+
if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
370+
emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
371+
process.emitWarning(
372+
`"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
373+
"DeprecationWarning"
374+
);
375+
}
376+
}
377+
338378
//------------------------------------------------------------------------------
339379
// Public Interface
340380
//------------------------------------------------------------------------------
@@ -566,7 +606,27 @@ class RuleTester {
566606
freezeDeeply(context.settings);
567607
freezeDeeply(context.parserOptions);
568608

569-
return (typeof rule === "function" ? rule : rule.create)(context);
609+
const newContext = Object.freeze(
610+
Object.create(
611+
context,
612+
Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
613+
methodName,
614+
{
615+
value(...args) {
616+
617+
// emit deprecation warning
618+
emitDeprecatedContextMethodWarning(ruleName, methodName);
619+
620+
// call the original method
621+
return context[methodName].call(this, ...args);
622+
},
623+
enumerable: true
624+
}
625+
]))
626+
)
627+
);
628+
629+
return (typeof rule === "function" ? rule : rule.create)(newContext);
570630
}
571631
}));
572632

tests/lib/rule-tester/rule-tester.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,6 +2489,72 @@ describe("RuleTester", () => {
24892489

24902490
assert.strictEqual(processStub.callCount, 0, "never calls `process.emitWarning()`");
24912491
});
2492+
2493+
Object.entries({
2494+
getSource: "getText",
2495+
getSourceLines: "getLines",
2496+
getAllComments: "getAllComments",
2497+
getNodeByRangeIndex: "getNodeByRangeIndex",
2498+
getCommentsBefore: "getCommentsBefore",
2499+
getCommentsAfter: "getCommentsAfter",
2500+
getCommentsInside: "getCommentsInside",
2501+
getJSDocComment: "getJSDocComment",
2502+
getFirstToken: "getFirstToken",
2503+
getFirstTokens: "getFirstTokens",
2504+
getLastToken: "getLastToken",
2505+
getLastTokens: "getLastTokens",
2506+
getTokenAfter: "getTokenAfter",
2507+
getTokenBefore: "getTokenBefore",
2508+
getTokenByRangeStart: "getTokenByRangeStart",
2509+
getTokens: "getTokens",
2510+
getTokensAfter: "getTokensAfter",
2511+
getTokensBefore: "getTokensBefore",
2512+
getTokensBetween: "getTokensBetween"
2513+
}).forEach(([methodName, replacementName]) => {
2514+
2515+
2516+
it(`should log a deprecation warning when calling \`context.${methodName}\``, () => {
2517+
const ruleToCheckDeprecation = {
2518+
meta: {
2519+
type: "problem",
2520+
schema: []
2521+
},
2522+
create(context) {
2523+
return {
2524+
Program(node) {
2525+
2526+
// special case
2527+
if (methodName === "getTokensBetween") {
2528+
context[methodName](node, node);
2529+
} else {
2530+
context[methodName](node);
2531+
}
2532+
2533+
context.report({ node, message: "bad" });
2534+
}
2535+
};
2536+
}
2537+
};
2538+
2539+
ruleTester.run("deprecated-method", ruleToCheckDeprecation, {
2540+
valid: [],
2541+
invalid: [
2542+
{ code: "var foo = bar;", options: [], errors: 1 }
2543+
]
2544+
});
2545+
2546+
assert.strictEqual(processStub.callCount, 1, "calls `process.emitWarning()` once");
2547+
assert.deepStrictEqual(
2548+
processStub.getCall(0).args,
2549+
[
2550+
`"deprecated-method" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${replacementName}()\` instead.`,
2551+
"DeprecationWarning"
2552+
]
2553+
);
2554+
});
2555+
2556+
});
2557+
24922558
});
24932559

24942560
/**

0 commit comments

Comments
 (0)