UNPKG

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