146 lines
4.2 KiB
JavaScript
146 lines
4.2 KiB
JavaScript
/**
|
|
* @fileoverview Rule to suggest using "Reflect" api over Function/Object methods
|
|
* @author Keith Cirkel <http://keithcirkel.co.uk>
|
|
* @deprecated in ESLint v3.9.0
|
|
*/
|
|
'use strict';
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** @type {import('../types').Rule.RuleModule} */
|
|
module.exports = {
|
|
meta: {
|
|
type: 'suggestion',
|
|
|
|
docs: {
|
|
description: 'Require `Reflect` methods where applicable',
|
|
recommended: false,
|
|
url: 'https://eslint.org/docs/latest/rules/prefer-reflect',
|
|
},
|
|
|
|
deprecated: {
|
|
message: 'The original intention of this rule was misguided.',
|
|
url: 'https://eslint.org/docs/latest/rules/prefer-reflect',
|
|
deprecatedSince: '3.9.0',
|
|
availableUntil: null,
|
|
replacedBy: [],
|
|
},
|
|
|
|
schema: [
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
exceptions: {
|
|
type: 'array',
|
|
items: {
|
|
enum: [
|
|
'apply',
|
|
'call',
|
|
'delete',
|
|
'defineProperty',
|
|
'getOwnPropertyDescriptor',
|
|
'getPrototypeOf',
|
|
'setPrototypeOf',
|
|
'isExtensible',
|
|
'getOwnPropertyNames',
|
|
'preventExtensions',
|
|
],
|
|
},
|
|
uniqueItems: true,
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
|
|
messages: {
|
|
preferReflect: 'Avoid using {{existing}}, instead use {{substitute}}.',
|
|
},
|
|
},
|
|
|
|
create(context) {
|
|
const existingNames = {
|
|
apply: 'Function.prototype.apply',
|
|
call: 'Function.prototype.call',
|
|
defineProperty: 'Object.defineProperty',
|
|
getOwnPropertyDescriptor: 'Object.getOwnPropertyDescriptor',
|
|
getPrototypeOf: 'Object.getPrototypeOf',
|
|
setPrototypeOf: 'Object.setPrototypeOf',
|
|
isExtensible: 'Object.isExtensible',
|
|
getOwnPropertyNames: 'Object.getOwnPropertyNames',
|
|
preventExtensions: 'Object.preventExtensions',
|
|
};
|
|
|
|
const reflectSubstitutes = {
|
|
apply: 'Reflect.apply',
|
|
call: 'Reflect.apply',
|
|
defineProperty: 'Reflect.defineProperty',
|
|
getOwnPropertyDescriptor: 'Reflect.getOwnPropertyDescriptor',
|
|
getPrototypeOf: 'Reflect.getPrototypeOf',
|
|
setPrototypeOf: 'Reflect.setPrototypeOf',
|
|
isExtensible: 'Reflect.isExtensible',
|
|
getOwnPropertyNames: 'Reflect.getOwnPropertyNames',
|
|
preventExtensions: 'Reflect.preventExtensions',
|
|
};
|
|
|
|
const exceptions = (context.options[0] || {}).exceptions || [];
|
|
|
|
/**
|
|
* Reports the Reflect violation based on the `existing` and `substitute`
|
|
* @param {Object} node The node that violates the rule.
|
|
* @param {string} existing The existing method name that has been used.
|
|
* @param {string} substitute The Reflect substitute that should be used.
|
|
* @returns {void}
|
|
*/
|
|
function report(node, existing, substitute) {
|
|
context.report({
|
|
node,
|
|
messageId: 'preferReflect',
|
|
data: {
|
|
existing,
|
|
substitute,
|
|
},
|
|
});
|
|
}
|
|
|
|
return {
|
|
CallExpression(node) {
|
|
const methodName = (node.callee.property || {}).name;
|
|
const isReflectCall = (node.callee.object || {}).name === 'Reflect';
|
|
const hasReflectSubstitute = Object.hasOwn(
|
|
reflectSubstitutes,
|
|
methodName
|
|
);
|
|
const userConfiguredException = exceptions.includes(methodName);
|
|
|
|
if (
|
|
hasReflectSubstitute &&
|
|
!isReflectCall &&
|
|
!userConfiguredException
|
|
) {
|
|
report(
|
|
node,
|
|
existingNames[methodName],
|
|
reflectSubstitutes[methodName]
|
|
);
|
|
}
|
|
},
|
|
UnaryExpression(node) {
|
|
const isDeleteOperator = node.operator === 'delete';
|
|
const targetsIdentifier = node.argument.type === 'Identifier';
|
|
const userConfiguredException = exceptions.includes('delete');
|
|
|
|
if (
|
|
isDeleteOperator &&
|
|
!targetsIdentifier &&
|
|
!userConfiguredException
|
|
) {
|
|
report(node, 'the delete keyword', 'Reflect.deleteProperty');
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|