This change modifies the build scripts to use --legacy-peer-deps flag when installing npm dependencies to resolve potential peer dependency conflicts during the build process. The change is applied to both bash (build.sh) and batch (build.bat) build scripts.
186 lines
5.9 KiB
JavaScript
186 lines
5.9 KiB
JavaScript
/**
|
|
* @fileoverview Rule to disallow loops with a body that allows only one iteration
|
|
* @author Milos Djermanovic
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Helpers
|
|
//------------------------------------------------------------------------------
|
|
|
|
const allLoopTypes = ["WhileStatement", "DoWhileStatement", "ForStatement", "ForInStatement", "ForOfStatement"];
|
|
|
|
/**
|
|
* Checks all segments in a set and returns true if any are reachable.
|
|
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
* @returns {boolean} True if any segment is reachable; false otherwise.
|
|
*/
|
|
function isAnySegmentReachable(segments) {
|
|
|
|
for (const segment of segments) {
|
|
if (segment.reachable) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Determines whether the given node is the first node in the code path to which a loop statement
|
|
* 'loops' for the next iteration.
|
|
* @param {ASTNode} node The node to check.
|
|
* @returns {boolean} `true` if the node is a looping target.
|
|
*/
|
|
function isLoopingTarget(node) {
|
|
const parent = node.parent;
|
|
|
|
if (parent) {
|
|
switch (parent.type) {
|
|
case "WhileStatement":
|
|
return node === parent.test;
|
|
case "DoWhileStatement":
|
|
return node === parent.body;
|
|
case "ForStatement":
|
|
return node === (parent.update || parent.test || parent.body);
|
|
case "ForInStatement":
|
|
case "ForOfStatement":
|
|
return node === parent.left;
|
|
|
|
// no default
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Creates an array with elements from the first given array that are not included in the second given array.
|
|
* @param {Array} arrA The array to compare from.
|
|
* @param {Array} arrB The array to compare against.
|
|
* @returns {Array} a new array that represents `arrA \ arrB`.
|
|
*/
|
|
function getDifference(arrA, arrB) {
|
|
return arrA.filter(a => !arrB.includes(a));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** @type {import('../shared/types').Rule} */
|
|
module.exports = {
|
|
meta: {
|
|
type: "problem",
|
|
|
|
docs: {
|
|
description: "Disallow loops with a body that allows only one iteration",
|
|
recommended: false,
|
|
url: "https://eslint.org/docs/latest/rules/no-unreachable-loop"
|
|
},
|
|
|
|
schema: [{
|
|
type: "object",
|
|
properties: {
|
|
ignore: {
|
|
type: "array",
|
|
items: {
|
|
enum: allLoopTypes
|
|
},
|
|
uniqueItems: true
|
|
}
|
|
},
|
|
additionalProperties: false
|
|
}],
|
|
|
|
messages: {
|
|
invalid: "Invalid loop. Its body allows only one iteration."
|
|
}
|
|
},
|
|
|
|
create(context) {
|
|
const ignoredLoopTypes = context.options[0] && context.options[0].ignore || [],
|
|
loopTypesToCheck = getDifference(allLoopTypes, ignoredLoopTypes),
|
|
loopSelector = loopTypesToCheck.join(","),
|
|
loopsByTargetSegments = new Map(),
|
|
loopsToReport = new Set();
|
|
|
|
const codePathSegments = [];
|
|
let currentCodePathSegments = new Set();
|
|
|
|
return {
|
|
|
|
onCodePathStart() {
|
|
codePathSegments.push(currentCodePathSegments);
|
|
currentCodePathSegments = new Set();
|
|
},
|
|
|
|
onCodePathEnd() {
|
|
currentCodePathSegments = codePathSegments.pop();
|
|
},
|
|
|
|
onUnreachableCodePathSegmentStart(segment) {
|
|
currentCodePathSegments.add(segment);
|
|
},
|
|
|
|
onUnreachableCodePathSegmentEnd(segment) {
|
|
currentCodePathSegments.delete(segment);
|
|
},
|
|
|
|
onCodePathSegmentEnd(segment) {
|
|
currentCodePathSegments.delete(segment);
|
|
},
|
|
|
|
onCodePathSegmentStart(segment, node) {
|
|
|
|
currentCodePathSegments.add(segment);
|
|
|
|
if (isLoopingTarget(node)) {
|
|
const loop = node.parent;
|
|
|
|
loopsByTargetSegments.set(segment, loop);
|
|
}
|
|
},
|
|
|
|
onCodePathSegmentLoop(_, toSegment, node) {
|
|
const loop = loopsByTargetSegments.get(toSegment);
|
|
|
|
/**
|
|
* The second iteration is reachable, meaning that the loop is valid by the logic of this rule,
|
|
* only if there is at least one loop event with the appropriate target (which has been already
|
|
* determined in the `loopsByTargetSegments` map), raised from either:
|
|
*
|
|
* - the end of the loop's body (in which case `node === loop`)
|
|
* - a `continue` statement
|
|
*
|
|
* This condition skips loop events raised from `ForInStatement > .right` and `ForOfStatement > .right` nodes.
|
|
*/
|
|
if (node === loop || node.type === "ContinueStatement") {
|
|
|
|
// Removes loop if it exists in the set. Otherwise, `Set#delete` has no effect and doesn't throw.
|
|
loopsToReport.delete(loop);
|
|
}
|
|
},
|
|
|
|
[loopSelector](node) {
|
|
|
|
/**
|
|
* Ignore unreachable loop statements to avoid unnecessary complexity in the implementation, or false positives otherwise.
|
|
* For unreachable segments, the code path analysis does not raise events required for this implementation.
|
|
*/
|
|
if (isAnySegmentReachable(currentCodePathSegments)) {
|
|
loopsToReport.add(node);
|
|
}
|
|
},
|
|
|
|
|
|
"Program:exit"() {
|
|
loopsToReport.forEach(
|
|
node => context.report({ node, messageId: "invalid" })
|
|
);
|
|
}
|
|
};
|
|
}
|
|
};
|