ESLint Plugin Prefer Arrow provides a configurable ESLint rule that enforces the use of arrow functions over traditional function declarations and expressions in JavaScript code. The plugin supports automatic code fixing when configured with the singleReturnOnly option and integrates seamlessly with ESLint's built-in functionality.
npm install --save-dev eslint-plugin-prefer-arrow// ESLint configuration (CommonJS)
const preferArrowPlugin = require('eslint-plugin-prefer-arrow');
// Or import specific rules
const { rules } = require('eslint-plugin-prefer-arrow');Add the plugin to your ESLint configuration file:
// .eslintrc.js
module.exports = {
plugins: ['prefer-arrow'],
rules: {
'prefer-arrow/prefer-arrow-functions': ['warn', {
disallowPrototype: true,
singleReturnOnly: false,
classPropertiesAllowed: false
}]
}
};Autofix with the --fix flag (requires singleReturnOnly: true):
eslint --fix src/ESLint Plugin Prefer Arrow is built around several key components:
The plugin operates by registering AST visitors for FunctionDeclaration and FunctionExpression nodes, then applying a series of contextual checks to determine if the function should be converted to an arrow function based on the configured options.
The main plugin export provides ESLint with the rule definitions and default configuration.
/**
* Main plugin export containing rule definitions and default configuration
*/
const plugin = {
rules: {
'prefer-arrow-functions': PreferArrowFunctionsRule
},
rulesConfig: {
'prefer-arrow-functions': [2]
}
};The core ESLint rule that detects function declarations and expressions that could be converted to arrow functions.
/**
* ESLint rule implementation for preferring arrow functions
*/
const PreferArrowFunctionsRule = {
meta: RuleMeta,
create: (context: ESLintContext) => RuleVisitor
};
interface RuleMeta {
docs: {
description: string;
category: string;
recommended: boolean;
};
fixable: "code";
schema: RuleOptionsSchema[];
}
interface RuleOptionsSchema {
type: "object";
properties: {
disallowPrototype?: boolean;
singleReturnOnly?: boolean;
classPropertiesAllowed?: boolean;
allowStandaloneDeclarations?: boolean;
};
additionalProperties: false;
}
interface RuleVisitor {
'FunctionDeclaration:exit': (node: ESLintNode) => void;
'FunctionExpression:exit': (node: ESLintNode) => void;
}Usage Example:
// In .eslintrc.js
module.exports = {
plugins: ['prefer-arrow'],
rules: {
'prefer-arrow/prefer-arrow-functions': ['error', {
disallowPrototype: true,
singleReturnOnly: true,
classPropertiesAllowed: false,
allowStandaloneDeclarations: true
}]
}
};The rule accepts a configuration object with the following optional properties:
interface RuleOptions {
/**
* If true, warns about all function usage regardless of context
* If false, allows functions assigned to object prototypes
* @default false
*/
disallowPrototype?: boolean;
/**
* If true, only warns about functions that contain only a return statement
* Enables automatic fixing with eslint --fix
* @default false
*/
singleReturnOnly?: boolean;
/**
* If true, warns about class methods that could be arrow functions
* Useful with Babel's transform-class-properties plugin
* @default false
*/
classPropertiesAllowed?: boolean;
/**
* If true, ignores top-level function declarations
* Still warns about nested functions
* @default false
*/
allowStandaloneDeclarations?: boolean;
}Configuration Examples:
// Conservative setup - only simple functions with auto-fix
{
"prefer-arrow/prefer-arrow-functions": ["warn", {
"singleReturnOnly": true
}]
}
// Aggressive setup - all functions except prototypes
{
"prefer-arrow/prefer-arrow-functions": ["error", {
"disallowPrototype": false,
"singleReturnOnly": false,
"classPropertiesAllowed": true,
"allowStandaloneDeclarations": false
}]
}
// Balanced setup - ignores top-level declarations
{
"prefer-arrow/prefer-arrow-functions": ["warn", {
"disallowPrototype": true,
"singleReturnOnly": true,
"allowStandaloneDeclarations": true
}]
}/**
* ESLint rule context object passed to rule create function
*/
interface ESLintContext {
options: [RuleOptions?];
report: (descriptor: ReportDescriptor) => void;
getSourceCode: () => SourceCode;
}
/**
* ESLint AST node representing function declarations and expressions
*/
interface ESLintNode {
type: 'FunctionDeclaration' | 'FunctionExpression';
id?: { name: string };
params: Parameter[];
body: BlockStatement;
generator: boolean;
async: boolean;
parent: ESLintNode;
range: [number, number];
}
/**
* Function parameter in AST
*/
interface Parameter {
type: string;
name?: string;
range: [number, number];
}
/**
* Block statement containing function body
*/
interface BlockStatement {
type: 'BlockStatement';
body: Statement[];
range: [number, number];
}
/**
* Generic AST statement
*/
interface Statement {
type: string;
range: [number, number];
}
/**
* ESLint source code object
*/
interface SourceCode {
getText: () => string;
getTokens: (node: ESLintNode) => Token[];
getTokenAfter: (token: Token) => Token;
}
/**
* ESLint token
*/
interface Token {
type: string;
value: string;
start?: number;
end?: number;
range: [number, number];
}
/**
* Report descriptor for ESLint violations
*/
interface ReportDescriptor {
node: ESLintNode;
message: string;
fix?: (fixer: RuleFixer) => FixCommand | FixCommand[];
}
/**
* ESLint rule fixer for automatic code fixes
*/
interface RuleFixer {
replaceText: (node: ESLintNode, text: string) => FixCommand;
replaceTextRange: (range: [number, number], text: string) => FixCommand;
}
/**
* Fix command returned by fixer methods
*/
interface FixCommand {
range: [number, number];
text: string;
}The rule includes several helper functions that analyze function nodes to determine conversion eligibility.
/**
* Determines if a function is assigned to an object prototype
* @param node - Function node to analyze
* @returns True if function is a prototype method
*/
function isPrototypeAssignment(node: ESLintNode): boolean;
/**
* Checks if a function is a constructor method
* @param node - Function node to analyze
* @returns True if function is a constructor
*/
function isConstructor(node: ESLintNode): boolean;
/**
* Analyzes if function body or parameters contain 'this' references
* @param node - AST node to search
* @returns True if 'this' is referenced
*/
function containsThis(node: ESLintNode): boolean;
/**
* Checks if function is a named function declaration
* @param node - Function node to analyze
* @returns True if function has a name
*/
function isNamed(node: ESLintNode): boolean;
/**
* Determines if function body contains only a return statement
* @param node - Function node to analyze
* @returns True if function only returns a value
*/
function functionOnlyContainsReturnStatement(node: ESLintNode): boolean;
/**
* Checks if function is a named default export
* @param node - Function node to analyze
* @returns True if function is exported as default with a name
*/
function isNamedDefaultExport(node: ESLintNode): boolean;
/**
* Determines if function is a class method
* @param node - Function node to analyze
* @returns True if function is defined within a class
*/
function isClassMethod(node: ESLintNode): boolean;
/**
* Checks if function is a generator function
* @param node - Function node to analyze
* @returns True if function uses generator syntax
*/
function isGeneratorFunction(node: ESLintNode): boolean;
/**
* Determines if function is a getter or setter method
* @param node - Function node to analyze
* @returns True if function is a property accessor
*/
function isGetterOrSetter(node: ESLintNode): boolean;
/**
* Checks if function is a CommonJS module export
* @param node - Function node to analyze
* @returns True if function is assigned to module.exports or exports
*/
function isModuleExport(node: ESLintNode): boolean;
/**
* Determines if function is a top-level standalone declaration
* @param node - Function node to analyze
* @returns True if function is declared at program or export level
*/
function isStandaloneDeclaration(node: ESLintNode): boolean;Functions responsible for converting function syntax to arrow function syntax during auto-fixing.
/**
* Converts function expressions to arrow function syntax
* @param src - ESLint source code object
* @param node - Function expression node to convert
* @returns Transformed arrow function code
*/
function fixFunctionExpression(src: SourceCode, node: ESLintNode): string;
/**
* Converts function declarations to arrow function variable assignments
* @param src - ESLint source code object
* @param node - Function declaration node to convert
* @returns Transformed arrow function variable declaration
*/
function fixFunctionDeclaration(src: SourceCode, node: ESLintNode): string;
/**
* Replaces tokens in source code with new text
* @param origSource - Original source code string
* @param tokens - Array of tokens to process
* @param replacements - Map of token positions to replacement text
* @returns Modified source code string
*/
function replaceTokens(
origSource: string,
tokens: Token[],
replacements: { [position: number]: [string, boolean?, boolean?] }
): string;
/**
* Creates a token matching function for finding specific tokens
* @param type - Token type to match
* @param value - Optional token value to match
* @returns Function that tests if a token matches criteria
*/
function tokenMatcher(type: string, value?: string): (token: Token) => boolean;The rule produces different error messages based on the detected pattern:
Install as a development dependency:
npm install --save-dev eslint-plugin-prefer-arrowAdd to your ESLint configuration:
// .eslintrc.js
module.exports = {
plugins: ['prefer-arrow'],
rules: {
'prefer-arrow/prefer-arrow-functions': 'warn'
}
};For automatic fixing, enable singleReturnOnly and use the --fix flag:
eslint --fix src/