UNPKG

6.58 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to disallow certain object properties
3 * @author Will Klein & Eli White
4 */
5
6"use strict";
7
8const astUtils = require("../ast-utils");
9
10//------------------------------------------------------------------------------
11// Rule Definition
12//------------------------------------------------------------------------------
13
14module.exports = {
15 meta: {
16 docs: {
17 description: "disallow certain properties on certain objects",
18 category: "Best Practices",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/no-restricted-properties"
21 },
22
23 schema: {
24 type: "array",
25 items: {
26 anyOf: [ // `object` and `property` are both optional, but at least one of them must be provided.
27 {
28 type: "object",
29 properties: {
30 object: {
31 type: "string"
32 },
33 property: {
34 type: "string"
35 },
36 message: {
37 type: "string"
38 }
39 },
40 additionalProperties: false,
41 required: ["object"]
42 },
43 {
44 type: "object",
45 properties: {
46 object: {
47 type: "string"
48 },
49 property: {
50 type: "string"
51 },
52 message: {
53 type: "string"
54 }
55 },
56 additionalProperties: false,
57 required: ["property"]
58 }
59 ]
60 },
61 uniqueItems: true
62 }
63 },
64
65 create(context) {
66 const restrictedCalls = context.options;
67
68 if (restrictedCalls.length === 0) {
69 return {};
70 }
71
72 const restrictedProperties = new Map();
73 const globallyRestrictedObjects = new Map();
74 const globallyRestrictedProperties = new Map();
75
76 restrictedCalls.forEach(option => {
77 const objectName = option.object;
78 const propertyName = option.property;
79
80 if (typeof objectName === "undefined") {
81 globallyRestrictedProperties.set(propertyName, { message: option.message });
82 } else if (typeof propertyName === "undefined") {
83 globallyRestrictedObjects.set(objectName, { message: option.message });
84 } else {
85 if (!restrictedProperties.has(objectName)) {
86 restrictedProperties.set(objectName, new Map());
87 }
88
89 restrictedProperties.get(objectName).set(propertyName, {
90 message: option.message
91 });
92 }
93 });
94
95 /**
96 * Checks to see whether a property access is restricted, and reports it if so.
97 * @param {ASTNode} node The node to report
98 * @param {string} objectName The name of the object
99 * @param {string} propertyName The name of the property
100 * @returns {undefined}
101 */
102 function checkPropertyAccess(node, objectName, propertyName) {
103 if (propertyName === null) {
104 return;
105 }
106 const matchedObject = restrictedProperties.get(objectName);
107 const matchedObjectProperty = matchedObject ? matchedObject.get(propertyName) : globallyRestrictedObjects.get(objectName);
108 const globalMatchedProperty = globallyRestrictedProperties.get(propertyName);
109
110 if (matchedObjectProperty) {
111 const message = matchedObjectProperty.message ? ` ${matchedObjectProperty.message}` : "";
112
113 context.report({
114 node,
115 // eslint-disable-next-line eslint-plugin/report-message-format
116 message: "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}",
117 data: {
118 objectName,
119 propertyName,
120 message
121 }
122 });
123 } else if (globalMatchedProperty) {
124 const message = globalMatchedProperty.message ? ` ${globalMatchedProperty.message}` : "";
125
126 context.report({
127 node,
128 // eslint-disable-next-line eslint-plugin/report-message-format
129 message: "'{{propertyName}}' is restricted from being used.{{message}}",
130 data: {
131 propertyName,
132 message
133 }
134 });
135 }
136 }
137
138 /**
139 * Checks property accesses in a destructuring assignment expression, e.g. `var foo; ({foo} = bar);`
140 * @param {ASTNode} node An AssignmentExpression or AssignmentPattern node
141 * @returns {undefined}
142 */
143 function checkDestructuringAssignment(node) {
144 if (node.right.type === "Identifier") {
145 const objectName = node.right.name;
146
147 if (node.left.type === "ObjectPattern") {
148 node.left.properties.forEach(property => {
149 checkPropertyAccess(node.left, objectName, astUtils.getStaticPropertyName(property));
150 });
151 }
152 }
153 }
154
155 return {
156 MemberExpression(node) {
157 checkPropertyAccess(node, node.object && node.object.name, astUtils.getStaticPropertyName(node));
158 },
159 VariableDeclarator(node) {
160 if (node.init && node.init.type === "Identifier") {
161 const objectName = node.init.name;
162
163 if (node.id.type === "ObjectPattern") {
164 node.id.properties.forEach(property => {
165 checkPropertyAccess(node.id, objectName, astUtils.getStaticPropertyName(property));
166 });
167 }
168 }
169 },
170 AssignmentExpression: checkDestructuringAssignment,
171 AssignmentPattern: checkDestructuringAssignment
172 };
173 }
174};