diff --git a/Makefile.js b/Makefile.js index 44a3c6922ea..bdd76586ed9 100644 --- a/Makefile.js +++ b/Makefile.js @@ -340,7 +340,7 @@ function getFirstCommitOfFile(filePath) { let commits = execSilent(`git rev-list HEAD -- ${filePath}`); commits = splitCommandResultToLines(commits); - return commits[commits.length - 1].trim(); + return commits.at(-1).trim(); } /** diff --git a/lib/linter/apply-disable-directives.js b/lib/linter/apply-disable-directives.js index c83d4b04c9f..f511821dd53 100644 --- a/lib/linter/apply-disable-directives.js +++ b/lib/linter/apply-disable-directives.js @@ -171,7 +171,7 @@ function createCommentRemoval(directives, commentToken) { return { description: ruleIds.length <= 2 ? ruleIds.join(" or ") - : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`, + : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds.at(-1)}`, fix: { range, text: " " @@ -342,7 +342,7 @@ function applyDirectives(options) { problem.suppressions = problem.suppressions.concat(suppressions); } else { problem.suppressions = suppressions; - usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]); + usedDisableDirectives.add(disableDirectivesForProblem.at(-1)); } } diff --git a/lib/linter/code-path-analysis/code-path.js b/lib/linter/code-path-analysis/code-path.js index 90cafef68f0..dd922a2b609 100644 --- a/lib/linter/code-path-analysis/code-path.js +++ b/lib/linter/code-path-analysis/code-path.js @@ -196,7 +196,7 @@ class CodePath { if (stack.length <= 1) { broken = true; } else { - skippedSegment = stack[stack.length - 2][0]; + skippedSegment = stack.at(-2)[0]; } }, @@ -237,7 +237,7 @@ class CodePath { * Otherwise, we just read the value and sometimes modify the * record as we traverse. */ - record = stack[stack.length - 1]; + record = stack.at(-1); segment = record[0]; index = record[1]; diff --git a/lib/linter/code-path-analysis/fork-context.js b/lib/linter/code-path-analysis/fork-context.js index 33140272f53..695d3bfa7e2 100644 --- a/lib/linter/code-path-analysis/fork-context.js +++ b/lib/linter/code-path-analysis/fork-context.js @@ -207,7 +207,7 @@ class ForkContext { get head() { const list = this.segmentsList; - return list.length === 0 ? [] : list[list.length - 1]; + return list.length === 0 ? [] : list.at(-1); } /** diff --git a/lib/linter/report-translator.js b/lib/linter/report-translator.js index 41a43eadc3b..1b3865b4af5 100644 --- a/lib/linter/report-translator.js +++ b/lib/linter/report-translator.js @@ -160,7 +160,7 @@ function mergeFixes(fixes, sourceCode) { const originalText = sourceCode.text; const start = fixes[0].range[0]; - const end = fixes[fixes.length - 1].range[1]; + const end = fixes.at(-1).range[1]; let text = ""; let lastPos = Number.MIN_SAFE_INTEGER; diff --git a/lib/rules/array-bracket-spacing.js b/lib/rules/array-bracket-spacing.js index 9dd3ffd902c..31ace985a20 100644 --- a/lib/rules/array-bracket-spacing.js +++ b/lib/rules/array-bracket-spacing.js @@ -199,7 +199,7 @@ module.exports = { : sourceCode.getLastToken(node), penultimate = sourceCode.getTokenBefore(last), firstElement = node.elements[0], - lastElement = node.elements[node.elements.length - 1]; + lastElement = node.elements.at(-1); const openingBracketMustBeSpaced = options.objectsInArraysException && isObjectType(firstElement) || diff --git a/lib/rules/block-scoped-var.js b/lib/rules/block-scoped-var.js index ec597d5661e..d65fc074bb1 100644 --- a/lib/rules/block-scoped-var.js +++ b/lib/rules/block-scoped-var.js @@ -79,7 +79,7 @@ module.exports = { } // Defines a predicate to check whether or not a given reference is outside of valid scope. - const scopeRange = stack[stack.length - 1]; + const scopeRange = stack.at(-1); /** * Check if a reference is out of scope diff --git a/lib/rules/callback-return.js b/lib/rules/callback-return.js index 5d441bdd9fa..ffc34b163d6 100644 --- a/lib/rules/callback-return.js +++ b/lib/rules/callback-return.js @@ -147,7 +147,7 @@ module.exports = { if (closestBlock.type === "BlockStatement") { // find the last item in the block - const lastItem = closestBlock.body[closestBlock.body.length - 1]; + const lastItem = closestBlock.body.at(-1); // if the callback is the last thing in a block that might be ok if (isCallbackExpression(node, lastItem)) { @@ -168,7 +168,7 @@ module.exports = { if (lastItem.type === "ReturnStatement") { // but only if the callback is immediately before - if (isCallbackExpression(node, closestBlock.body[closestBlock.body.length - 2])) { + if (isCallbackExpression(node, closestBlock.body.at(-2))) { return; } } diff --git a/lib/rules/comma-dangle.js b/lib/rules/comma-dangle.js index 5f4180f12c5..c24c1475444 100644 --- a/lib/rules/comma-dangle.js +++ b/lib/rules/comma-dangle.js @@ -154,7 +154,7 @@ module.exports = { * @returns {any} The last element */ function last(array) { - return array[array.length - 1]; + return array.at(-1); } switch (node.type) { diff --git a/lib/rules/comma-style.js b/lib/rules/comma-style.js index 0b51219531d..83a5af132e9 100644 --- a/lib/rules/comma-style.js +++ b/lib/rules/comma-style.js @@ -218,7 +218,7 @@ module.exports = { previousItemToken = tokenAfterItem ? sourceCode.getTokenBefore(tokenAfterItem) - : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]; + : sourceCode.ast.tokens.at(-1); } else { previousItemToken = currentItemToken; } diff --git a/lib/rules/constructor-super.js b/lib/rules/constructor-super.js index 330be80f386..bab709d4188 100644 --- a/lib/rules/constructor-super.js +++ b/lib/rules/constructor-super.js @@ -109,7 +109,7 @@ function isPossibleConstructor(node) { ); case "SequenceExpression": { - const lastExpression = node.expressions[node.expressions.length - 1]; + const lastExpression = node.expressions.at(-1); return isPossibleConstructor(lastExpression); } diff --git a/lib/rules/default-case.js b/lib/rules/default-case.js index 4f2fad0c4f8..1f83cef3b11 100644 --- a/lib/rules/default-case.js +++ b/lib/rules/default-case.js @@ -54,7 +54,7 @@ module.exports = { * @returns {any} Last element */ function last(collection) { - return collection[collection.length - 1]; + return collection.at(-1); } //-------------------------------------------------------------------------- diff --git a/lib/rules/eol-last.js b/lib/rules/eol-last.js index 03487b039f3..c46ff4eff09 100644 --- a/lib/rules/eol-last.js +++ b/lib/rules/eol-last.js @@ -45,7 +45,7 @@ module.exports = { Program: function checkBadEOF(node) { const sourceCode = context.sourceCode, src = sourceCode.getText(), - lastLine = sourceCode.lines[sourceCode.lines.length - 1], + lastLine = sourceCode.lines.at(-1), location = { column: lastLine.length, line: sourceCode.lines.length @@ -89,7 +89,7 @@ module.exports = { }); } else if (mode === "never" && endsWithNewline) { - const secondLastLine = sourceCode.lines[sourceCode.lines.length - 2]; + const secondLastLine = sourceCode.lines.at(-2); // File is newline-terminated, but shouldn't be context.report({ diff --git a/lib/rules/function-paren-newline.js b/lib/rules/function-paren-newline.js index de315a0204b..86268e52a1d 100644 --- a/lib/rules/function-paren-newline.js +++ b/lib/rules/function-paren-newline.js @@ -218,7 +218,7 @@ module.exports = { case "FunctionExpression": { const leftParen = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken); const rightParen = node.params.length - ? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken) + ? sourceCode.getTokenAfter(node.params.at(-1), astUtils.isClosingParenToken) : sourceCode.getTokenAfter(leftParen); return { leftParen, rightParen }; @@ -234,7 +234,7 @@ module.exports = { } const rightParen = node.params.length - ? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken) + ? sourceCode.getTokenAfter(node.params.at(-1), astUtils.isClosingParenToken) : sourceCode.getTokenAfter(firstToken); return { diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js index 78bf965cb3f..117815aee4a 100644 --- a/lib/rules/indent-legacy.js +++ b/lib/rules/indent-legacy.js @@ -789,7 +789,7 @@ module.exports = { if (elements.length > 0) { // Skip last block line check if last item in same line - if (elements[elements.length - 1].loc.end.line === node.loc.end.line) { + if (elements.at(-1).loc.end.line === node.loc.end.line) { return; } } @@ -873,7 +873,7 @@ module.exports = { */ function filterOutSameLineVars(node) { return node.declarations.reduce((finalCollection, elem) => { - const lastElem = finalCollection[finalCollection.length - 1]; + const lastElem = finalCollection.at(-1); if ((elem.loc.start.line !== node.loc.start.line && !lastElem) || (lastElem && lastElem.loc.start.line !== elem.loc.start.line)) { @@ -892,7 +892,7 @@ module.exports = { function checkIndentInVariableDeclarations(node) { const elements = filterOutSameLineVars(node); const nodeIndent = getNodeIndent(node).goodChar; - const lastElement = elements[elements.length - 1]; + const lastElement = elements.at(-1); const elementsIndent = nodeIndent + indentSize * options.VariableDeclarator[node.kind]; @@ -999,7 +999,7 @@ module.exports = { }, VariableDeclaration(node) { - if (node.declarations[node.declarations.length - 1].loc.start.line > node.declarations[0].loc.start.line) { + if (node.declarations.at(-1).loc.start.line > node.declarations[0].loc.start.line) { checkIndentInVariableDeclarations(node); } }, diff --git a/lib/rules/indent.js b/lib/rules/indent.js index 9bcbd640c4d..de8a857b312 100644 --- a/lib/rules/indent.js +++ b/lib/rules/indent.js @@ -1077,7 +1077,7 @@ module.exports = { "ObjectExpression, ObjectPattern"(node) { const openingCurly = sourceCode.getFirstToken(node); const closingCurly = sourceCode.getTokenAfter( - node.properties.length ? node.properties[node.properties.length - 1] : openingCurly, + node.properties.length ? node.properties.at(-1) : openingCurly, astUtils.isClosingBraceToken ); @@ -1458,7 +1458,7 @@ module.exports = { if (node.cases.length) { sourceCode.getTokensBetween( - node.cases[node.cases.length - 1], + node.cases.at(-1), closingCurly, { includeComments: true, filter: astUtils.isCommentToken } ).forEach(token => offsets.ignoreToken(token)); @@ -1509,7 +1509,7 @@ module.exports = { variableIndent = DEFAULT_VARIABLE_INDENT; } - if (node.declarations[node.declarations.length - 1].loc.start.line > node.loc.start.line) { + if (node.declarations.at(-1).loc.start.line > node.loc.start.line) { /* * VariableDeclarator indentation is a bit different from other forms of indentation, in that the diff --git a/lib/rules/key-spacing.js b/lib/rules/key-spacing.js index 19fc0167ae0..64f61255589 100644 --- a/lib/rules/key-spacing.js +++ b/lib/rules/key-spacing.js @@ -28,7 +28,7 @@ function containsLineTerminator(str) { * @returns {any} Last element of arr. */ function last(arr) { - return arr[arr.length - 1]; + return arr.at(-1); } /** diff --git a/lib/rules/lines-around-directive.js b/lib/rules/lines-around-directive.js index 1c82d8f98d6..fb6871d3e8a 100644 --- a/lib/rules/lines-around-directive.js +++ b/lib/rules/lines-around-directive.js @@ -166,7 +166,7 @@ module.exports = { reportError(firstDirective, "before", false); } - const lastDirective = directives[directives.length - 1]; + const lastDirective = directives.at(-1); const statements = node.type === "Program" ? node.body : node.body.body; /* @@ -174,7 +174,7 @@ module.exports = { * contains a directive prologue and isn't followed by a comment to ensure * this rule behaves well with padded-blocks. */ - if (lastDirective === statements[statements.length - 1] && !lastDirective.trailingComments) { + if (lastDirective === statements.at(-1) && !lastDirective.trailingComments) { return; } diff --git a/lib/rules/max-len.js b/lib/rules/max-len.js index 138a0f239fd..e9ab5e685f1 100644 --- a/lib/rules/max-len.js +++ b/lib/rules/max-len.js @@ -124,7 +124,7 @@ module.exports = { } // The options object must be the last option specified… - const options = Object.assign({}, context.options[context.options.length - 1]); + const options = Object.assign({}, context.options.at(-1)); // …but max code length… if (typeof context.options[0] === "number") { @@ -290,7 +290,7 @@ module.exports = { if (isJSXEmptyExpressionInSingleLineContainer(containingNode)) { // push a unique node only - if (comments[comments.length - 1] !== containingNode.parent) { + if (comments.at(-1) !== containingNode.parent) { comments.push(containingNode.parent); } } else { diff --git a/lib/rules/max-lines.js b/lib/rules/max-lines.js index e85d4429081..b7c57e9bb82 100644 --- a/lib/rules/max-lines.js +++ b/lib/rules/max-lines.js @@ -148,7 +148,7 @@ module.exports = { * If file ends with a linebreak, `sourceCode.lines` will have one extra empty line at the end. * That isn't a real line, so we shouldn't count it. */ - if (lines.length > 1 && lines[lines.length - 1].text === "") { + if (lines.length > 1 && lines.at(-1).text === "") { lines.pop(); } @@ -174,7 +174,7 @@ module.exports = { }, end: { line: sourceCode.lines.length, - column: sourceCode.lines[sourceCode.lines.length - 1].length + column: sourceCode.lines.at(-1).length } }; diff --git a/lib/rules/multiline-comment-style.js b/lib/rules/multiline-comment-style.js index 6da9862015c..19caa8384af 100644 --- a/lib/rules/multiline-comment-style.js +++ b/lib/rules/multiline-comment-style.js @@ -113,7 +113,7 @@ module.exports = { return /^\*\s*$/u.test(lines[0]) && lines.slice(1, -1).every(line => /^\s* /u.test(line)) && - /^\s*$/u.test(lines[lines.length - 1]); + /^\s*$/u.test(lines.at(-1)); } /** @@ -272,11 +272,11 @@ module.exports = { context.report({ loc: { start: firstComment.loc.start, - end: commentGroup[commentGroup.length - 1].loc.end + end: commentGroup.at(-1).loc.end }, messageId: "expectedBlock", fix(fixer) { - const range = [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]]; + const range = [firstComment.range[0], commentGroup.at(-1).range[1]]; return commentLines.some(value => value.startsWith("/")) ? null @@ -301,7 +301,7 @@ module.exports = { }); } - if (!/^\s*$/u.test(lines[lines.length - 1])) { + if (!/^\s*$/u.test(lines.at(-1))) { context.report({ loc: { start: { line: firstComment.loc.end.line, column: firstComment.loc.end.column - 2 }, @@ -408,12 +408,12 @@ module.exports = { context.report({ loc: { start: firstComment.loc.start, - end: commentGroup[commentGroup.length - 1].loc.end + end: commentGroup.at(-1).loc.end }, messageId: "expectedBlock", fix(fixer) { return fixer.replaceTextRange( - [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]], + [firstComment.range[0], commentGroup.at(-1).range[1]], convertToBlock(firstComment, commentLines) ); } @@ -459,7 +459,7 @@ module.exports = { tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 && tokenBefore === commentList[index - 1] ) { - commentGroups[commentGroups.length - 1].push(comment); + commentGroups.at(-1).push(comment); } else { commentGroups.push([comment]); } diff --git a/lib/rules/newline-after-var.js b/lib/rules/newline-after-var.js index dc8b24d4738..6e4476412d9 100644 --- a/lib/rules/newline-after-var.js +++ b/lib/rules/newline-after-var.js @@ -215,7 +215,7 @@ module.exports = { fix(fixer) { const linesBetween = sourceCode.getText().slice(lastToken.range[1], nextToken.range[0]).split(astUtils.LINEBREAK_MATCHER); - return fixer.replaceTextRange([lastToken.range[1], nextToken.range[0]], `${linesBetween.slice(0, -1).join("")}\n${linesBetween[linesBetween.length - 1]}`); + return fixer.replaceTextRange([lastToken.range[1], nextToken.range[0]], `${linesBetween.slice(0, -1).join("")}\n${linesBetween.at(-1)}`); } }); } diff --git a/lib/rules/newline-before-return.js b/lib/rules/newline-before-return.js index 73d8ef99ff3..21e9faf1492 100644 --- a/lib/rules/newline-before-return.js +++ b/lib/rules/newline-before-return.js @@ -166,7 +166,7 @@ module.exports = { */ function canFix(node) { const leadingComments = sourceCode.getCommentsBefore(node); - const lastLeadingComment = leadingComments[leadingComments.length - 1]; + const lastLeadingComment = leadingComments.at(-1); const tokenBefore = sourceCode.getTokenBefore(node); if (leadingComments.length === 0) { diff --git a/lib/rules/no-constant-binary-expression.js b/lib/rules/no-constant-binary-expression.js index 1c0e39fdcbc..239dd54157c 100644 --- a/lib/rules/no-constant-binary-expression.js +++ b/lib/rules/no-constant-binary-expression.js @@ -103,7 +103,7 @@ function hasConstantNullishness(scope, node, nonNullish) { return true; case "SequenceExpression": { - const last = node.expressions[node.expressions.length - 1]; + const last = node.expressions.at(-1); return hasConstantNullishness(scope, last, nonNullish); } @@ -248,7 +248,7 @@ function hasConstantLooseBooleanComparison(scope, node) { */ return false; case "SequenceExpression": { - const last = node.expressions[node.expressions.length - 1]; + const last = node.expressions.at(-1); return hasConstantLooseBooleanComparison(scope, last); } @@ -299,7 +299,7 @@ function hasConstantStrictBooleanComparison(scope, node) { return true; } case "SequenceExpression": { - const last = node.expressions[node.expressions.length - 1]; + const last = node.expressions.at(-1); return hasConstantStrictBooleanComparison(scope, last); } @@ -384,7 +384,7 @@ function isAlwaysNew(scope, node) { // Regular expressions are objects, and thus always new return typeof node.regex === "object"; case "SequenceExpression": { - const last = node.expressions[node.expressions.length - 1]; + const last = node.expressions.at(-1); return isAlwaysNew(scope, last); } diff --git a/lib/rules/no-constructor-return.js b/lib/rules/no-constructor-return.js index 075ec918571..ccce10ad636 100644 --- a/lib/rules/no-constructor-return.js +++ b/lib/rules/no-constructor-return.js @@ -40,7 +40,7 @@ module.exports = { stack.pop(); }, ReturnStatement(node) { - const last = stack[stack.length - 1]; + const last = stack.at(-1); if (!last.parent) { return; diff --git a/lib/rules/no-dupe-class-members.js b/lib/rules/no-dupe-class-members.js index 2a7a9e810dc..f35a2b3089e 100644 --- a/lib/rules/no-dupe-class-members.js +++ b/lib/rules/no-dupe-class-members.js @@ -42,7 +42,7 @@ module.exports = { * - retv.set {boolean} A flag which shows the name is declared as setter. */ function getState(name, isStatic) { - const stateMap = stack[stack.length - 1]; + const stateMap = stack.at(-1); const key = `$${name}`; // to avoid "__proto__". if (!stateMap[key]) { diff --git a/lib/rules/no-else-return.js b/lib/rules/no-else-return.js index 9dbf569651c..6e6bf476dd8 100644 --- a/lib/rules/no-else-return.js +++ b/lib/rules/no-else-return.js @@ -270,7 +270,7 @@ module.exports = { function naiveHasReturn(node) { if (node.type === "BlockStatement") { const body = node.body, - lastChildNode = body[body.length - 1]; + lastChildNode = body.at(-1); return lastChildNode && checkForReturn(lastChildNode); } diff --git a/lib/rules/no-fallthrough.js b/lib/rules/no-fallthrough.js index 91da1212022..03b5f6f7ce6 100644 --- a/lib/rules/no-fallthrough.js +++ b/lib/rules/no-fallthrough.js @@ -187,7 +187,7 @@ module.exports = { */ if (isAnySegmentReachable(currentCodePathSegments) && (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) && - node.parent.cases[node.parent.cases.length - 1] !== node) { + node.parent.cases.at(-1) !== node) { fallthroughCase = node; } } diff --git a/lib/rules/no-invalid-this.js b/lib/rules/no-invalid-this.js index 9e214035c33..4ea22889852 100644 --- a/lib/rules/no-invalid-this.js +++ b/lib/rules/no-invalid-this.js @@ -74,7 +74,7 @@ module.exports = { * an object which has a flag that whether or not `this` keyword is valid. */ stack.getCurrent = function() { - const current = this[this.length - 1]; + const current = this.at(-1); if (!current.init) { current.init = true; diff --git a/lib/rules/no-lone-blocks.js b/lib/rules/no-lone-blocks.js index 767eec2becf..51f30f351f1 100644 --- a/lib/rules/no-lone-blocks.js +++ b/lib/rules/no-lone-blocks.js @@ -78,7 +78,7 @@ module.exports = { const block = node.parent; - if (loneBlocks[loneBlocks.length - 1] === block) { + if (loneBlocks.at(-1) === block) { loneBlocks.pop(); } } @@ -101,7 +101,7 @@ module.exports = { } }, "BlockStatement:exit"(node) { - if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) { + if (loneBlocks.length > 0 && loneBlocks.at(-1) === node) { loneBlocks.pop(); report(node); } else if ( diff --git a/lib/rules/no-multiple-empty-lines.js b/lib/rules/no-multiple-empty-lines.js index 5d038ff05b2..b597138e8f4 100644 --- a/lib/rules/no-multiple-empty-lines.js +++ b/lib/rules/no-multiple-empty-lines.js @@ -70,7 +70,7 @@ module.exports = { const sourceCode = context.sourceCode; // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue - const allLines = sourceCode.lines[sourceCode.lines.length - 1] === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines; + const allLines = sourceCode.lines.at(-1) === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines; const templateLiteralLines = new Set(); //-------------------------------------------------------------------------- diff --git a/lib/rules/no-return-await.js b/lib/rules/no-return-await.js index 77abda0cadf..0c297411621 100644 --- a/lib/rules/no-return-await.js +++ b/lib/rules/no-return-await.js @@ -118,7 +118,7 @@ module.exports = { if (node.parent.type === "LogicalExpression" && node === node.parent.right) { return isInTailCallPosition(node.parent); } - if (node.parent.type === "SequenceExpression" && node === node.parent.expressions[node.parent.expressions.length - 1]) { + if (node.parent.type === "SequenceExpression" && node === node.parent.expressions.at(-1)) { return isInTailCallPosition(node.parent); } return false; diff --git a/lib/rules/no-unsafe-optional-chaining.js b/lib/rules/no-unsafe-optional-chaining.js index fe2bead856e..fa787939649 100644 --- a/lib/rules/no-unsafe-optional-chaining.js +++ b/lib/rules/no-unsafe-optional-chaining.js @@ -94,7 +94,7 @@ module.exports = { break; case "SequenceExpression": checkUndefinedShortCircuit( - node.expressions[node.expressions.length - 1], + node.expressions.at(-1), reportFunc ); break; diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js index f29e678d6fd..2db309c06c4 100644 --- a/lib/rules/no-unused-vars.js +++ b/lib/rules/no-unused-vars.js @@ -218,7 +218,7 @@ module.exports = { function hasRestSibling(node) { return node.type === "Property" && node.parent.type === "ObjectPattern" && - REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type); + REST_PROPERTY_TYPE.test(node.parent.properties.at(-1).type); } /** @@ -323,7 +323,7 @@ module.exports = { } if (parent.type === "SequenceExpression") { - const isLastExpression = parent.expressions[parent.expressions.length - 1] === node; + const isLastExpression = parent.expressions.at(-1) === node; if (!isLastExpression) { return true; @@ -392,7 +392,7 @@ module.exports = { while (parent && isInside(parent, rhsNode)) { switch (parent.type) { case "SequenceExpression": - if (parent.expressions[parent.expressions.length - 1] !== node) { + if (parent.expressions.at(-1) !== node) { return false; } break; @@ -688,7 +688,7 @@ module.exports = { let referenceToReport; if (writeReferences.length > 0) { - referenceToReport = writeReferences[writeReferences.length - 1]; + referenceToReport = writeReferences.at(-1); } context.report({ diff --git a/lib/rules/no-useless-backreference.js b/lib/rules/no-useless-backreference.js index 7ca43c8b260..2a54de2e3b8 100644 --- a/lib/rules/no-useless-backreference.js +++ b/lib/rules/no-useless-backreference.js @@ -138,7 +138,7 @@ module.exports = { // the opposite of the previous when the regex is matching backward in a lookbehind context. messageId = "backward"; - } else if (groupCut[groupCut.length - 1].type === "Alternative") { + } else if (groupCut.at(-1).type === "Alternative") { // group's and bref's ancestor nodes below the lowest common ancestor are sibling alternatives => they're disjunctive. messageId = "disjunctive"; diff --git a/lib/rules/object-curly-spacing.js b/lib/rules/object-curly-spacing.js index 4463bcd5af1..6cb0b0f9313 100644 --- a/lib/rules/object-curly-spacing.js +++ b/lib/rules/object-curly-spacing.js @@ -217,7 +217,7 @@ module.exports = { * @returns {Token} '}' token. */ function getClosingBraceOfObject(node) { - const lastProperty = node.properties[node.properties.length - 1]; + const lastProperty = node.properties.at(-1); return sourceCode.getTokenAfter(lastProperty, astUtils.isClosingBraceToken); } @@ -251,7 +251,7 @@ module.exports = { } let firstSpecifier = node.specifiers[0]; - const lastSpecifier = node.specifiers[node.specifiers.length - 1]; + const lastSpecifier = node.specifiers.at(-1); if (lastSpecifier.type !== "ImportSpecifier") { return; @@ -279,7 +279,7 @@ module.exports = { } const firstSpecifier = node.specifiers[0], - lastSpecifier = node.specifiers[node.specifiers.length - 1], + lastSpecifier = node.specifiers.at(-1), first = sourceCode.getTokenBefore(firstSpecifier), last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken), second = sourceCode.getTokenAfter(first, { includeComments: true }), diff --git a/lib/rules/object-property-newline.js b/lib/rules/object-property-newline.js index 6ffa06421f0..8a08676cfa1 100644 --- a/lib/rules/object-property-newline.js +++ b/lib/rules/object-property-newline.js @@ -63,7 +63,7 @@ module.exports = { if (allowSameLine) { if (node.properties.length > 1) { const firstTokenOfFirstProperty = sourceCode.getFirstToken(node.properties[0]); - const lastTokenOfLastProperty = sourceCode.getLastToken(node.properties[node.properties.length - 1]); + const lastTokenOfLastProperty = sourceCode.getLastToken(node.properties.at(-1)); if (firstTokenOfFirstProperty.loc.end.line === lastTokenOfLastProperty.loc.start.line) { diff --git a/lib/rules/one-var.js b/lib/rules/one-var.js index abb1525b1a5..29fd6253d51 100644 --- a/lib/rules/one-var.js +++ b/lib/rules/one-var.js @@ -216,11 +216,11 @@ module.exports = { let currentScope; if (statementType === "var") { - currentScope = functionStack[functionStack.length - 1]; + currentScope = functionStack.at(-1); } else if (statementType === "let") { - currentScope = blockStack[blockStack.length - 1].let; + currentScope = blockStack.at(-1).let; } else if (statementType === "const") { - currentScope = blockStack[blockStack.length - 1].const; + currentScope = blockStack.at(-1).const; } return currentScope; } diff --git a/lib/rules/prefer-arrow-callback.js b/lib/rules/prefer-arrow-callback.js index d22e508beb0..b23696dd64c 100644 --- a/lib/rules/prefer-arrow-callback.js +++ b/lib/rules/prefer-arrow-callback.js @@ -220,7 +220,7 @@ module.exports = { // If there are below, it cannot replace with arrow functions merely. ThisExpression() { - const info = stack[stack.length - 1]; + const info = stack.at(-1); if (info) { info.this = true; @@ -228,7 +228,7 @@ module.exports = { }, Super() { - const info = stack[stack.length - 1]; + const info = stack.at(-1); if (info) { info.super = true; @@ -236,7 +236,7 @@ module.exports = { }, MetaProperty(node) { - const info = stack[stack.length - 1]; + const info = stack.at(-1); if (info && checkMetaProperty(node, "new", "target")) { info.meta = true; diff --git a/lib/rules/prefer-template.js b/lib/rules/prefer-template.js index a2c8c724135..d7d70c50640 100644 --- a/lib/rules/prefer-template.js +++ b/lib/rules/prefer-template.js @@ -113,7 +113,7 @@ function endsWithTemplateCurly(node) { return startsWithTemplateCurly(node.right); } if (node.type === "TemplateLiteral") { - return node.expressions.length && node.quasis.length && node.quasis[node.quasis.length - 1].range[0] === node.quasis[node.quasis.length - 1].range[1]; + return node.expressions.length && node.quasis.length && node.quasis.at(-1).range[0] === node.quasis.at(-1).range[1]; } return node.type !== "Literal" || typeof node.value !== "string"; } diff --git a/lib/rules/radix.js b/lib/rules/radix.js index 7df97d98602..efae749690a 100644 --- a/lib/rules/radix.js +++ b/lib/rules/radix.js @@ -133,8 +133,8 @@ module.exports = { messageId: "addRadixParameter10", fix(fixer) { const tokens = sourceCode.getTokens(node); - const lastToken = tokens[tokens.length - 1]; // Parenthesis. - const secondToLastToken = tokens[tokens.length - 2]; // May or may not be a comma. + const lastToken = tokens.at(-1); // Parenthesis. + const secondToLastToken = tokens.at(-2); // May or may not be a comma. const hasTrailingComma = secondToLastToken.type === "Punctuator" && secondToLastToken.value === ","; return fixer.insertTextBefore(lastToken, hasTrailingComma ? " 10," : ", 10"); diff --git a/lib/rules/semi-style.js b/lib/rules/semi-style.js index caf2224df3d..11caaf0b945 100644 --- a/lib/rules/semi-style.js +++ b/lib/rules/semi-style.js @@ -65,7 +65,7 @@ function isLastChild(node) { } const nodeList = getChildren(node.parent); - return nodeList !== null && nodeList[nodeList.length - 1] === node; // before `}` or etc. + return nodeList !== null && nodeList.at(-1) === node; // before `}` or etc. } /** @type {import('../shared/types').Rule} */ diff --git a/lib/rules/sort-imports.js b/lib/rules/sort-imports.js index 04814ed6fed..9deaf1d4c97 100644 --- a/lib/rules/sort-imports.js +++ b/lib/rules/sort-imports.js @@ -208,7 +208,7 @@ module.exports = { } return fixer.replaceTextRange( - [importSpecifiers[0].range[0], importSpecifiers[importSpecifiers.length - 1].range[1]], + [importSpecifiers[0].range[0], importSpecifiers.at(-1).range[1]], importSpecifiers // Clone the importSpecifiers array to avoid mutating it diff --git a/lib/rules/sort-keys.js b/lib/rules/sort-keys.js index 088b5890f30..e355e8afdc8 100644 --- a/lib/rules/sort-keys.js +++ b/lib/rules/sort-keys.js @@ -185,7 +185,7 @@ module.exports = { }); // check blank line between the current node and the last token - if (!isBlankLineBetweenNodes && (node.loc.start.line - tokens[tokens.length - 1].loc.end.line > 1)) { + if (!isBlankLineBetweenNodes && (node.loc.start.line - tokens.at(-1).loc.end.line > 1)) { isBlankLineBetweenNodes = true; } diff --git a/lib/rules/sort-vars.js b/lib/rules/sort-vars.js index 8fd723fd4e5..21bfb88e8dd 100644 --- a/lib/rules/sort-vars.js +++ b/lib/rules/sort-vars.js @@ -66,7 +66,7 @@ module.exports = { return null; } return fixer.replaceTextRange( - [idDeclarations[0].range[0], idDeclarations[idDeclarations.length - 1].range[1]], + [idDeclarations[0].range[0], idDeclarations.at(-1).range[1]], idDeclarations // Clone the idDeclarations array to avoid mutating it diff --git a/lib/rules/strict.js b/lib/rules/strict.js index f9dd7500be3..fd970f279e1 100644 --- a/lib/rules/strict.js +++ b/lib/rules/strict.js @@ -173,7 +173,7 @@ module.exports = { function enterFunctionInFunctionMode(node, useStrictDirectives) { const isInClass = classScopes.length > 0, isParentGlobal = scopes.length === 0 && classScopes.length === 0, - isParentStrict = scopes.length > 0 && scopes[scopes.length - 1], + isParentStrict = scopes.length > 0 && scopes.at(-1), isStrict = useStrictDirectives.length > 0; if (isStrict) { diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js index 962bdde0af1..1e5237df8b6 100644 --- a/lib/rules/utils/ast-utils.js +++ b/lib/rules/utils/ast-utils.js @@ -969,7 +969,7 @@ function isConstant(scope, node, inBooleanPosition) { return false; case "SequenceExpression": - return isConstant(scope, node.expressions[node.expressions.length - 1], inBooleanPosition); + return isConstant(scope, node.expressions.at(-1), inBooleanPosition); case "SpreadElement": return isConstant(scope, node.argument, inBooleanPosition); case "CallExpression": @@ -1231,7 +1231,7 @@ module.exports = { * @private */ isSurroundedBy(val, character) { - return val[0] === character && val[val.length - 1] === character; + return val[0] === character && val.at(-1) === character; }, /** @@ -2055,7 +2055,7 @@ module.exports = { case "SequenceExpression": { const exprs = node.expressions; - return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]); + return exprs.length !== 0 && module.exports.couldBeError(exprs.at(-1)); } case "LogicalExpression": @@ -2119,9 +2119,9 @@ module.exports = { const comments = tokens.comments; - leftToken = tokens[tokens.length - 1]; + leftToken = tokens.at(-1); if (comments.length) { - const lastComment = comments[comments.length - 1]; + const lastComment = comments.at(-1); if (!leftToken || lastComment.range[0] > leftToken.range[0]) { leftToken = lastComment; diff --git a/lib/source-code/source-code.js b/lib/source-code/source-code.js index c4f51bc3139..31dec21cf49 100644 --- a/lib/source-code/source-code.js +++ b/lib/source-code/source-code.js @@ -415,10 +415,10 @@ class SourceCode extends TokenStore { * and uses match.index to get the correct line start indices. */ while ((match = lineEndingPattern.exec(this.text))) { - this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index)); + this.lines.push(this.text.slice(this.lineStartIndices.at(-1), match.index)); this.lineStartIndices.push(match.index + match[0].length); } - this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1])); + this.lines.push(this.text.slice(this.lineStartIndices.at(-1))); // don't allow further modification of this object Object.freeze(this); @@ -623,14 +623,14 @@ class SourceCode extends TokenStore { * See getIndexFromLoc for the motivation for this special case. */ if (index === this.text.length) { - return { line: this.lines.length, column: this.lines[this.lines.length - 1].length }; + return { line: this.lines.length, column: this.lines.at(-1).length }; } /* * To figure out which line index is on, determine the last place at which index could * be inserted into lineStartIndices to keep the list sorted. */ - const lineNumber = index >= this.lineStartIndices[this.lineStartIndices.length - 1] + const lineNumber = index >= this.lineStartIndices.at(-1) ? this.lineStartIndices.length : this.lineStartIndices.findIndex(el => index < el); diff --git a/packages/eslint-config-eslint/base.js b/packages/eslint-config-eslint/base.js index 153194153ba..b4346e37bf0 100644 --- a/packages/eslint-config-eslint/base.js +++ b/packages/eslint-config-eslint/base.js @@ -362,6 +362,7 @@ const unicornConfigs = [{ "unicorn/prefer-array-flat": "error", "unicorn/prefer-array-index-of": "error", "unicorn/prefer-array-some": "error", + "unicorn/prefer-at": "error", "unicorn/prefer-includes": "error", "unicorn/prefer-set-has": "error", "unicorn/prefer-string-slice": "error", diff --git a/packages/eslint-config-eslint/eslintrc.js b/packages/eslint-config-eslint/eslintrc.js index 3d61b761160..4a69bbe851d 100644 --- a/packages/eslint-config-eslint/eslintrc.js +++ b/packages/eslint-config-eslint/eslintrc.js @@ -438,6 +438,7 @@ module.exports = { "unicorn/prefer-array-flat": "error", "unicorn/prefer-array-index-of": "error", "unicorn/prefer-array-some": "error", + "unicorn/prefer-at": "error", "unicorn/prefer-includes": "error", "unicorn/prefer-set-has": "error", "unicorn/prefer-string-slice": "error", diff --git a/tests/lib/eslint/legacy-eslint.js b/tests/lib/eslint/legacy-eslint.js index ffd0eaea8e9..b972a48f5f1 100644 --- a/tests/lib/eslint/legacy-eslint.js +++ b/tests/lib/eslint/legacy-eslint.js @@ -88,7 +88,7 @@ describe("LegacyESLint", () => { * @returns {void} */ function callLastArgument(...args) { - process.nextTick(args[args.length - 1], null); + process.nextTick(args.at(-1), null); } // copy into clean area so as not to get "infected" by this project's .eslintrc files diff --git a/tests/lib/source-code/source-code.js b/tests/lib/source-code/source-code.js index d70bbcf3c83..555acfadca7 100644 --- a/tests/lib/source-code/source-code.js +++ b/tests/lib/source-code/source-code.js @@ -1255,7 +1255,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( sourceCode.ast.tokens[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1] + sourceCode.ast.tokens.at(-1) ), expected ); @@ -1269,7 +1269,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1], + sourceCode.ast.tokens.at(-1), sourceCode.ast.tokens[0] ), expected @@ -1319,7 +1319,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( sourceCode.ast.tokens[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2] + sourceCode.ast.tokens.at(-2) ), expected ); @@ -1333,7 +1333,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2], + sourceCode.ast.tokens.at(-2), sourceCode.ast.tokens[0] ), expected @@ -1381,7 +1381,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( sourceCode.ast.tokens[0], - sourceCode.ast.body[sourceCode.ast.body.length - 1] + sourceCode.ast.body.at(-1) ), expected ); @@ -1395,7 +1395,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( - sourceCode.ast.body[sourceCode.ast.body.length - 1], + sourceCode.ast.body.at(-1), sourceCode.ast.tokens[0] ), expected @@ -1443,7 +1443,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( sourceCode.ast.body[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1] + sourceCode.ast.tokens.at(-1) ), expected ); @@ -1457,7 +1457,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1], + sourceCode.ast.tokens.at(-1), sourceCode.ast.body[0] ), expected @@ -1503,7 +1503,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( sourceCode.ast.body[0], - sourceCode.ast.body[sourceCode.ast.body.length - 1] + sourceCode.ast.body.at(-1) ), expected ); @@ -1517,7 +1517,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( - sourceCode.ast.body[sourceCode.ast.body.length - 1], + sourceCode.ast.body.at(-1), sourceCode.ast.body[0] ), expected @@ -1608,7 +1608,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1], + sourceCode.ast.tokens.at(-1), sourceCode.ast.body[0] ), expected @@ -1625,7 +1625,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetween( sourceCode.ast.body[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1] + sourceCode.ast.tokens.at(-1) ), expected ); @@ -1651,7 +1651,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( sourceCode.ast.tokens[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1] + sourceCode.ast.tokens.at(-1) ), expected ); @@ -1665,7 +1665,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1], + sourceCode.ast.tokens.at(-1), sourceCode.ast.tokens[0] ), expected @@ -1715,7 +1715,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( sourceCode.ast.tokens[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2] + sourceCode.ast.tokens.at(-2) ), expected ); @@ -1729,7 +1729,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2], + sourceCode.ast.tokens.at(-2), sourceCode.ast.tokens[0] ), expected @@ -1777,7 +1777,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( sourceCode.ast.tokens[0], - sourceCode.ast.body[sourceCode.ast.body.length - 1] + sourceCode.ast.body.at(-1) ), expected ); @@ -1791,7 +1791,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( - sourceCode.ast.body[sourceCode.ast.body.length - 1], + sourceCode.ast.body.at(-1), sourceCode.ast.tokens[0] ), expected @@ -1839,7 +1839,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( sourceCode.ast.body[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1] + sourceCode.ast.tokens.at(-1) ), expected ); @@ -1853,7 +1853,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1], + sourceCode.ast.tokens.at(-1), sourceCode.ast.body[0] ), expected @@ -1899,7 +1899,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( sourceCode.ast.body[0], - sourceCode.ast.body[sourceCode.ast.body.length - 1] + sourceCode.ast.body.at(-1) ), expected ); @@ -1913,7 +1913,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( - sourceCode.ast.body[sourceCode.ast.body.length - 1], + sourceCode.ast.body.at(-1), sourceCode.ast.body[0] ), expected @@ -2004,7 +2004,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1], + sourceCode.ast.tokens.at(-1), sourceCode.ast.body[0] ), expected @@ -2021,7 +2021,7 @@ describe("SourceCode", () => { assert.strictEqual( sourceCode.isSpaceBetweenTokens( sourceCode.ast.body[0], - sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1] + sourceCode.ast.tokens.at(-1) ), expected );