Skip to content

Commit

Permalink
feat: Emit deprecation warnings in RuleTester (#17527)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nzakas committed Sep 2, 2023
1 parent acb7df3 commit 32b2327
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 1 deletion.
62 changes: 61 additions & 1 deletion lib/rule-tester/rule-tester.js
Expand Up @@ -164,6 +164,30 @@ const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters

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

const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getSource: "getText",
getSourceLines: "getLines",
getAllComments: "getAllComments",
getNodeByRangeIndex: "getNodeByRangeIndex",

// getComments: "getComments", -- already handled by a separate error
getCommentsBefore: "getCommentsBefore",
getCommentsAfter: "getCommentsAfter",
getCommentsInside: "getCommentsInside",
getJSDocComment: "getJSDocComment",
getFirstToken: "getFirstToken",
getFirstTokens: "getFirstTokens",
getLastToken: "getLastToken",
getLastTokens: "getLastTokens",
getTokenAfter: "getTokenAfter",
getTokenBefore: "getTokenBefore",
getTokenByRangeStart: "getTokenByRangeStart",
getTokens: "getTokens",
getTokensAfter: "getTokensAfter",
getTokensBefore: "getTokensBefore",
getTokensBetween: "getTokensBetween"
};

/**
* Clones a given value deeply.
* Note: This ignores `parent` property.
Expand Down Expand Up @@ -335,6 +359,22 @@ function emitMissingSchemaWarning(ruleName) {
}
}

/**
* Emit a deprecation warning if a rule uses a deprecated `context` method.
* @param {string} ruleName Name of the rule.
* @param {string} methodName The name of the method on `context` that was used.
* @returns {void}
*/
function emitDeprecatedContextMethodWarning(ruleName, methodName) {
if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
process.emitWarning(
`"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
"DeprecationWarning"
);
}
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -566,7 +606,27 @@ class RuleTester {
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);

return (typeof rule === "function" ? rule : rule.create)(context);
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
}
]))
)
);

return (typeof rule === "function" ? rule : rule.create)(newContext);
}
}));

Expand Down
66 changes: 66 additions & 0 deletions tests/lib/rule-tester/rule-tester.js
Expand Up @@ -2489,6 +2489,72 @@ describe("RuleTester", () => {

assert.strictEqual(processStub.callCount, 0, "never calls `process.emitWarning()`");
});

Object.entries({
getSource: "getText",
getSourceLines: "getLines",
getAllComments: "getAllComments",
getNodeByRangeIndex: "getNodeByRangeIndex",
getCommentsBefore: "getCommentsBefore",
getCommentsAfter: "getCommentsAfter",
getCommentsInside: "getCommentsInside",
getJSDocComment: "getJSDocComment",
getFirstToken: "getFirstToken",
getFirstTokens: "getFirstTokens",
getLastToken: "getLastToken",
getLastTokens: "getLastTokens",
getTokenAfter: "getTokenAfter",
getTokenBefore: "getTokenBefore",
getTokenByRangeStart: "getTokenByRangeStart",
getTokens: "getTokens",
getTokensAfter: "getTokensAfter",
getTokensBefore: "getTokensBefore",
getTokensBetween: "getTokensBetween"
}).forEach(([methodName, replacementName]) => {


it(`should log a deprecation warning when calling \`context.${methodName}\``, () => {
const ruleToCheckDeprecation = {
meta: {
type: "problem",
schema: []
},
create(context) {
return {
Program(node) {

// special case
if (methodName === "getTokensBetween") {
context[methodName](node, node);
} else {
context[methodName](node);
}

context.report({ node, message: "bad" });
}
};
}
};

ruleTester.run("deprecated-method", ruleToCheckDeprecation, {
valid: [],
invalid: [
{ code: "var foo = bar;", options: [], errors: 1 }
]
});

assert.strictEqual(processStub.callCount, 1, "calls `process.emitWarning()` once");
assert.deepStrictEqual(
processStub.getCall(0).args,
[
`"deprecated-method" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${replacementName}()\` instead.`,
"DeprecationWarning"
]
);
});

});

});

/**
Expand Down

0 comments on commit 32b2327

Please sign in to comment.