]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-magic-numbers.js
510b3f9b26172ec7617d4a0312bc32bc2251707e
2 * @fileoverview Rule to flag statements that use magic numbers (adapted from https://github.com/danielstjules/buddy.js)
3 * @author Vincent Lemeunier
8 const astUtils
= require("./utils/ast-utils");
10 // Maximum array length by the ECMAScript Specification.
11 const MAX_ARRAY_LENGTH
= 2 ** 32 - 1;
13 //------------------------------------------------------------------------------
15 //------------------------------------------------------------------------------
18 * Convert the value to bigint if it's a string. Otherwise return the value as-is.
19 * @param {bigint|number|string} x The value to normalize.
20 * @returns {bigint|number} The normalized value.
22 function normalizeIgnoreValue(x
) {
23 if (typeof x
=== "string") {
24 return BigInt(x
.slice(0, -1));
34 description
: "disallow magic numbers",
35 category
: "Best Practices",
37 url
: "https://eslint.org/docs/rules/no-magic-numbers"
56 { type
: "string", pattern
: "^[+-]?(?:0|[1-9][0-9]*)n$" }
65 ignoreDefaultValues
: {
70 additionalProperties
: false
74 useConst
: "Number constants declarations must use 'const'.",
75 noMagic
: "No magic number: {{raw}}."
80 const config
= context
.options
[0] || {},
81 detectObjects
= !!config
.detectObjects
,
82 enforceConst
= !!config
.enforceConst
,
83 ignore
= (config
.ignore
|| []).map(normalizeIgnoreValue
),
84 ignoreArrayIndexes
= !!config
.ignoreArrayIndexes
,
85 ignoreDefaultValues
= !!config
.ignoreDefaultValues
;
87 const okTypes
= detectObjects
? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
90 * Returns whether the rule is configured to ignore the given value
91 * @param {bigint|number} value The value to check
92 * @returns {boolean} true if the value is ignored
94 function isIgnoredValue(value
) {
95 return ignore
.indexOf(value
) !== -1;
99 * Returns whether the number is a default value assignment.
100 * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
101 * @returns {boolean} true if the number is a default value
103 function isDefaultValue(fullNumberNode
) {
104 const parent
= fullNumberNode
.parent
;
106 return parent
.type
=== "AssignmentPattern" && parent
.right
=== fullNumberNode
;
110 * Returns whether the given node is used as a radix within parseInt() or Number.parseInt()
111 * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
112 * @returns {boolean} true if the node is radix
114 function isParseIntRadix(fullNumberNode
) {
115 const parent
= fullNumberNode
.parent
;
117 return parent
.type
=== "CallExpression" && fullNumberNode
=== parent
.arguments
[1] &&
119 astUtils
.isSpecificId(parent
.callee
, "parseInt") ||
120 astUtils
.isSpecificMemberAccess(parent
.callee
, "Number", "parseInt")
125 * Returns whether the given node is a direct child of a JSX node.
126 * In particular, it aims to detect numbers used as prop values in JSX tags.
127 * Example: <input maxLength={10} />
128 * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
129 * @returns {boolean} true if the node is a JSX number
131 function isJSXNumber(fullNumberNode
) {
132 return fullNumberNode
.parent
.type
.indexOf("JSX") === 0;
136 * Returns whether the given node is used as an array index.
137 * Value must coerce to a valid array index name: "0", "1", "2" ... "4294967294".
139 * All other values, like "-1", "2.5", or "4294967295", are just "normal" object properties,
140 * which can be created and accessed on an array in addition to the array index properties,
141 * but they don't affect array's length and are not considered by methods such as .map(), .forEach() etc.
143 * The maximum array length by the specification is 2 ** 32 - 1 = 4294967295,
144 * thus the maximum valid index is 2 ** 32 - 2 = 4294967294.
146 * All notations are allowed, as long as the value coerces to one of "0", "1", "2" ... "4294967294".
149 * a[0], a[1], a[1.2e1], a[0xAB], a[0n], a[1n]
150 * a[-0] (same as a[0] because -0 coerces to "0")
151 * a[-0n] (-0n evaluates to 0n)
154 * a[-1], a[-0xAB], a[-1n], a[2.5], a[1.23e1], a[12e-1]
155 * a[4294967295] (above the max index, it's an access to a regular property a["4294967295"])
156 * a[999999999999999999999] (even if it wasn't above the max index, it would be a["1e+21"])
157 * a[1e310] (same as a["Infinity"])
158 * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
159 * @param {bigint|number} value Value expressed by the fullNumberNode
160 * @returns {boolean} true if the node is a valid array index
162 function isArrayIndex(fullNumberNode
, value
) {
163 const parent
= fullNumberNode
.parent
;
165 return parent
.type
=== "MemberExpression" && parent
.property
=== fullNumberNode
&&
166 (Number
.isInteger(value
) || typeof value
=== "bigint") &&
167 value
>= 0 && value
< MAX_ARRAY_LENGTH
;
172 if (!astUtils
.isNumericLiteral(node
)) {
180 // Treat unary minus as a part of the number
181 if (node
.parent
.type
=== "UnaryExpression" && node
.parent
.operator
=== "-") {
182 fullNumberNode
= node
.parent
;
184 raw
= `-${node.raw}`;
186 fullNumberNode
= node
;
191 const parent
= fullNumberNode
.parent
;
193 // Always allow radix arguments and JSX props
195 isIgnoredValue(value
) ||
196 (ignoreDefaultValues
&& isDefaultValue(fullNumberNode
)) ||
197 isParseIntRadix(fullNumberNode
) ||
198 isJSXNumber(fullNumberNode
) ||
199 (ignoreArrayIndexes
&& isArrayIndex(fullNumberNode
, value
))
204 if (parent
.type
=== "VariableDeclarator") {
205 if (enforceConst
&& parent
.parent
.kind
!== "const") {
207 node
: fullNumberNode
,
208 messageId
: "useConst"
212 okTypes
.indexOf(parent
.type
) === -1 ||
213 (parent
.type
=== "AssignmentExpression" && parent
.left
.type
=== "Identifier")
216 node
: fullNumberNode
,
217 messageId
: "noMagic",