build: update npm install command to use --legacy-peer-deps

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.
This commit is contained in:
Luiz Costa committed 2025-11-18 10:23:55 -03:00
1 parent 20129a1ac7
commit ac4f232e5e
787 files changed
+82341 -121690

No files matched your search

+343 -527
View File
@@ -22,16 +22,14 @@ const baseTypes = new Set(["Identifier", "Super", "ThisExpression"]);
* @returns {boolean} True iff "undefined" or "void ..."
*/
function isUndefined(expression, scope) {
if (expression.type === "Identifier" && expression.name === "undefined") {
return astUtils.isReferenceToGlobalVariable(scope, expression);
}
if (expression.type === "Identifier" && expression.name === "undefined") {
return astUtils.isReferenceToGlobalVariable(scope, expression);
}
return (
expression.type === "UnaryExpression" &&
expression.operator === "void" &&
expression.argument.type === "Literal" &&
expression.argument.value === 0
);
return expression.type === "UnaryExpression" &&
expression.operator === "void" &&
expression.argument.type === "Literal" &&
expression.argument.value === 0;
}
/**
@@ -40,10 +38,8 @@ function isUndefined(expression, scope) {
* @returns {boolean} True for identifiers and member expressions
*/
function isReference(expression) {
return (
(expression.type === "Identifier" && expression.name !== "undefined") ||
expression.type === "MemberExpression"
);
return (expression.type === "Identifier" && expression.name !== "undefined") ||
expression.type === "MemberExpression";
}
/**
@@ -54,21 +50,15 @@ function isReference(expression) {
* @returns {boolean} True iff implicit nullish comparison
*/
function isImplicitNullishComparison(expression, scope) {
if (
expression.type !== "BinaryExpression" ||
expression.operator !== "=="
) {
return false;
}
if (expression.type !== "BinaryExpression" || expression.operator !== "==") {
return false;
}
const reference = isReference(expression.left) ? "left" : "right";
const nullish = reference === "left" ? "right" : "left";
const reference = isReference(expression.left) ? "left" : "right";
const nullish = reference === "left" ? "right" : "left";
return (
isReference(expression[reference]) &&
(astUtils.isNullLiteral(expression[nullish]) ||
isUndefined(expression[nullish], scope))
);
return isReference(expression[reference]) &&
(astUtils.isNullLiteral(expression[nullish]) || isUndefined(expression[nullish], scope));
}
/**
@@ -77,14 +67,12 @@ function isImplicitNullishComparison(expression, scope) {
* @returns {boolean} True iff matches ? === ? || ? === ?
*/
function isDoubleComparison(expression) {
return (
expression.type === "LogicalExpression" &&
expression.operator === "||" &&
expression.left.type === "BinaryExpression" &&
expression.left.operator === "===" &&
expression.right.type === "BinaryExpression" &&
expression.right.operator === "==="
);
return expression.type === "LogicalExpression" &&
expression.operator === "||" &&
expression.left.type === "BinaryExpression" &&
expression.left.operator === "===" &&
expression.right.type === "BinaryExpression" &&
expression.right.operator === "===";
}
/**
@@ -95,26 +83,17 @@ function isDoubleComparison(expression) {
* @returns {boolean} True iff explicit nullish comparison
*/
function isExplicitNullishComparison(expression, scope) {
if (!isDoubleComparison(expression)) {
return false;
}
const leftReference = isReference(expression.left.left) ? "left" : "right";
const leftNullish = leftReference === "left" ? "right" : "left";
const rightReference = isReference(expression.right.left)
? "left"
: "right";
const rightNullish = rightReference === "left" ? "right" : "left";
if (!isDoubleComparison(expression)) {
return false;
}
const leftReference = isReference(expression.left.left) ? "left" : "right";
const leftNullish = leftReference === "left" ? "right" : "left";
const rightReference = isReference(expression.right.left) ? "left" : "right";
const rightNullish = rightReference === "left" ? "right" : "left";
return (
astUtils.isSameReference(
expression.left[leftReference],
expression.right[rightReference],
) &&
((astUtils.isNullLiteral(expression.left[leftNullish]) &&
isUndefined(expression.right[rightNullish], scope)) ||
(isUndefined(expression.left[leftNullish], scope) &&
astUtils.isNullLiteral(expression.right[rightNullish])))
);
return astUtils.isSameReference(expression.left[leftReference], expression.right[rightReference]) &&
((astUtils.isNullLiteral(expression.left[leftNullish]) && isUndefined(expression.right[rightNullish], scope)) ||
(isUndefined(expression.left[leftNullish], scope) && astUtils.isNullLiteral(expression.right[rightNullish])));
}
/**
@@ -124,12 +103,10 @@ function isExplicitNullishComparison(expression, scope) {
* @returns {boolean} Whether the expression is a boolean cast
*/
function isBooleanCast(expression, scope) {
return (
expression.type === "CallExpression" &&
expression.callee.name === "Boolean" &&
expression.arguments.length === 1 &&
astUtils.isReferenceToGlobalVariable(scope, expression.callee)
);
return expression.type === "CallExpression" &&
expression.callee.name === "Boolean" &&
expression.arguments.length === 1 &&
astUtils.isReferenceToGlobalVariable(scope, expression.callee);
}
/**
@@ -142,39 +119,22 @@ function isBooleanCast(expression, scope) {
* @returns {?{ reference: ASTNode, operator: '??'|'||'|'&&'}} Null if not a known existence
*/
function getExistence(expression, scope) {
const isNegated =
expression.type === "UnaryExpression" && expression.operator === "!";
const base = isNegated ? expression.argument : expression;
const isNegated = expression.type === "UnaryExpression" && expression.operator === "!";
const base = isNegated ? expression.argument : expression;
switch (true) {
case isReference(base):
return { reference: base, operator: isNegated ? "||" : "&&" };
case base.type === "UnaryExpression" &&
base.operator === "!" &&
isReference(base.argument):
return { reference: base.argument, operator: "&&" };
case isBooleanCast(base, scope) && isReference(base.arguments[0]):
return {
reference: base.arguments[0],
operator: isNegated ? "||" : "&&",
};
case isImplicitNullishComparison(expression, scope):
return {
reference: isReference(expression.left)
? expression.left
: expression.right,
operator: "??",
};
case isExplicitNullishComparison(expression, scope):
return {
reference: isReference(expression.left.left)
? expression.left.left
: expression.left.right,
operator: "??",
};
default:
return null;
}
switch (true) {
case isReference(base):
return { reference: base, operator: isNegated ? "||" : "&&" };
case base.type === "UnaryExpression" && base.operator === "!" && isReference(base.argument):
return { reference: base.argument, operator: "&&" };
case isBooleanCast(base, scope) && isReference(base.arguments[0]):
return { reference: base.arguments[0], operator: isNegated ? "||" : "&&" };
case isImplicitNullishComparison(expression, scope):
return { reference: isReference(expression.left) ? expression.left : expression.right, operator: "??" };
case isExplicitNullishComparison(expression, scope):
return { reference: isReference(expression.left.left) ? expression.left.left : expression.left.right, operator: "??" };
default: return null;
}
}
/**
@@ -183,13 +143,11 @@ function getExistence(expression, scope) {
* @returns {boolean} True iff passed node is inside a with block
*/
function isInsideWithBlock(node) {
if (node.type === "Program") {
return false;
}
if (node.type === "Program") {
return false;
}
return node.parent.type === "WithStatement" && node.parent.body === node
? true
: isInsideWithBlock(node.parent);
return node.parent.type === "WithStatement" && node.parent.body === node ? true : isInsideWithBlock(node.parent);
}
/**
@@ -199,490 +157,348 @@ function isInsideWithBlock(node) {
* @returns {Expression} Leftmost operand
*/
function getLeftmostOperand(sourceCode, node) {
let left = node.left;
let left = node.left;
while (left.type === "LogicalExpression" && left.operator === node.operator) {
if (astUtils.isParenthesised(sourceCode, left)) {
/*
* It should have associativity,
* but ignore it if use parentheses to make the evaluation order clear.
*/
return left;
}
left = left.left;
}
return left;
while (
left.type === "LogicalExpression" &&
left.operator === node.operator
) {
if (astUtils.isParenthesised(sourceCode, left)) {
/*
* It should have associativity,
* but ignore it if use parentheses to make the evaluation order clear.
*/
return left;
}
left = left.left;
}
return left;
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../types').Rule.RuleModule} */
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
type: "suggestion",
meta: {
type: "suggestion",
docs: {
description:
"Require or disallow logical assignment operator shorthand",
recommended: false,
frozen: true,
url: "https://eslint.org/docs/latest/rules/logical-assignment-operators",
},
docs: {
description: "Require or disallow logical assignment operator shorthand",
recommended: false,
url: "https://eslint.org/docs/latest/rules/logical-assignment-operators"
},
schema: {
type: "array",
oneOf: [
{
items: [
{ const: "always" },
{
type: "object",
properties: {
enforceForIfStatements: {
type: "boolean",
},
},
additionalProperties: false,
},
],
minItems: 0, // 0 for allowing passing no options
maxItems: 2,
},
{
items: [{ const: "never" }],
minItems: 1,
maxItems: 1,
},
],
},
fixable: "code",
hasSuggestions: true,
messages: {
assignment:
"Assignment (=) can be replaced with operator assignment ({{operator}}).",
useLogicalOperator:
"Convert this assignment to use the operator {{ operator }}.",
logical:
"Logical expression can be replaced with an assignment ({{ operator }}).",
convertLogical:
"Replace this logical expression with an assignment with the operator {{ operator }}.",
if: "'if' statement can be replaced with a logical operator assignment with operator {{ operator }}.",
convertIf:
"Replace this 'if' statement with a logical assignment with operator {{ operator }}.",
unexpected:
"Unexpected logical operator assignment ({{operator}}) shorthand.",
separate:
"Separate the logical assignment into an assignment with a logical operator.",
},
},
schema: {
type: "array",
oneOf: [{
items: [
{ const: "always" },
{
type: "object",
properties: {
enforceForIfStatements: {
type: "boolean"
}
},
additionalProperties: false
}
],
minItems: 0, // 0 for allowing passing no options
maxItems: 2
}, {
items: [{ const: "never" }],
minItems: 1,
maxItems: 1
}]
},
fixable: "code",
hasSuggestions: true,
messages: {
assignment: "Assignment (=) can be replaced with operator assignment ({{operator}}).",
useLogicalOperator: "Convert this assignment to use the operator {{ operator }}.",
logical: "Logical expression can be replaced with an assignment ({{ operator }}).",
convertLogical: "Replace this logical expression with an assignment with the operator {{ operator }}.",
if: "'if' statement can be replaced with a logical operator assignment with operator {{ operator }}.",
convertIf: "Replace this 'if' statement with a logical assignment with operator {{ operator }}.",
unexpected: "Unexpected logical operator assignment ({{operator}}) shorthand.",
separate: "Separate the logical assignment into an assignment with a logical operator."
}
},
create(context) {
const mode = context.options[0] === "never" ? "never" : "always";
const checkIf =
mode === "always" &&
context.options.length > 1 &&
context.options[1].enforceForIfStatements;
const sourceCode = context.sourceCode;
const isStrict = sourceCode.getScope(sourceCode.ast).isStrict;
create(context) {
const mode = context.options[0] === "never" ? "never" : "always";
const checkIf = mode === "always" && context.options.length > 1 && context.options[1].enforceForIfStatements;
const sourceCode = context.sourceCode;
const isStrict = sourceCode.getScope(sourceCode.ast).isStrict;
/**
* Returns false if the access could be a getter
* @param {ASTNode} node Assignment expression
* @returns {boolean} True iff the fix is safe
*/
function cannotBeGetter(node) {
return (
node.type === "Identifier" &&
(isStrict || !isInsideWithBlock(node))
);
}
/**
* Returns false if the access could be a getter
* @param {ASTNode} node Assignment expression
* @returns {boolean} True iff the fix is safe
*/
function cannotBeGetter(node) {
return node.type === "Identifier" &&
(isStrict || !isInsideWithBlock(node));
}
/**
* Check whether only a single property is accessed
* @param {ASTNode} node reference
* @returns {boolean} True iff a single property is accessed
*/
function accessesSingleProperty(node) {
if (!isStrict && isInsideWithBlock(node)) {
return node.type === "Identifier";
}
/**
* Check whether only a single property is accessed
* @param {ASTNode} node reference
* @returns {boolean} True iff a single property is accessed
*/
function accessesSingleProperty(node) {
if (!isStrict && isInsideWithBlock(node)) {
return node.type === "Identifier";
}
return (
node.type === "MemberExpression" &&
baseTypes.has(node.object.type) &&
(!node.computed ||
(node.property.type !== "MemberExpression" &&
node.property.type !== "ChainExpression"))
);
}
return node.type === "MemberExpression" &&
baseTypes.has(node.object.type) &&
(!node.computed || (node.property.type !== "MemberExpression" && node.property.type !== "ChainExpression"));
}
/**
* Adds a fixer or suggestion whether on the fix is safe.
* @param {{ messageId: string, node: ASTNode }} descriptor Report descriptor without fix or suggest
* @param {{ messageId: string, fix: Function }} suggestion Adds the fix or the whole suggestion as only element in "suggest" to suggestion
* @param {boolean} shouldBeFixed Fix iff the condition is true
* @returns {Object} Descriptor with either an added fix or suggestion
*/
function createConditionalFixer(descriptor, suggestion, shouldBeFixed) {
if (shouldBeFixed) {
return {
...descriptor,
fix: suggestion.fix,
};
}
/**
* Adds a fixer or suggestion whether on the fix is safe.
* @param {{ messageId: string, node: ASTNode }} descriptor Report descriptor without fix or suggest
* @param {{ messageId: string, fix: Function }} suggestion Adds the fix or the whole suggestion as only element in "suggest" to suggestion
* @param {boolean} shouldBeFixed Fix iff the condition is true
* @returns {Object} Descriptor with either an added fix or suggestion
*/
function createConditionalFixer(descriptor, suggestion, shouldBeFixed) {
if (shouldBeFixed) {
return {
...descriptor,
fix: suggestion.fix
};
}
return {
...descriptor,
suggest: [suggestion],
};
}
return {
...descriptor,
suggest: [suggestion]
};
}
/**
* Returns the operator token for assignments and binary expressions
* @param {ASTNode} node AssignmentExpression or BinaryExpression
* @returns {import('eslint').AST.Token} Operator token between the left and right expression
*/
function getOperatorToken(node) {
return sourceCode.getFirstTokenBetween(
node.left,
node.right,
token => token.value === node.operator,
);
}
if (mode === "never") {
return {
// foo ||= bar
AssignmentExpression(assignment) {
if (
!astUtils.isLogicalAssignmentOperator(
assignment.operator,
)
) {
return;
}
/**
* Returns the operator token for assignments and binary expressions
* @param {ASTNode} node AssignmentExpression or BinaryExpression
* @returns {import('eslint').AST.Token} Operator token between the left and right expression
*/
function getOperatorToken(node) {
return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
}
const descriptor = {
messageId: "unexpected",
node: assignment,
data: { operator: assignment.operator },
};
const suggestion = {
messageId: "separate",
*fix(ruleFixer) {
if (
sourceCode.getCommentsInside(assignment)
.length > 0
) {
return;
}
if (mode === "never") {
return {
const operatorToken = getOperatorToken(assignment);
// foo ||= bar
"AssignmentExpression"(assignment) {
if (!astUtils.isLogicalAssignmentOperator(assignment.operator)) {
return;
}
// -> foo = bar
yield ruleFixer.replaceText(operatorToken, "=");
const descriptor = {
messageId: "unexpected",
node: assignment,
data: { operator: assignment.operator }
};
const suggestion = {
messageId: "separate",
*fix(ruleFixer) {
if (sourceCode.getCommentsInside(assignment).length > 0) {
return;
}
const assignmentText = sourceCode.getText(
assignment.left,
);
const operator = assignment.operator.slice(0, -1);
const operatorToken = getOperatorToken(assignment);
// -> foo = foo || bar
yield ruleFixer.insertTextAfter(
operatorToken,
` ${assignmentText} ${operator}`,
);
// -> foo = bar
yield ruleFixer.replaceText(operatorToken, "=");
const precedence =
astUtils.getPrecedence(assignment.right) <=
astUtils.getPrecedence({
type: "LogicalExpression",
operator,
});
const assignmentText = sourceCode.getText(assignment.left);
const operator = assignment.operator.slice(0, -1);
// ?? and || / && cannot be mixed but have same precedence
const mixed =
assignment.operator === "??=" &&
astUtils.isLogicalExpression(assignment.right);
// -> foo = foo || bar
yield ruleFixer.insertTextAfter(operatorToken, ` ${assignmentText} ${operator}`);
if (
!astUtils.isParenthesised(
sourceCode,
assignment.right,
) &&
(precedence || mixed)
) {
// -> foo = foo || (bar)
yield ruleFixer.insertTextBefore(
assignment.right,
"(",
);
yield ruleFixer.insertTextAfter(
assignment.right,
")",
);
}
},
};
const precedence = astUtils.getPrecedence(assignment.right) <= astUtils.getPrecedence({ type: "LogicalExpression", operator });
context.report(
createConditionalFixer(
descriptor,
suggestion,
cannotBeGetter(assignment.left),
),
);
},
};
}
// ?? and || / && cannot be mixed but have same precedence
const mixed = assignment.operator === "??=" && astUtils.isLogicalExpression(assignment.right);
return {
// foo = foo || bar
"AssignmentExpression[operator='='][right.type='LogicalExpression']"(
assignment,
) {
const leftOperand = getLeftmostOperand(
sourceCode,
assignment.right,
);
if (!astUtils.isParenthesised(sourceCode, assignment.right) && (precedence || mixed)) {
if (!astUtils.isSameReference(assignment.left, leftOperand)) {
return;
}
// -> foo = foo || (bar)
yield ruleFixer.insertTextBefore(assignment.right, "(");
yield ruleFixer.insertTextAfter(assignment.right, ")");
}
}
};
const descriptor = {
messageId: "assignment",
node: assignment,
data: { operator: `${assignment.right.operator}=` },
};
const suggestion = {
messageId: "useLogicalOperator",
data: { operator: `${assignment.right.operator}=` },
*fix(ruleFixer) {
if (
sourceCode.getCommentsInside(assignment).length > 0
) {
return;
}
context.report(createConditionalFixer(descriptor, suggestion, cannotBeGetter(assignment.left)));
}
};
}
// No need for parenthesis around the assignment based on precedence as the precedence stays the same even with changed operator
const assignmentOperatorToken =
getOperatorToken(assignment);
return {
// -> foo ||= foo || bar
yield ruleFixer.insertTextBefore(
assignmentOperatorToken,
assignment.right.operator,
);
// foo = foo || bar
"AssignmentExpression[operator='='][right.type='LogicalExpression']"(assignment) {
const leftOperand = getLeftmostOperand(sourceCode, assignment.right);
// -> foo ||= bar
const logicalOperatorToken = getOperatorToken(
leftOperand.parent,
);
const firstRightOperandToken =
sourceCode.getTokenAfter(logicalOperatorToken);
if (!astUtils.isSameReference(assignment.left, leftOperand)
) {
return;
}
yield ruleFixer.removeRange([
leftOperand.parent.range[0],
firstRightOperandToken.range[0],
]);
},
};
const descriptor = {
messageId: "assignment",
node: assignment,
data: { operator: `${assignment.right.operator}=` }
};
const suggestion = {
messageId: "useLogicalOperator",
data: { operator: `${assignment.right.operator}=` },
*fix(ruleFixer) {
if (sourceCode.getCommentsInside(assignment).length > 0) {
return;
}
context.report(
createConditionalFixer(
descriptor,
suggestion,
cannotBeGetter(assignment.left),
),
);
},
// No need for parenthesis around the assignment based on precedence as the precedence stays the same even with changed operator
const assignmentOperatorToken = getOperatorToken(assignment);
// foo || (foo = bar)
'LogicalExpression[right.type="AssignmentExpression"][right.operator="="]'(
logical,
) {
// Right side has to be parenthesized, otherwise would be parsed as (foo || foo) = bar which is illegal
if (
isReference(logical.left) &&
astUtils.isSameReference(logical.left, logical.right.left)
) {
const descriptor = {
messageId: "logical",
node: logical,
data: { operator: `${logical.operator}=` },
};
const suggestion = {
messageId: "convertLogical",
data: { operator: `${logical.operator}=` },
*fix(ruleFixer) {
if (
sourceCode.getCommentsInside(logical).length > 0
) {
return;
}
// -> foo ||= foo || bar
yield ruleFixer.insertTextBefore(assignmentOperatorToken, assignment.right.operator);
const parentPrecedence = astUtils.getPrecedence(
logical.parent,
);
const requiresOuterParenthesis =
logical.parent.type !== "ExpressionStatement" &&
(parentPrecedence === -1 ||
astUtils.getPrecedence({
type: "AssignmentExpression",
}) < parentPrecedence);
// -> foo ||= bar
const logicalOperatorToken = getOperatorToken(leftOperand.parent);
const firstRightOperandToken = sourceCode.getTokenAfter(logicalOperatorToken);
if (
!astUtils.isParenthesised(
sourceCode,
logical,
) &&
requiresOuterParenthesis
) {
yield ruleFixer.insertTextBefore(logical, "(");
yield ruleFixer.insertTextAfter(logical, ")");
}
yield ruleFixer.removeRange([leftOperand.parent.range[0], firstRightOperandToken.range[0]]);
}
};
// Also removes all opening parenthesis
yield ruleFixer.removeRange([
logical.range[0],
logical.right.range[0],
]); // -> foo = bar)
context.report(createConditionalFixer(descriptor, suggestion, cannotBeGetter(assignment.left)));
},
// Also removes all ending parenthesis
yield ruleFixer.removeRange([
logical.right.range[1],
logical.range[1],
]); // -> foo = bar
// foo || (foo = bar)
'LogicalExpression[right.type="AssignmentExpression"][right.operator="="]'(logical) {
const operatorToken = getOperatorToken(
logical.right,
);
// Right side has to be parenthesized, otherwise would be parsed as (foo || foo) = bar which is illegal
if (isReference(logical.left) && astUtils.isSameReference(logical.left, logical.right.left)) {
const descriptor = {
messageId: "logical",
node: logical,
data: { operator: `${logical.operator}=` }
};
const suggestion = {
messageId: "convertLogical",
data: { operator: `${logical.operator}=` },
*fix(ruleFixer) {
if (sourceCode.getCommentsInside(logical).length > 0) {
return;
}
yield ruleFixer.insertTextBefore(
operatorToken,
logical.operator,
); // -> foo ||= bar
},
};
const fix =
cannotBeGetter(logical.left) ||
accessesSingleProperty(logical.left);
const parentPrecedence = astUtils.getPrecedence(logical.parent);
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && (
parentPrecedence === -1 ||
astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence
);
context.report(
createConditionalFixer(descriptor, suggestion, fix),
);
}
},
if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {
yield ruleFixer.insertTextBefore(logical, "(");
yield ruleFixer.insertTextAfter(logical, ")");
}
// if (foo) foo = bar
"IfStatement[alternate=null]"(ifNode) {
if (!checkIf) {
return;
}
// Also removes all opening parenthesis
yield ruleFixer.removeRange([logical.range[0], logical.right.range[0]]); // -> foo = bar)
const hasBody = ifNode.consequent.type === "BlockStatement";
// Also removes all ending parenthesis
yield ruleFixer.removeRange([logical.right.range[1], logical.range[1]]); // -> foo = bar
if (hasBody && ifNode.consequent.body.length !== 1) {
return;
}
const operatorToken = getOperatorToken(logical.right);
const body = hasBody
? ifNode.consequent.body[0]
: ifNode.consequent;
const scope = sourceCode.getScope(ifNode);
const existence = getExistence(ifNode.test, scope);
yield ruleFixer.insertTextBefore(operatorToken, logical.operator); // -> foo ||= bar
}
};
const fix = cannotBeGetter(logical.left) || accessesSingleProperty(logical.left);
if (
body.type === "ExpressionStatement" &&
body.expression.type === "AssignmentExpression" &&
body.expression.operator === "=" &&
existence !== null &&
astUtils.isSameReference(
existence.reference,
body.expression.left,
)
) {
const descriptor = {
messageId: "if",
node: ifNode,
data: { operator: `${existence.operator}=` },
};
const suggestion = {
messageId: "convertIf",
data: { operator: `${existence.operator}=` },
*fix(ruleFixer) {
if (
sourceCode.getCommentsInside(ifNode).length > 0
) {
return;
}
context.report(createConditionalFixer(descriptor, suggestion, fix));
}
},
const firstBodyToken =
sourceCode.getFirstToken(body);
const prevToken = sourceCode.getTokenBefore(ifNode);
// if (foo) foo = bar
"IfStatement[alternate=null]"(ifNode) {
if (!checkIf) {
return;
}
if (
prevToken !== null &&
prevToken.value !== ";" &&
prevToken.value !== "{" &&
firstBodyToken.type !== "Identifier" &&
firstBodyToken.type !== "Keyword"
) {
// Do not fix if the fixed statement could be part of the previous statement (eg. fn() if (a == null) (a) = b --> fn()(a) ??= b)
return;
}
const hasBody = ifNode.consequent.type === "BlockStatement";
const operatorToken = getOperatorToken(
body.expression,
);
if (hasBody && ifNode.consequent.body.length !== 1) {
return;
}
yield ruleFixer.insertTextBefore(
operatorToken,
existence.operator,
); // -> if (foo) foo ||= bar
const body = hasBody ? ifNode.consequent.body[0] : ifNode.consequent;
const scope = sourceCode.getScope(ifNode);
const existence = getExistence(ifNode.test, scope);
yield ruleFixer.removeRange([
ifNode.range[0],
body.range[0],
]); // -> foo ||= bar
if (
body.type === "ExpressionStatement" &&
body.expression.type === "AssignmentExpression" &&
body.expression.operator === "=" &&
existence !== null &&
astUtils.isSameReference(existence.reference, body.expression.left)
) {
const descriptor = {
messageId: "if",
node: ifNode,
data: { operator: `${existence.operator}=` }
};
const suggestion = {
messageId: "convertIf",
data: { operator: `${existence.operator}=` },
*fix(ruleFixer) {
if (sourceCode.getCommentsInside(ifNode).length > 0) {
return;
}
yield ruleFixer.removeRange([
body.range[1],
ifNode.range[1],
]); // -> foo ||= bar, only present if "if" had a body
const firstBodyToken = sourceCode.getFirstToken(body);
const prevToken = sourceCode.getTokenBefore(ifNode);
const nextToken = sourceCode.getTokenAfter(
body.expression,
);
if (
prevToken !== null &&
prevToken.value !== ";" &&
prevToken.value !== "{" &&
firstBodyToken.type !== "Identifier" &&
firstBodyToken.type !== "Keyword"
) {
if (
hasBody &&
nextToken !== null &&
nextToken.value !== ";"
) {
yield ruleFixer.insertTextAfter(ifNode, ";");
}
},
};
const shouldBeFixed =
cannotBeGetter(existence.reference) ||
(ifNode.test.type !== "LogicalExpression" &&
accessesSingleProperty(existence.reference));
// Do not fix if the fixed statement could be part of the previous statement (eg. fn() if (a == null) (a) = b --> fn()(a) ??= b)
return;
}
context.report(
createConditionalFixer(
descriptor,
suggestion,
shouldBeFixed,
),
);
}
},
};
},
const operatorToken = getOperatorToken(body.expression);
yield ruleFixer.insertTextBefore(operatorToken, existence.operator); // -> if (foo) foo ||= bar
yield ruleFixer.removeRange([ifNode.range[0], body.range[0]]); // -> foo ||= bar
yield ruleFixer.removeRange([body.range[1], ifNode.range[1]]); // -> foo ||= bar, only present if "if" had a body
const nextToken = sourceCode.getTokenAfter(body.expression);
if (hasBody && (nextToken !== null && nextToken.value !== ";")) {
yield ruleFixer.insertTextAfter(ifNode, ";");
}
}
};
const shouldBeFixed = cannotBeGetter(existence.reference) ||
(ifNode.test.type !== "LogicalExpression" && accessesSingleProperty(existence.reference));
context.report(createConditionalFixer(descriptor, suggestion, shouldBeFixed));
}
}
};
}
};