You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					177 lines
				
				6.5 KiB
			
		
		
			
		
	
	
					177 lines
				
				6.5 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @fileoverview Rule to disallow certain object properties
							 | 
						||
| 
								 | 
							
								 * @author Will Klein & Eli White
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const astUtils = require("./utils/ast-utils");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								// Rule Definition
							 | 
						||
| 
								 | 
							
								//------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = {
							 | 
						||
| 
								 | 
							
								    meta: {
							 | 
						||
| 
								 | 
							
								        type: "suggestion",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        docs: {
							 | 
						||
| 
								 | 
							
								            description: "disallow certain properties on certain objects",
							 | 
						||
| 
								 | 
							
								            category: "Best Practices",
							 | 
						||
| 
								 | 
							
								            recommended: false,
							 | 
						||
| 
								 | 
							
								            url: "https://eslint.org/docs/rules/no-restricted-properties"
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        schema: {
							 | 
						||
| 
								 | 
							
								            type: "array",
							 | 
						||
| 
								 | 
							
								            items: {
							 | 
						||
| 
								 | 
							
								                anyOf: [ // `object` and `property` are both optional, but at least one of them must be provided.
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        type: "object",
							 | 
						||
| 
								 | 
							
								                        properties: {
							 | 
						||
| 
								 | 
							
								                            object: {
							 | 
						||
| 
								 | 
							
								                                type: "string"
							 | 
						||
| 
								 | 
							
								                            },
							 | 
						||
| 
								 | 
							
								                            property: {
							 | 
						||
| 
								 | 
							
								                                type: "string"
							 | 
						||
| 
								 | 
							
								                            },
							 | 
						||
| 
								 | 
							
								                            message: {
							 | 
						||
| 
								 | 
							
								                                type: "string"
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        },
							 | 
						||
| 
								 | 
							
								                        additionalProperties: false,
							 | 
						||
| 
								 | 
							
								                        required: ["object"]
							 | 
						||
| 
								 | 
							
								                    },
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        type: "object",
							 | 
						||
| 
								 | 
							
								                        properties: {
							 | 
						||
| 
								 | 
							
								                            object: {
							 | 
						||
| 
								 | 
							
								                                type: "string"
							 | 
						||
| 
								 | 
							
								                            },
							 | 
						||
| 
								 | 
							
								                            property: {
							 | 
						||
| 
								 | 
							
								                                type: "string"
							 | 
						||
| 
								 | 
							
								                            },
							 | 
						||
| 
								 | 
							
								                            message: {
							 | 
						||
| 
								 | 
							
								                                type: "string"
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        },
							 | 
						||
| 
								 | 
							
								                        additionalProperties: false,
							 | 
						||
| 
								 | 
							
								                        required: ["property"]
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                ]
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            uniqueItems: true
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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, { message: option.message });
							 | 
						||
| 
								 | 
							
								            } else if (typeof propertyName === "undefined") {
							 | 
						||
| 
								 | 
							
								                globallyRestrictedObjects.set(objectName, { message: option.message });
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                if (!restrictedProperties.has(objectName)) {
							 | 
						||
| 
								 | 
							
								                    restrictedProperties.set(objectName, new Map());
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                restrictedProperties.get(objectName).set(propertyName, {
							 | 
						||
| 
								 | 
							
								                    message: option.message
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * 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) {
							 | 
						||
| 
								 | 
							
								                const message = matchedObjectProperty.message ? ` ${matchedObjectProperty.message}` : "";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                context.report({
							 | 
						||
| 
								 | 
							
								                    node,
							 | 
						||
| 
								 | 
							
								                    // eslint-disable-next-line eslint-plugin/report-message-format
							 | 
						||
| 
								 | 
							
								                    message: "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}",
							 | 
						||
| 
								 | 
							
								                    data: {
							 | 
						||
| 
								 | 
							
								                        objectName,
							 | 
						||
| 
								 | 
							
								                        propertyName,
							 | 
						||
| 
								 | 
							
								                        message
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            } else if (globalMatchedProperty) {
							 | 
						||
| 
								 | 
							
								                const message = globalMatchedProperty.message ? ` ${globalMatchedProperty.message}` : "";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                context.report({
							 | 
						||
| 
								 | 
							
								                    node,
							 | 
						||
| 
								 | 
							
								                    // eslint-disable-next-line eslint-plugin/report-message-format
							 | 
						||
| 
								 | 
							
								                    message: "'{{propertyName}}' is restricted from being used.{{message}}",
							 | 
						||
| 
								 | 
							
								                    data: {
							 | 
						||
| 
								 | 
							
								                        propertyName,
							 | 
						||
| 
								 | 
							
								                        message
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                });
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * Checks property accesses in a destructuring assignment expression, e.g. `var foo; ({foo} = bar);`
							 | 
						||
| 
								 | 
							
								         * @param {ASTNode} node An AssignmentExpression or AssignmentPattern node
							 | 
						||
| 
								 | 
							
								         * @returns {undefined}
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        function checkDestructuringAssignment(node) {
							 | 
						||
| 
								 | 
							
								            if (node.right.type === "Identifier") {
							 | 
						||
| 
								 | 
							
								                const objectName = node.right.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (node.left.type === "ObjectPattern") {
							 | 
						||
| 
								 | 
							
								                    node.left.properties.forEach(property => {
							 | 
						||
| 
								 | 
							
								                        checkPropertyAccess(node.left, objectName, astUtils.getStaticPropertyName(property));
							 | 
						||
| 
								 | 
							
								                    });
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return {
							 | 
						||
| 
								 | 
							
								            MemberExpression(node) {
							 | 
						||
| 
								 | 
							
								                checkPropertyAccess(node, node.object && node.object.name, astUtils.getStaticPropertyName(node));
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            VariableDeclarator(node) {
							 | 
						||
| 
								 | 
							
								                if (node.init && node.init.type === "Identifier") {
							 | 
						||
| 
								 | 
							
								                    const objectName = node.init.name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (node.id.type === "ObjectPattern") {
							 | 
						||
| 
								 | 
							
								                        node.id.properties.forEach(property => {
							 | 
						||
| 
								 | 
							
								                            checkPropertyAccess(node.id, objectName, astUtils.getStaticPropertyName(property));
							 | 
						||
| 
								 | 
							
								                        });
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            AssignmentExpression: checkDestructuringAssignment,
							 | 
						||
| 
								 | 
							
								            AssignmentPattern: checkDestructuringAssignment
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 |