Advanced static code analysis capabilities including value evaluation and side effect detection. These functions enable ESLint rules to understand code behavior without execution, supporting complex analysis scenarios.
Evaluate the static value of AST nodes when possible, handling constants, literals, and simple expressions.
/**
* Get the value of a given node if it's a static value
* @param node - The node to evaluate
* @param initialScope - Optional scope to start finding variables for identifier resolution
* @returns Static value result or null if not evaluable
*/
function getStaticValue(node: Node, initialScope?: Scope): StaticValue | null;
interface StaticValue {
value: any;
optional?: boolean;
}Usage Examples:
import { getStaticValue } from "eslint-utils";
create(context) {
return {
CallExpression(node) {
// Check if first argument is a static string
if (node.arguments[0]) {
const staticArg = getStaticValue(node.arguments[0], context.getScope());
if (staticArg && typeof staticArg.value === 'string') {
console.log('Static string argument:', staticArg.value);
}
}
},
BinaryExpression(node) {
// Evaluate static binary expressions
const result = getStaticValue(node, context.getScope());
if (result) {
console.log('Binary expression evaluates to:', result.value);
// Warn about always-true conditions
if (node.operator === '===' && result.value === true) {
context.report(node, 'Condition is always true');
}
}
},
Identifier(node) {
// Resolve constant variable values
const value = getStaticValue(node, context.getScope());
if (value) {
console.log(`Variable '${node.name}' has static value:`, value.value);
}
}
};
}Specialized function for extracting string values from literals and template literals.
/**
* Get the value of a given node if it's a literal or template literal
* @param node - The node to evaluate
* @param initialScope - Optional scope for identifier evaluation
* @returns String value or null if not a string constant
*/
function getStringIfConstant(node: Node, initialScope?: Scope): string | null;Usage Examples:
import { getStringIfConstant } from "eslint-utils";
create(context) {
return {
CallExpression(node) {
// Check for specific function calls with string arguments
if (node.callee.name === 'require') {
const moduleName = getStringIfConstant(node.arguments[0]);
if (moduleName) {
console.log('Requiring module:', moduleName);
if (moduleName.startsWith('./')) {
context.report(node, 'Use absolute imports instead of relative');
}
}
}
},
Property(node) {
// Check object property keys
const keyString = getStringIfConstant(node.key);
if (keyString && keyString.includes('-')) {
context.report(node, 'Use camelCase property names');
}
},
TemplateLiteral(node) {
// Evaluate template literals with no expressions
const stringValue = getStringIfConstant(node);
if (stringValue) {
console.log('Template literal evaluates to:', stringValue);
}
}
};
}Analyze nodes for potential side effects, crucial for optimization and code analysis rules.
/**
* Check whether a given node has any side effect or not
* @param node - The node to check
* @param sourceCode - The source code object
* @param options - Optional configuration for side effect detection
* @returns True if the node has potential side effects
*/
function hasSideEffect(node: Node, sourceCode: SourceCode, options?: SideEffectOptions): boolean;
interface SideEffectOptions {
considerGetters?: boolean;
considerImplicitTypeConversion?: boolean;
visitorKeys?: object;
}Usage Examples:
import { hasSideEffect } from "eslint-utils";
create(context) {
const sourceCode = context.getSourceCode();
return {
ExpressionStatement(node) {
// Check for unused expressions with side effects
if (!hasSideEffect(node.expression, sourceCode)) {
context.report(node, 'Unused expression with no side effects');
}
},
ConditionalExpression(node) {
// Warn about side effects in ternary conditions
if (hasSideEffect(node.test, sourceCode)) {
context.report(node.test, 'Avoid side effects in ternary conditions');
}
},
CallExpression(node) {
// Advanced side effect checking with options
const hasEffects = hasSideEffect(node, sourceCode, {
considerGetters: true,
considerImplicitTypeConversion: true
});
if (hasEffects && isInIIFE(node)) {
context.report(node, 'Side effects detected in IIFE');
}
},
MemberExpression(node) {
// Check property access for getter side effects
const hasGetterEffects = hasSideEffect(node, sourceCode, {
considerGetters: true
});
if (hasGetterEffects) {
context.report(node, 'Property access may have side effects');
}
}
};
}Complex scenarios combining static analysis functions:
import { getStaticValue, getStringIfConstant, hasSideEffect } from "eslint-utils";
create(context) {
const sourceCode = context.getSourceCode();
function analyzeExpression(node) {
// Try static evaluation first
const staticValue = getStaticValue(node, context.getScope());
if (staticValue) {
return {
type: 'static',
value: staticValue.value,
hasSideEffects: false
};
}
// Check for string constants
const stringValue = getStringIfConstant(node, context.getScope());
if (stringValue) {
return {
type: 'string',
value: stringValue,
hasSideEffects: false
};
}
// Check for side effects
const hasEffects = hasSideEffect(node, sourceCode, {
considerGetters: true,
considerImplicitTypeConversion: true
});
return {
type: 'dynamic',
value: null,
hasSideEffects: hasEffects
};
}
return {
VariableDeclarator(node) {
if (node.init) {
const analysis = analyzeExpression(node.init);
if (analysis.type === 'static') {
console.log(`Variable '${node.id.name}' initialized with static value:`, analysis.value);
} else if (analysis.hasSideEffects) {
context.report(node, 'Variable initialization has side effects');
}
}
}
};
}The static value evaluation supports:
The side effect detection identifies:
considerGetters is trueconsiderImplicitTypeConversion is true