Skip to content

Commit 3f88f76

Browse files
committed
feat: Add named-functions rule to recommended and recommended-typescript configs
1 parent 4b8f039 commit 3f88f76

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +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][] | 🔧 |
2324
| [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. |![badge-recommended-typescript][] | 🔧 |
2425
| [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][] | 🔧 |
2526
| [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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Enforce top-level functions to be named functions (`th-rules/named-functions`)
2+
3+
💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-typescript`.
4+
5+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6+
7+
<!-- end auto-generated rule header -->

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ configs.recommended = {
1919
'th-rules/no-destructuring': 'error',
2020
'th-rules/no-default-export': 'error',
2121
'th-rules/no-comments': 'error',
22+
'th-rules/named-functions': 'error',
2223
'unicorn/prefer-module': 'warn',
2324
'unicorn/filename-case': 'off',
2425
'unicorn/no-array-callback-reference': 'off',

src/rules/named-functions.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
module.exports = {
2+
meta: {
3+
type: 'problem',
4+
docs: {
5+
description: 'Enforce top-level functions to be named functions',
6+
url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/named-functions.md',
7+
},
8+
fixable: 'code',
9+
schema: [],
10+
},
11+
12+
create(context) {
13+
function reportAndFix(node, name) {
14+
context.report({
15+
node,
16+
message: 'Top-level functions must be named function declarations.',
17+
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;
22+
const functionBody = sourceCode.getText(node.body);
23+
const functionParams = sourceCode.getText(node.params);
24+
const asyncKeyword = node.async ? 'async ' : '';
25+
const generatorKeyword = node.generator ? '*' : '';
26+
27+
const functionDeclaration = `${asyncKeyword}function ${generatorKeyword}${varName}(${functionParams}) ${functionBody}`;
28+
return fixer.replaceText(node.parent.parent, functionDeclaration);
29+
}
30+
31+
if (node.type === 'FunctionExpression') {
32+
// Convert to a function declaration
33+
const sourceCode = context.getSourceCode();
34+
const functionBody = sourceCode.getText(node.body);
35+
const functionParams = sourceCode.getText(node.params);
36+
const asyncKeyword = node.async ? 'async ' : '';
37+
const generatorKeyword = node.generator ? '*' : '';
38+
39+
const functionDeclaration = `${asyncKeyword}function ${generatorKeyword}${name}(${functionParams}) ${functionBody}`;
40+
return fixer.replaceText(node, functionDeclaration);
41+
}
42+
43+
return null;
44+
},
45+
});
46+
}
47+
48+
return {
49+
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+
}
57+
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+
}
69+
}
70+
71+
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);
75+
}
76+
});
77+
},
78+
};
79+
},
80+
};

0 commit comments

Comments
 (0)