| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- /**
- * @fileoverview Rule to disallow certain object properties
- * @author Will Klein & Eli White
- */
- "use strict";
- const astUtils = require("./utils/ast-utils");
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- /** @type {import('../types').Rule.RuleModule} */
- module.exports = {
- meta: {
- type: "suggestion",
- docs: {
- description: "Disallow certain properties on certain objects",
- recommended: false,
- url: "https://eslint.org/docs/latest/rules/no-restricted-properties",
- },
- schema: {
- type: "array",
- items: {
- type: "object",
- properties: {
- object: {
- type: "string",
- },
- property: {
- type: "string",
- },
- allowObjects: {
- type: "array",
- items: {
- type: "string",
- },
- uniqueItems: true,
- },
- allowProperties: {
- type: "array",
- items: {
- type: "string",
- },
- uniqueItems: true,
- },
- message: {
- type: "string",
- },
- },
- anyOf: [
- {
- required: ["object"],
- },
- {
- required: ["property"],
- },
- ],
- not: {
- anyOf: [
- { required: ["allowObjects", "object"] },
- { required: ["allowProperties", "property"] },
- ],
- },
- additionalProperties: false,
- },
- uniqueItems: true,
- },
- messages: {
- restrictedObjectProperty:
- // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
- "'{{objectName}}.{{propertyName}}' is restricted from being used.{{allowedPropertiesMessage}}{{message}}",
- restrictedProperty:
- // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
- "'{{propertyName}}' is restricted from being used.{{allowedObjectsMessage}}{{message}}",
- },
- },
- create(context) {
- const restrictedCalls = context.options;
- if (restrictedCalls.length === 0) {
- return {};
- }
- const restrictedProperties = new Map();
- const globallyRestrictedObjects = new Map();
- const globallyRestrictedProperties = new Map();
- restrictedCalls.forEach(option => {
- const objectName = option.object;
- const propertyName = option.property;
- if (typeof objectName === "undefined") {
- globallyRestrictedProperties.set(propertyName, {
- allowObjects: option.allowObjects,
- message: option.message,
- });
- } else if (typeof propertyName === "undefined") {
- globallyRestrictedObjects.set(objectName, {
- allowProperties: option.allowProperties,
- message: option.message,
- });
- } else {
- if (!restrictedProperties.has(objectName)) {
- restrictedProperties.set(objectName, new Map());
- }
- restrictedProperties.get(objectName).set(propertyName, {
- message: option.message,
- });
- }
- });
- /**
- * Checks if a name is in the allowed list.
- * @param {string} name The name to check
- * @param {string[]} [allowedList] The list of allowed names
- * @returns {boolean} True if the name is allowed, false otherwise
- */
- function isAllowed(name, allowedList) {
- if (!allowedList) {
- return false;
- }
- return allowedList.includes(name);
- }
- /**
- * Checks to see whether a property access is restricted, and reports it if so.
- * @param {ASTNode} node The node to report
- * @param {string} objectName The name of the object
- * @param {string} propertyName The name of the property
- * @returns {undefined}
- */
- function checkPropertyAccess(node, objectName, propertyName) {
- if (propertyName === null) {
- return;
- }
- const matchedObject = restrictedProperties.get(objectName);
- const matchedObjectProperty = matchedObject
- ? matchedObject.get(propertyName)
- : globallyRestrictedObjects.get(objectName);
- const globalMatchedProperty =
- globallyRestrictedProperties.get(propertyName);
- if (
- matchedObjectProperty &&
- !isAllowed(propertyName, matchedObjectProperty.allowProperties)
- ) {
- const message = matchedObjectProperty.message
- ? ` ${matchedObjectProperty.message}`
- : "";
- const allowedPropertiesMessage =
- matchedObjectProperty.allowProperties
- ? ` Only these properties are allowed: ${matchedObjectProperty.allowProperties.join(", ")}.`
- : "";
- context.report({
- node,
- messageId: "restrictedObjectProperty",
- data: {
- objectName,
- propertyName,
- message,
- allowedPropertiesMessage,
- },
- });
- } else if (
- globalMatchedProperty &&
- !isAllowed(objectName, globalMatchedProperty.allowObjects)
- ) {
- const message = globalMatchedProperty.message
- ? ` ${globalMatchedProperty.message}`
- : "";
- const allowedObjectsMessage = globalMatchedProperty.allowObjects
- ? ` Property '${propertyName}' is only allowed on these objects: ${globalMatchedProperty.allowObjects.join(", ")}.`
- : "";
- context.report({
- node,
- messageId: "restrictedProperty",
- data: {
- propertyName,
- message,
- allowedObjectsMessage,
- },
- });
- }
- }
- return {
- MemberExpression(node) {
- checkPropertyAccess(
- node,
- node.object && node.object.name,
- astUtils.getStaticPropertyName(node),
- );
- },
- ObjectPattern(node) {
- let objectName = null;
- if (node.parent.type === "VariableDeclarator") {
- if (
- node.parent.init &&
- node.parent.init.type === "Identifier"
- ) {
- objectName = node.parent.init.name;
- }
- } else if (
- node.parent.type === "AssignmentExpression" ||
- node.parent.type === "AssignmentPattern"
- ) {
- if (node.parent.right.type === "Identifier") {
- objectName = node.parent.right.name;
- }
- }
- node.properties.forEach(property => {
- checkPropertyAccess(
- node,
- objectName,
- astUtils.getStaticPropertyName(property),
- );
- });
- },
- };
- },
- };
|