UNPKG

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