| /** |
| * @fileoverview Prefers Object.hasOwn() instead of Object.prototype.hasOwnProperty.call() |
| * @author Nitin Kumar |
| * @author Gautam Arora |
| */ |
| |
| "use strict"; |
| |
| //------------------------------------------------------------------------------ |
| // Requirements |
| //------------------------------------------------------------------------------ |
| |
| const astUtils = require("./utils/ast-utils"); |
| |
| //------------------------------------------------------------------------------ |
| // Helpers |
| //------------------------------------------------------------------------------ |
| |
| /** |
| * Checks if the given node is considered to be an access to a property of `Object.prototype`. |
| * @param {ASTNode} node `MemberExpression` node to evaluate. |
| * @returns {boolean} `true` if `node.object` is `Object`, `Object.prototype`, or `{}` (empty 'ObjectExpression' node). |
| */ |
| function hasLeftHandObject(node) { |
| |
| /* |
| * ({}).hasOwnProperty.call(obj, prop) - `true` |
| * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty |
| */ |
| if (node.object.type === "ObjectExpression" && node.object.properties.length === 0) { |
| return true; |
| } |
| |
| const objectNodeToCheck = node.object.type === "MemberExpression" && astUtils.getStaticPropertyName(node.object) === "prototype" ? node.object.object : node.object; |
| |
| if (objectNodeToCheck.type === "Identifier" && objectNodeToCheck.name === "Object") { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| /** @type {import('../shared/types').Rule} */ |
| module.exports = { |
| meta: { |
| type: "suggestion", |
| docs: { |
| description: |
| "disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`", |
| recommended: false, |
| url: "https://eslint.org/docs/rules/prefer-object-has-own" |
| }, |
| schema: [], |
| messages: { |
| useHasOwn: "Use 'Object.hasOwn()' instead of 'Object.prototype.hasOwnProperty.call()'." |
| }, |
| fixable: "code" |
| }, |
| create(context) { |
| return { |
| CallExpression(node) { |
| if (!(node.callee.type === "MemberExpression" && node.callee.object.type === "MemberExpression")) { |
| return; |
| } |
| |
| const calleePropertyName = astUtils.getStaticPropertyName(node.callee); |
| const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); |
| const isObject = hasLeftHandObject(node.callee.object); |
| |
| // check `Object` scope |
| const scope = context.getScope(); |
| const variable = astUtils.getVariableByName(scope, "Object"); |
| |
| if ( |
| calleePropertyName === "call" && |
| objectPropertyName === "hasOwnProperty" && |
| isObject && |
| variable && variable.scope.type === "global" |
| ) { |
| context.report({ |
| node, |
| messageId: "useHasOwn", |
| fix(fixer) { |
| const sourceCode = context.getSourceCode(); |
| |
| if (sourceCode.getCommentsInside(node.callee).length > 0) { |
| return null; |
| } |
| |
| const tokenJustBeforeNode = sourceCode.getTokenBefore(node.callee, { includeComments: true }); |
| |
| // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335 |
| if ( |
| tokenJustBeforeNode && |
| tokenJustBeforeNode.range[1] === node.callee.range[0] && |
| !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn") |
| ) { |
| return fixer.replaceText(node.callee, " Object.hasOwn"); |
| } |
| |
| return fixer.replaceText(node.callee, "Object.hasOwn"); |
| } |
| }); |
| } |
| } |
| }; |
| } |
| }; |