UNPKG

eslint-plugin-canonical

Version:
165 lines (164 loc) 6.22 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable no-prototype-builtins */ /** * @author https://github.com/dustinspecker/eslint-plugin-no-use-extend-native/blob/master/src/no-use-extend-native.js */ const is_get_set_prop_1 = __importDefault(require("is-get-set-prop")); const is_js_type_1 = __importDefault(require("is-js-type")); const is_obj_prop_1 = __importDefault(require("is-obj-prop")); const is_proto_prop_1 = __importDefault(require("is-proto-prop")); const utilities_1 = require("../utilities"); const isProtoProperty = (jsType, propertyName) => { if (jsType === 'Object' && propertyName === 'groupBy') { return true; } return (0, is_proto_prop_1.default)(jsType, propertyName); }; /** * Return type of value of left or right * * @param {object} subject - left or right of node.object * @returns {string} - type of o */ const getType = (subject) => { const type = typeof subject.value; if (subject.regex) { return 'RegExp'; } return type.charAt(0).toUpperCase() + type.slice(1); }; /** * Returns type of binary expression result * * @param {object} subject - node's object with a BinaryExpression type * @returns {string} - type of value produced */ const binaryExpressionProduces = (subject) => { const leftType = subject.left.type === 'BinaryExpression' ? binaryExpressionProduces(subject.left) : getType(subject.left); const rightType = subject.right.type === 'BinaryExpression' ? binaryExpressionProduces(subject.right) : getType(subject.right); const isRegExp = leftType === rightType && leftType === 'RegExp'; if (leftType === 'String' || rightType === 'String' || isRegExp) { return 'String'; } if (leftType === rightType) { return leftType; } return 'Unknown'; }; /** * Returns the JS type and property name * * @param {object} node - node to examine * @returns {object} - jsType and propertyName */ const getJsTypeAndPropertyName = (node) => { let jsType; let propertyName; switch (node.object.type) { case 'NewExpression': jsType = node.object.callee.name; break; case 'Literal': jsType = getType(node.object); break; case 'BinaryExpression': jsType = binaryExpressionProduces(node.object); break; case 'Identifier': if (node.property.name === 'prototype' && node.parent.property) { jsType = node.object.name; propertyName = node.parent.property.name; } else { jsType = node.object.name; } break; default: jsType = node.object.type.replace('Expression', ''); } propertyName = propertyName || node.property.name || node.property.value; return { jsType, propertyName, }; }; const isUnkownGettSetterOrJsTypeExpressed = (jsType, propertyName, usageType) => { const isExpression = usageType === 'ExpressionStatement' || usageType === 'MemberExpression'; return (isExpression && !(0, is_get_set_prop_1.default)(jsType, propertyName) && !isProtoProperty(jsType, propertyName) && !(0, is_obj_prop_1.default)(jsType, propertyName)); }; /** * Determine if a jsType's usage of propertyName is valid * * @param {string} jsType - the JS type to validate * @param {string} propertyName - the property name to validate usage of on jsType * @param {string} usageType - how propertyName is being used * @returns {boolean} - is the usage invalid? */ const isInvalid = (jsType, propertyName, usageType) => { if (typeof propertyName !== 'string' || typeof jsType !== 'string' || !(0, is_js_type_1.default)(jsType)) { return false; } const unknownGetterSetterOrjsTypeExpressed = isUnkownGettSetterOrJsTypeExpressed(jsType, propertyName, usageType); const isFunctionCall = usageType === 'CallExpression'; const getterSetterCalledAsFunction = isFunctionCall && (0, is_get_set_prop_1.default)(jsType, propertyName); const unknownjsTypeCalledAsFunction = isFunctionCall && !isProtoProperty(jsType, propertyName) && !(0, is_obj_prop_1.default)(jsType, propertyName); return (unknownGetterSetterOrjsTypeExpressed || getterSetterCalledAsFunction || unknownjsTypeCalledAsFunction); }; exports.default = (0, utilities_1.createRule)({ create(context) { return { MemberExpression(node) { var _a, _b; if (node.computed && node.property.type === 'Identifier') { /** * handles cases like {}[i][j] * not enough information to identify type of variable in computed properties * so ignore false positives by not performing any checks */ return; } const isArgumentToParent = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.hasOwnProperty('arguments')) && 'arguments' in node.parent && node.parent.arguments.includes(node); const usageType = isArgumentToParent ? node.type : (_b = node.parent) === null || _b === void 0 ? void 0 : _b.type; const { propertyName, jsType } = getJsTypeAndPropertyName(node); if (isInvalid(jsType, propertyName, usageType) && isInvalid('Function', propertyName, usageType)) { context.report({ messageId: 'noExtendNative', node, }); } }, }; }, defaultOptions: [], meta: { docs: { description: '', }, messages: { noExtendNative: 'Avoid using extended native objects', }, schema: [], type: 'problem', }, name: 'no-use-extend-native', });