]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Rule to disallow certain object properties | |
3 | * @author Will Klein & Eli White | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | const astUtils = require("./utils/ast-utils"); | |
9 | ||
10 | //------------------------------------------------------------------------------ | |
11 | // Rule Definition | |
12 | //------------------------------------------------------------------------------ | |
13 | ||
34eeec05 | 14 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
15 | module.exports = { |
16 | meta: { | |
17 | type: "suggestion", | |
18 | ||
19 | docs: { | |
8f9d1d4d | 20 | description: "Disallow certain properties on certain objects", |
eb39fafa | 21 | recommended: false, |
f2a92ac6 | 22 | url: "https://eslint.org/docs/latest/rules/no-restricted-properties" |
eb39fafa DC |
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 | messages: { | |
609c276f | 67 | // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period |
eb39fafa | 68 | restrictedObjectProperty: "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}", |
609c276f | 69 | // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period |
eb39fafa DC |
70 | restrictedProperty: "'{{propertyName}}' is restricted from being used.{{message}}" |
71 | } | |
72 | }, | |
73 | ||
74 | create(context) { | |
75 | const restrictedCalls = context.options; | |
76 | ||
77 | if (restrictedCalls.length === 0) { | |
78 | return {}; | |
79 | } | |
80 | ||
81 | const restrictedProperties = new Map(); | |
82 | const globallyRestrictedObjects = new Map(); | |
83 | const globallyRestrictedProperties = new Map(); | |
84 | ||
85 | restrictedCalls.forEach(option => { | |
86 | const objectName = option.object; | |
87 | const propertyName = option.property; | |
88 | ||
89 | if (typeof objectName === "undefined") { | |
90 | globallyRestrictedProperties.set(propertyName, { message: option.message }); | |
91 | } else if (typeof propertyName === "undefined") { | |
92 | globallyRestrictedObjects.set(objectName, { message: option.message }); | |
93 | } else { | |
94 | if (!restrictedProperties.has(objectName)) { | |
95 | restrictedProperties.set(objectName, new Map()); | |
96 | } | |
97 | ||
98 | restrictedProperties.get(objectName).set(propertyName, { | |
99 | message: option.message | |
100 | }); | |
101 | } | |
102 | }); | |
103 | ||
104 | /** | |
105 | * Checks to see whether a property access is restricted, and reports it if so. | |
106 | * @param {ASTNode} node The node to report | |
107 | * @param {string} objectName The name of the object | |
108 | * @param {string} propertyName The name of the property | |
109 | * @returns {undefined} | |
110 | */ | |
111 | function checkPropertyAccess(node, objectName, propertyName) { | |
112 | if (propertyName === null) { | |
113 | return; | |
114 | } | |
115 | const matchedObject = restrictedProperties.get(objectName); | |
116 | const matchedObjectProperty = matchedObject ? matchedObject.get(propertyName) : globallyRestrictedObjects.get(objectName); | |
117 | const globalMatchedProperty = globallyRestrictedProperties.get(propertyName); | |
118 | ||
119 | if (matchedObjectProperty) { | |
120 | const message = matchedObjectProperty.message ? ` ${matchedObjectProperty.message}` : ""; | |
121 | ||
122 | context.report({ | |
123 | node, | |
124 | messageId: "restrictedObjectProperty", | |
125 | data: { | |
126 | objectName, | |
127 | propertyName, | |
128 | message | |
129 | } | |
130 | }); | |
131 | } else if (globalMatchedProperty) { | |
132 | const message = globalMatchedProperty.message ? ` ${globalMatchedProperty.message}` : ""; | |
133 | ||
134 | context.report({ | |
135 | node, | |
136 | messageId: "restrictedProperty", | |
137 | data: { | |
138 | propertyName, | |
139 | message | |
140 | } | |
141 | }); | |
142 | } | |
143 | } | |
144 | ||
145 | /** | |
146 | * Checks property accesses in a destructuring assignment expression, e.g. `var foo; ({foo} = bar);` | |
147 | * @param {ASTNode} node An AssignmentExpression or AssignmentPattern node | |
148 | * @returns {undefined} | |
149 | */ | |
150 | function checkDestructuringAssignment(node) { | |
151 | if (node.right.type === "Identifier") { | |
152 | const objectName = node.right.name; | |
153 | ||
154 | if (node.left.type === "ObjectPattern") { | |
155 | node.left.properties.forEach(property => { | |
156 | checkPropertyAccess(node.left, objectName, astUtils.getStaticPropertyName(property)); | |
157 | }); | |
158 | } | |
159 | } | |
160 | } | |
161 | ||
162 | return { | |
163 | MemberExpression(node) { | |
164 | checkPropertyAccess(node, node.object && node.object.name, astUtils.getStaticPropertyName(node)); | |
165 | }, | |
166 | VariableDeclarator(node) { | |
167 | if (node.init && node.init.type === "Identifier") { | |
168 | const objectName = node.init.name; | |
169 | ||
170 | if (node.id.type === "ObjectPattern") { | |
171 | node.id.properties.forEach(property => { | |
172 | checkPropertyAccess(node.id, objectName, astUtils.getStaticPropertyName(property)); | |
173 | }); | |
174 | } | |
175 | } | |
176 | }, | |
177 | AssignmentExpression: checkDestructuringAssignment, | |
178 | AssignmentPattern: checkDestructuringAssignment | |
179 | }; | |
180 | } | |
181 | }; |