Skip to content

Commit 5996ef4

Browse files
committed
chore: Update named-functions rule to enforce named function declarations
1 parent 6c39fc2 commit 5996ef4

File tree

4 files changed

+40
-36
lines changed

4 files changed

+40
-36
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This repository contains custom ESLint rules to enhance code quality and consist
2020

2121
| Name              | Description | 💼 | 🔧 |
2222
| :--------------------------------------------------- | :------------------------------------------------------------------------------- | :---------------------------------- | :- |
23-
| [named-functions](docs/rules/named-functions.md) | Enforce top-level functions to be named functions |![badge-recommended-typescript][] | 🔧 |
23+
| [named-functions](docs/rules/named-functions.md) | Enforce top-level functions to be named function declarations |![badge-recommended-typescript][] | 🔧 |
2424
| [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. |![badge-recommended-typescript][] | 🔧 |
2525
| [no-default-export](docs/rules/no-default-export.md) | Convert unnamed default exports to named default exports based on the file name. |![badge-recommended-typescript][] | 🔧 |
2626
| [no-destructuring](docs/rules/no-destructuring.md) | Disallow destructuring that does not meet certain conditions |![badge-recommended-typescript][] | |

docs/rules/named-functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Enforce top-level functions to be named functions (`th-rules/named-functions`)
1+
# Enforce top-level functions to be named function declarations (`th-rules/named-functions`)
22

33
💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-typescript`.
44

src/rules/named-functions.js

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,48 @@ module.exports = {
22
meta: {
33
type: 'problem',
44
docs: {
5-
description: 'Enforce top-level functions to be named functions',
5+
description: 'Enforce top-level functions to be named function declarations',
66
url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/named-functions.md',
77
},
88
fixable: 'code',
99
schema: [],
1010
},
1111

1212
create(context) {
13-
function reportAndFix(node, name) {
13+
function reportAndFix(node, varName) {
1414
context.report({
1515
node,
1616
message: 'Top-level functions must be named function declarations.',
1717
fix(fixer) {
18-
if (node.type === 'FunctionExpression' && node.parent.type === 'VariableDeclarator') {
19-
// Convert to a function declaration
20-
const sourceCode = context.getSourceCode();
21-
const varName = node.parent.id.name;
18+
const sourceCode = context.getSourceCode();
19+
20+
if (node.type === 'ArrowFunctionExpression') {
21+
// Convert arrow function to a named function declaration
2222
const functionBody = sourceCode.getText(node.body);
2323
const functionParams = sourceCode.getText(node.params);
2424
const asyncKeyword = node.async ? 'async ' : '';
25-
const generatorKeyword = node.generator ? '*' : '';
2625

27-
const functionDeclaration = `${asyncKeyword}function ${generatorKeyword}${varName}(${functionParams}) ${functionBody}`;
28-
return fixer.replaceText(node.parent.parent, functionDeclaration);
26+
let functionDeclaration;
27+
28+
if (node.body.type === 'BlockStatement') {
29+
functionDeclaration = `${asyncKeyword}function ${varName}(${functionParams}) ${functionBody}`;
30+
} else {
31+
// For concise body (single expression)
32+
functionDeclaration = `${asyncKeyword}function ${varName}(${functionParams}) { return ${functionBody}; }`;
33+
}
34+
35+
return fixer.replaceText(node.parent, functionDeclaration);
2936
}
3037

3138
if (node.type === 'FunctionExpression') {
32-
// Convert to a function declaration
33-
const sourceCode = context.getSourceCode();
39+
// Convert anonymous function expression to named function declaration
3440
const functionBody = sourceCode.getText(node.body);
3541
const functionParams = sourceCode.getText(node.params);
3642
const asyncKeyword = node.async ? 'async ' : '';
3743
const generatorKeyword = node.generator ? '*' : '';
3844

39-
const functionDeclaration = `${asyncKeyword}function ${generatorKeyword}${name}(${functionParams}) ${functionBody}`;
40-
return fixer.replaceText(node, functionDeclaration);
45+
const functionDeclaration = `${asyncKeyword}function ${generatorKeyword}${varName}(${functionParams}) ${functionBody}`;
46+
return fixer.replaceText(node.parent, functionDeclaration);
4147
}
4248

4349
return null;
@@ -47,31 +53,29 @@ module.exports = {
4753

4854
return {
4955
Program(programNode) {
50-
const topLevelNodes = programNode.body;
51-
52-
topLevelNodes.forEach((node) => {
53-
if (node.type === 'FunctionDeclaration') {
54-
// Skip if already a named function declaration
55-
return;
56+
programNode.body.forEach((node) => {
57+
// Handle VariableDeclaration (e.g. const function_ = () => {})
58+
if (node.type === 'VariableDeclaration') {
59+
node.declarations.forEach((declaration) => {
60+
// Ensure that declaration.init is defined and that it's a function expression or arrow function
61+
if (
62+
declaration.init &&
63+
(declaration.init.type === 'ArrowFunctionExpression' || declaration.init.type === 'FunctionExpression')
64+
) {
65+
const varName = declaration.id.name;
66+
reportAndFix(declaration.init, varName);
67+
}
68+
});
5669
}
5770

58-
if (
59-
node.type === 'FunctionExpression' ||
60-
(node.type === 'VariableDeclaration' && node.declarations[0].init?.type === 'FunctionExpression')
61-
) {
62-
// Force it to be a named function declaration
63-
if (node.type === 'VariableDeclaration') {
64-
const varName = node.declarations[0].id.name;
65-
reportAndFix(node.declarations[0].init, varName);
66-
} else {
67-
reportAndFix(node, 'Anonymous');
68-
}
71+
// Handle anonymous top-level function declarations
72+
if (node.type === 'FunctionDeclaration' && !node.id) {
73+
reportAndFix(node, 'Anonymous');
6974
}
7075

76+
// Handle ExpressionStatement for top-level arrow function (though uncommon)
7177
if (node.type === 'ExpressionStatement' && node.expression.type === 'ArrowFunctionExpression') {
72-
// Convert arrow functions into named function declarations
73-
const varName = node.expression.id ? node.expression.id.name : 'Anonymous';
74-
reportAndFix(node.expression, varName);
78+
reportAndFix(node.expression, 'Anonymous');
7579
}
7680
});
7781
},

src/rules/no-default-export.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const path = require('node:path');
44

55
const meta = {
6-
type: 'suggestion',
6+
type: 'problem',
77
docs: {
88
description: 'Convert unnamed default exports to named default exports based on the file name.',
99
url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-default-export.md'

0 commit comments

Comments
 (0)