]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/new-cap.js
2 * @fileoverview Rule to flag use of constructors without capital letters
3 * @author Nicholas C. Zakas
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 const CAPS_ALLOWED
= [
33 * Ensure that if the key is provided, it must be an array.
34 * @param {Object} obj Object to check with `key`.
35 * @param {string} key Object key to check on `obj`.
36 * @param {any} fallback If obj[key] is not present, this will be returned.
37 * @throws {TypeError} If key is not an own array type property of `obj`.
38 * @returns {string[]} Returns obj[key] if it's an Array, otherwise `fallback`
40 function checkArray(obj
, key
, fallback
) {
43 if (Object
.prototype.hasOwnProperty
.call(obj
, key
) && !Array
.isArray(obj
[key
])) {
44 throw new TypeError(`${key}, if provided, must be an Array`);
46 return obj
[key
] || fallback
;
50 * A reducer function to invert an array to an Object mapping the string form of the key, to `true`.
51 * @param {Object} map Accumulator object for the reduce.
52 * @param {string} key Object key to set to `true`.
53 * @returns {Object} Returns the updated Object for further reduction.
55 function invert(map
, key
) {
61 * Creates an object with the cap is new exceptions as its keys and true as their values.
62 * @param {Object} config Rule configuration
63 * @returns {Object} Object with cap is new exceptions.
65 function calculateCapIsNewExceptions(config
) {
66 let capIsNewExceptions
= checkArray(config
, "capIsNewExceptions", CAPS_ALLOWED
);
68 if (capIsNewExceptions
!== CAPS_ALLOWED
) {
69 capIsNewExceptions
= capIsNewExceptions
.concat(CAPS_ALLOWED
);
72 return capIsNewExceptions
.reduce(invert
, {});
75 //------------------------------------------------------------------------------
77 //------------------------------------------------------------------------------
79 /** @type {import('../shared/types').Rule} */
85 description
: "Require constructor names to begin with a capital letter",
87 url
: "https://eslint.org/docs/rules/new-cap"
102 newIsCapExceptions
: {
108 newIsCapExceptionPattern
: {
111 capIsNewExceptions
: {
117 capIsNewExceptionPattern
: {
125 additionalProperties
: false
129 upper
: "A function with a name starting with an uppercase letter should only be used as a constructor.",
130 lower
: "A constructor name should not start with a lowercase letter."
136 const config
= Object
.assign({}, context
.options
[0]);
138 config
.newIsCap
= config
.newIsCap
!== false;
139 config
.capIsNew
= config
.capIsNew
!== false;
140 const skipProperties
= config
.properties
=== false;
142 const newIsCapExceptions
= checkArray(config
, "newIsCapExceptions", []).reduce(invert
, {});
143 const newIsCapExceptionPattern
= config
.newIsCapExceptionPattern
? new RegExp(config
.newIsCapExceptionPattern
, "u") : null;
145 const capIsNewExceptions
= calculateCapIsNewExceptions(config
);
146 const capIsNewExceptionPattern
= config
.capIsNewExceptionPattern
? new RegExp(config
.capIsNewExceptionPattern
, "u") : null;
148 const listeners
= {};
150 const sourceCode
= context
.getSourceCode();
152 //--------------------------------------------------------------------------
154 //--------------------------------------------------------------------------
157 * Get exact callee name from expression
158 * @param {ASTNode} node CallExpression or NewExpression node
159 * @returns {string} name
161 function extractNameFromExpression(node
) {
162 return node
.callee
.type
=== "Identifier"
164 : astUtils
.getStaticPropertyName(node
.callee
) || "";
168 * Returns the capitalization state of the string -
169 * Whether the first character is uppercase, lowercase, or non-alphabetic
170 * @param {string} str String
171 * @returns {string} capitalization state: "non-alpha", "lower", or "upper"
173 function getCap(str
) {
174 const firstChar
= str
.charAt(0);
176 const firstCharLower
= firstChar
.toLowerCase();
177 const firstCharUpper
= firstChar
.toUpperCase();
179 if (firstCharLower
=== firstCharUpper
) {
181 // char has no uppercase variant, so it's non-alphabetic
184 if (firstChar
=== firstCharLower
) {
192 * Check if capitalization is allowed for a CallExpression
193 * @param {Object} allowedMap Object mapping calleeName to a Boolean
194 * @param {ASTNode} node CallExpression node
195 * @param {string} calleeName Capitalized callee name from a CallExpression
196 * @param {Object} pattern RegExp object from options pattern
197 * @returns {boolean} Returns true if the callee may be capitalized
199 function isCapAllowed(allowedMap
, node
, calleeName
, pattern
) {
200 const sourceText
= sourceCode
.getText(node
.callee
);
202 if (allowedMap
[calleeName
] || allowedMap
[sourceText
]) {
206 if (pattern
&& pattern
.test(sourceText
)) {
210 const callee
= astUtils
.skipChainExpression(node
.callee
);
212 if (calleeName
=== "UTC" && callee
.type
=== "MemberExpression") {
214 // allow if callee is Date.UTC
215 return callee
.object
.type
=== "Identifier" &&
216 callee
.object
.name
=== "Date";
219 return skipProperties
&& callee
.type
=== "MemberExpression";
223 * Reports the given messageId for the given node. The location will be the start of the property or the callee.
224 * @param {ASTNode} node CallExpression or NewExpression node.
225 * @param {string} messageId The messageId to report.
228 function report(node
, messageId
) {
229 let callee
= astUtils
.skipChainExpression(node
.callee
);
231 if (callee
.type
=== "MemberExpression") {
232 callee
= callee
.property
;
235 context
.report({ node
, loc
: callee
.loc
, messageId
});
238 //--------------------------------------------------------------------------
240 //--------------------------------------------------------------------------
242 if (config
.newIsCap
) {
243 listeners
.NewExpression = function(node
) {
245 const constructorName
= extractNameFromExpression(node
);
247 if (constructorName
) {
248 const capitalization
= getCap(constructorName
);
249 const isAllowed
= capitalization
!== "lower" || isCapAllowed(newIsCapExceptions
, node
, constructorName
, newIsCapExceptionPattern
);
252 report(node
, "lower");
258 if (config
.capIsNew
) {
259 listeners
.CallExpression = function(node
) {
261 const calleeName
= extractNameFromExpression(node
);
264 const capitalization
= getCap(calleeName
);
265 const isAllowed
= capitalization
!== "upper" || isCapAllowed(capIsNewExceptions
, node
, calleeName
, capIsNewExceptionPattern
);
268 report(node
, "upper");