eslint-plugin-th-rules 1.11.4 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,5 @@
1
- {}
1
+ {
2
+ "cSpell.words": [
3
+ "sonarjs"
4
+ ]
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "plugin:th-rules/recommended-typescript"
4
+ ]
5
+ }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.13.0](https://github.com/tomerh2001/eslint-plugin-th-rules/compare/v1.12.0...v1.13.0) (2024-08-19)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add named-functions rule to recommended and recommended-typescript configs ([3f88f76](https://github.com/tomerh2001/eslint-plugin-th-rules/commit/3f88f76460d7bce934f83da68c555c9795fdfe92))
7
+
8
+ # [1.12.0](https://github.com/tomerh2001/eslint-plugin-th-rules/compare/v1.11.4...v1.12.0) (2024-08-19)
9
+
10
+
11
+ ### Features
12
+
13
+ * created no-comments rule ([82dda49](https://github.com/tomerh2001/eslint-plugin-th-rules/commit/82dda49f7f4708b68b8a4bd81e5af3ca84519a43))
14
+
1
15
  ## [1.11.4](https://github.com/tomerh2001/eslint-plugin-th-rules/compare/v1.11.3...v1.11.4) (2024-08-19)
2
16
 
3
17
 
package/README.md CHANGED
@@ -12,50 +12,17 @@ This repository contains custom ESLint rules to enhance code quality and consist
12
12
  This repository contains custom ESLint rules to enhance code quality and consistency across projects, created by Tomer Horowitz.
13
13
 
14
14
  ## Rules
15
+ <!-- begin auto-generated rules list -->
15
16
 
16
- ### 1. No-destructuring Rule
17
+ 💼 Configurations enabled in.\
18
+ ✅ Set in the `recommended` configuration.\
19
+ 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
17
20
 
18
- **Rule ID:** `th-rules/no-destructuring`
21
+ | Name              | Description | 💼 | 🔧 |
22
+ | :--------------------------------------------------- | :------------------------------------------------------------------------------- | :---------------------------------- | :- |
23
+ | [named-functions](docs/rules/named-functions.md) | Enforce top-level functions to be named functions | ✅ ![badge-recommended-typescript][] | 🔧 |
24
+ | [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. | ✅ ![badge-recommended-typescript][] | 🔧 |
25
+ | [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][] | 🔧 |
26
+ | [no-destructuring](docs/rules/no-destructuring.md) | Disallow destructuring that does not meet certain conditions | ✅ ![badge-recommended-typescript][] | |
19
27
 
20
- #### Description
21
-
22
- This rule disallows destructuring that does not meet certain conditions, aiming to prevent overly complex destructuring patterns and ensure code readability.
23
-
24
- #### Rule Details
25
-
26
- This rule checks for:
27
-
28
- - Destructuring at a nesting level above 3.
29
- - Destructuring of more than the specified maximum number of variables (default is 2).
30
- - Destructuring on a line exceeding the specified maximum line length (default is 100 characters).
31
-
32
- #### Configuration
33
-
34
- ```json
35
- {
36
- "rules": {
37
- "th-rules/no-destructuring": ["error", { "maximumDestructuredVariables": 2, "maximumLineLength": 100 }]
38
- }
39
- }
40
- ```
41
-
42
- ### 2. Name-Export Rule
43
-
44
- **Rule ID:** `th-rules/no-default-export`
45
-
46
- #### Description
47
-
48
- Converts unnamed default exports to named default exports based on the file name. This rule helps maintain consistency in export names and facilitates easier identification of components or modules.
49
-
50
- #### Rule Details
51
-
52
- This rule targets unnamed default exports and automatically generates a named export based on the file name.
53
-
54
- #### Configuration
55
- ```json
56
- {
57
- "rules": {
58
- "no-default-export": "error"
59
- }
60
- }
61
- ```
28
+ <!-- end auto-generated rules list -->
package/bun.lockb CHANGED
Binary file
@@ -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 -->
@@ -0,0 +1,48 @@
1
+ # Disallow comments except for specified allowed patterns (`th-rules/no-comments`)
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 -->
8
+
9
+ ## Options
10
+
11
+ <!-- begin auto-generated rule options list -->
12
+
13
+ | Name | Description | Type |
14
+ | :--------- | :------------------------------------------- | :------- |
15
+ | `allow` | Additional patterns to allow in comments. | String[] |
16
+ | `disallow` | Additional patterns to disallow in comments. | String[] |
17
+
18
+ <!-- end auto-generated rule options list -->
19
+
20
+ ## Description
21
+
22
+ This rule disallows comments unless they match specified allowed patterns. It ensures that only relevant and permitted comments are present in the codebase, such as TODOs, warnings, JSDoc comments, ESLint directives, etc.
23
+
24
+ ## Rule Details
25
+
26
+ By default, the following comments are allowed:
27
+
28
+ - TODO, WARNING, ERROR, INFO (case-insensitive).
29
+ - ESLint directives like `/* eslint-disable */`.
30
+ - JSDoc comments (any comment starting with `/**`).
31
+
32
+ You can also configure additional patterns to allow or disallow specific types of comments.
33
+
34
+ ## Usage
35
+
36
+ ```json
37
+ {
38
+ "rules": {
39
+ "th-rules/no-comments": [
40
+ "error",
41
+ {
42
+ "allow": ["keep", "important"],
43
+ "disallow": ["deprecated", "hack"]
44
+ }
45
+ ]
46
+ }
47
+ }
48
+ ```
@@ -0,0 +1,24 @@
1
+ # Convert unnamed default exports to named default exports based on the file name (`th-rules/no-default-export`)
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 -->
8
+
9
+ ## Description
10
+
11
+ Converts unnamed default exports to named default exports based on the file name. This rule helps maintain consistency in export names and facilitates easier identification of components or modules.
12
+
13
+ ## Rule Details
14
+
15
+ This rule targets unnamed default exports and automatically generates a named export based on the file name.
16
+
17
+ ## Usage
18
+ ```json
19
+ {
20
+ "rules": {
21
+ "no-default-export": "error"
22
+ }
23
+ }
24
+ ```
@@ -0,0 +1,38 @@
1
+ # Disallow destructuring that does not meet certain conditions (`th-rules/no-destructuring`)
2
+
3
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, `recommended-typescript`.
4
+
5
+ <!-- end auto-generated rule header -->
6
+
7
+ ## Options
8
+
9
+ <!-- begin auto-generated rule options list -->
10
+
11
+ | Name | Type |
12
+ | :----------------------------- | :------ |
13
+ | `maximumDestructuredVariables` | Integer |
14
+ | `maximumLineLength` | Integer |
15
+
16
+ <!-- end auto-generated rule options list -->
17
+
18
+ ## Description
19
+
20
+ This rule disallows destructuring that does not meet certain conditions, aiming to prevent overly complex destructuring patterns and ensure code readability.
21
+
22
+ ## Rule Details
23
+
24
+ This rule checks for:
25
+
26
+ - Destructuring at a nesting level above 3.
27
+ - Destructuring of more than the specified maximum number of variables (default is 2).
28
+ - Destructuring on a line exceeding the specified maximum line length (default is 100 characters).
29
+
30
+ ## Usage
31
+
32
+ ```json
33
+ {
34
+ "rules": {
35
+ "th-rules/no-destructuring": ["error", { "maximumDestructuredVariables": 2, "maximumLineLength": 100 }]
36
+ }
37
+ }
38
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "1.11.4",
3
+ "version": "1.13.0",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",
@@ -18,16 +18,17 @@
18
18
  "update:eslint-docs": "eslint-doc-generator"
19
19
  },
20
20
  "dependencies": {
21
+ "@babel/eslint-parser": "^7.25.1",
21
22
  "eslint-config-jsdoc": "^15.4.0",
22
23
  "eslint-config-xo": "^0.46.0",
23
24
  "eslint-config-xo-react": "^0.27.0",
24
25
  "eslint-plugin-jsdoc": "^50.2.2",
25
- "eslint-plugin-no-comments": "^1.1.10",
26
26
  "eslint-plugin-react": "^7.35.0",
27
27
  "eslint-plugin-react-hooks": "^4.6.2",
28
28
  "eslint-plugin-react-native": "^4.1.0",
29
29
  "eslint-plugin-security": "^3.0.1",
30
30
  "eslint-plugin-sonarjs": "^1.0.4",
31
+ "eslint-plugin-th-rules": "^1.12.0",
31
32
  "requireindex": "^1.2.0"
32
33
  },
33
34
  "devDependencies": {
package/src/index.js CHANGED
@@ -18,6 +18,8 @@ configs.recommended = {
18
18
  rules: {
19
19
  'th-rules/no-destructuring': 'error',
20
20
  'th-rules/no-default-export': 'error',
21
+ 'th-rules/no-comments': 'error',
22
+ 'th-rules/named-functions': 'error',
21
23
  'unicorn/prefer-module': 'warn',
22
24
  'unicorn/filename-case': 'off',
23
25
  'unicorn/no-array-callback-reference': 'off',
@@ -26,14 +28,9 @@ configs.recommended = {
26
28
  'unicorn/no-await-expression-member': 'off',
27
29
  'new-cap': 'off',
28
30
  'no-await-in-loop': 'off',
31
+ 'n/file-extension-in-import': 'off',
32
+ 'import/no-cycle': 'off',
29
33
  camelcase: 'warn',
30
- 'no-comments/disallowComments': [
31
- 'error',
32
- {
33
- allow: ['^\\*\\*', 'eslint-disable', 'global', 'TODO', 'FIXME', 'NOTE', 'DEBUG'],
34
- },
35
- ],
36
-
37
34
  },
38
35
  env: {
39
36
  node: true,
@@ -43,7 +40,7 @@ configs.recommended = {
43
40
  };
44
41
 
45
42
  for (const configName of Object.keys(configs)) {
46
- configs[configName + '-typescript'] = {
43
+ configs[`${configName}-typescript`] = {
47
44
  ...configs[configName],
48
45
  extends: [
49
46
  'plugin:@typescript-eslint/strict-type-checked',
@@ -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,0 +1,97 @@
1
+ /* eslint-disable unicorn/prefer-module */
2
+
3
+ const allowedPatterns = [
4
+ /todo/i, // Allow TODO (case-insensitive)
5
+ /warning/i, // Allow WARNING (case-insensitive)
6
+ /error/i, // Allow ERROR (case-insensitive)
7
+ /info/i, // Allow INFO (case-insensitive)
8
+ /^\s*eslint-(disable|enable|env|globals|ignore|directive)/, // Allow ESLint directives
9
+ ];
10
+
11
+ const meta = {
12
+ type: 'problem',
13
+ docs: {
14
+ description: 'Disallow comments except for specified allowed patterns.',
15
+ url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-comments.md'
16
+ },
17
+ fixable: 'code',
18
+ schema: [
19
+ {
20
+ type: 'object',
21
+ properties: {
22
+ allow: {
23
+ type: 'array',
24
+ items: {
25
+ type: 'string',
26
+ },
27
+ description: 'Additional patterns to allow in comments.',
28
+ },
29
+ disallow: {
30
+ type: 'array',
31
+ items: {
32
+ type: 'string',
33
+ },
34
+ description: 'Additional patterns to disallow in comments.',
35
+ },
36
+ },
37
+ additionalProperties: false,
38
+ },
39
+ ],
40
+ };
41
+
42
+ function create(context) {
43
+ const options = context.options[0] || {};
44
+ const userAllowedPatterns = (options.allow || []).map((pattern) => new RegExp(pattern));
45
+ const userDisallowedPatterns = (options.disallow || []).map((pattern) => new RegExp(pattern));
46
+
47
+ function isCommentAllowed(comment) {
48
+ const text = comment.value.trim();
49
+
50
+ // Check if the comment is a valid JSDoc comment
51
+ if (comment.type === 'Block' && comment.value.startsWith('*')) {
52
+ return true; // Allow any JSDoc-style block comment (/** ... */)
53
+ }
54
+
55
+ // Check if the comment matches any allowed pattern
56
+ for (const pattern of [...allowedPatterns, ...userAllowedPatterns]) {
57
+ if (pattern.test(text)) {
58
+ return true;
59
+ }
60
+ }
61
+
62
+ // Check if the comment matches any disallowed pattern
63
+ for (const pattern of userDisallowedPatterns) {
64
+ if (pattern.test(text)) {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ return false; // Disallow by default if no match
70
+ }
71
+
72
+ return {
73
+ Program() {
74
+ const sourceCode = context.getSourceCode();
75
+ const comments = sourceCode.getAllComments();
76
+
77
+ comments.forEach((comment) => {
78
+ if (!isCommentAllowed(comment)) {
79
+ context.report({
80
+ node: comment,
81
+ message: 'Comment not allowed.',
82
+ fix(fixer) {
83
+ return fixer.remove(comment);
84
+ },
85
+ });
86
+ }
87
+ });
88
+ },
89
+ };
90
+ }
91
+
92
+ const rule = {
93
+ meta,
94
+ create,
95
+ };
96
+
97
+ module.exports = rule;
@@ -6,6 +6,7 @@ const meta = {
6
6
  type: 'suggestion',
7
7
  docs: {
8
8
  description: 'Convert unnamed default exports to named default exports based on the file name.',
9
+ url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-default-export.md'
9
10
  },
10
11
  fixable: 'code',
11
12
  schema: [],
@@ -20,6 +20,7 @@ const meta = {
20
20
  description: 'Disallow destructuring that does not meet certain conditions',
21
21
  category: 'Possible Errors',
22
22
  recommended: true,
23
+ url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-destructuring.md'
23
24
  },
24
25
  schema: [
25
26
  {
@@ -1,56 +0,0 @@
1
- const {RuleTester} = require('eslint');
2
- const rule = require('../../../lib/rules/no-default-export.js');
3
-
4
- const ruleTester = new RuleTester({
5
- parserOptions: {
6
- ecmaVersion: 2020,
7
- sourceType: 'module',
8
- },
9
- });
10
-
11
- ruleTester.run('no-unamed-default-export', rule, {
12
- valid: [
13
- // Already named exports should pass
14
- 'const a = 1; export default a;',
15
- 'export default function foo() {}',
16
- 'export default class Foo {}',
17
- ],
18
- invalid: [
19
- // Function exports without a name
20
- {
21
- code: 'export default function () {}',
22
- output: 'const Input = function () {};\nexport default Input;',
23
- errors: [{message: 'Unnamed default export should be named based on the file name.'}],
24
- },
25
- // Class exports without a name
26
- {
27
- code: 'export default class {}',
28
- output: 'const Input = class {};\nexport default Input;',
29
- errors: [{message: 'Unnamed default export should be named based on the file name.'}],
30
- },
31
- // Object literal exports
32
- {
33
- code: 'export default {}',
34
- output: 'const Input = {};\nexport default Input;',
35
- errors: [{message: 'Unnamed default export should be named based on the file name.'}],
36
- },
37
- // Primitive value exports
38
- {
39
- code: 'export default 42',
40
- output: 'const Input = 42;\nexport default Input;',
41
- errors: [{message: 'Unnamed default export should be named based on the file name.'}],
42
- },
43
- // Null exports
44
- {
45
- code: 'export default null',
46
- output: 'const Input = null;\nexport default Input;',
47
- errors: [{message: 'Unnamed default export should be named based on the file name.'}],
48
- },
49
- // Arrow function exports
50
- {
51
- code: 'export default () => {}',
52
- output: 'const Input = () => {};\nexport default Input;',
53
- errors: [{message: 'Unnamed default export should be named based on the file name.'}],
54
- },
55
- ],
56
- });
@@ -1,51 +0,0 @@
1
- const {RuleTester} = require('eslint');
2
- const rule = require('../../../lib/rules/no-destructuring.js');
3
-
4
- const ruleTester = new RuleTester({
5
- parserOptions: {
6
- ecmaVersion: 2020, // ES6 syntax
7
- sourceType: 'module', // Allows the use of imports
8
- },
9
- });
10
-
11
- ruleTester.run('no-destructuring', rule, {
12
- valid: [
13
- {
14
- code: 'const { a, b } = obj;',
15
- options: [{maximumDestructuredVariables: 3, maximumLineLength: 80}],
16
- },
17
- {
18
- code: 'const { a } = obj;',
19
- options: [{maximumDestructuredVariables: 2, maximumLineLength: 80}],
20
- },
21
- {
22
- code: 'const { a, b, c } = obj;',
23
- options: [{maximumDestructuredVariables: 3, maximumLineLength: 30}],
24
- },
25
- {
26
- code: 'function foo() {\n\tconst { a, b } = obj;\n}',
27
- options: [{maximumDestructuredVariables: 3, maximumLineLength: 80}],
28
- },
29
- {
30
- code: '\tconst { a, b } = obj;',
31
- options: [{maximumDestructuredVariables: 3, maximumLineLength: 80}],
32
- },
33
- ],
34
- invalid: [
35
- {
36
- code: 'const { a, b, c } = obj;',
37
- options: [{maximumDestructuredVariables: 2, maximumLineLength: 80}],
38
- errors: [{message: 'destructuring of more than 2 variables is not allowed'}],
39
- },
40
- {
41
- code: 'const { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z } = obj;',
42
- options: [{maximumDestructuredVariables: 3, maximumLineLength: 80}],
43
- errors: [{message: 'destructuring of more than 3 variables is not allowed'}, {message: 'destructuring on a line exceeding 80 characters is not allowed'}],
44
- },
45
- {
46
- code: 'function foo() {\n\t\t\t\t\t\t\tconst { a, b } = obj;\n}',
47
- options: [{maximumDestructuredVariables: 3, maximumLineLength: 80}],
48
- errors: [{message: 'destructuring at a nesting level above 3 is not allowed, instead saw 7 levels of nesting'}],
49
- },
50
- ],
51
- });