eslint-plugin-th-rules 2.7.1 → 2.8.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.
Files changed (74) hide show
  1. package/README.md +171 -11
  2. package/dist/configs/bundles/recommended-react.d.ts +199 -0
  3. package/dist/configs/bundles/recommended-react.js +11 -0
  4. package/dist/configs/bundles/recommended-typescript.d.ts +190 -0
  5. package/dist/configs/bundles/recommended-typescript.js +6 -0
  6. package/dist/configs/bundles/recommended.d.ts +190 -0
  7. package/dist/configs/bundles/recommended.js +8 -0
  8. package/dist/configs/core/base.d.ts +170 -0
  9. package/dist/configs/core/base.js +23 -0
  10. package/dist/configs/core/react.d.ts +6 -0
  11. package/dist/configs/core/react.js +8 -0
  12. package/dist/configs/core/typescript.d.ts +2 -0
  13. package/dist/configs/core/typescript.js +19 -0
  14. package/dist/configs/externals/base.d.ts +15 -0
  15. package/dist/configs/externals/base.js +16 -0
  16. package/dist/configs/externals/opinionated.d.ts +10 -0
  17. package/dist/configs/externals/opinionated.js +12 -0
  18. package/dist/index.d.ts +1170 -0
  19. package/dist/index.js +21 -0
  20. package/dist/plugin.d.ts +5 -0
  21. package/dist/plugin.js +14 -0
  22. package/dist/rules/no-boolean-coercion.d.ts +5 -0
  23. package/dist/rules/no-boolean-coercion.js +98 -0
  24. package/dist/rules/no-comments.d.ts +11 -0
  25. package/dist/rules/no-comments.js +83 -0
  26. package/dist/rules/no-default-export.d.ts +5 -0
  27. package/dist/rules/no-default-export.js +61 -0
  28. package/dist/rules/no-destructuring.d.ts +8 -0
  29. package/dist/rules/no-destructuring.js +121 -0
  30. package/dist/rules/prefer-is-empty.d.ts +5 -0
  31. package/dist/rules/prefer-is-empty.js +101 -0
  32. package/dist/rules/schemas-in-schemas-file.d.ts +9 -0
  33. package/dist/rules/schemas-in-schemas-file.js +141 -0
  34. package/dist/rules/top-level-functions.d.ts +5 -0
  35. package/dist/rules/top-level-functions.js +153 -0
  36. package/dist/rules/types-in-dts.d.ts +8 -0
  37. package/dist/rules/types-in-dts.js +76 -0
  38. package/package.json +25 -14
  39. package/.github/dependabot.yml +0 -15
  40. package/.github/workflows/codecov.yml +0 -26
  41. package/.github/workflows/codeql.yml +0 -82
  42. package/.github/workflows/dependency-review.yml +0 -20
  43. package/.github/workflows/main.yml +0 -43
  44. package/.github/workflows/scorecard.yml +0 -72
  45. package/.github/workflows/snyk-security.yml +0 -67
  46. package/.releaserc +0 -13
  47. package/.vscode/settings.json +0 -8
  48. package/.yarn/releases/yarn-4.12.0.cjs +0 -942
  49. package/.yarnrc.yml +0 -3
  50. package/CHANGELOG.md +0 -628
  51. package/SECURITY.md +0 -48
  52. package/docs/rules/no-boolean-coercion.md +0 -9
  53. package/docs/rules/no-comments.md +0 -50
  54. package/docs/rules/no-default-export.md +0 -26
  55. package/docs/rules/no-destructuring.md +0 -40
  56. package/docs/rules/prefer-is-empty.md +0 -9
  57. package/docs/rules/schemas-in-schemas-file.md +0 -170
  58. package/docs/rules/top-level-functions.md +0 -48
  59. package/docs/rules/types-in-dts.md +0 -112
  60. package/renovate.json +0 -3
  61. package/scripts/verify.mjs +0 -16
  62. package/src/index.js +0 -144
  63. package/src/rules/no-boolean-coercion.js +0 -124
  64. package/src/rules/no-comments.js +0 -94
  65. package/src/rules/no-default-export.js +0 -64
  66. package/src/rules/no-destructuring.js +0 -114
  67. package/src/rules/prefer-is-empty.js +0 -104
  68. package/src/rules/schemas-in-schemas-file.js +0 -191
  69. package/src/rules/top-level-functions.js +0 -200
  70. package/src/rules/types-in-dts.js +0 -94
  71. package/tests/no-boolean-coercion.test.ts +0 -83
  72. package/tests/prefer-is-empty.test.ts +0 -148
  73. package/tsconfig.json +0 -22
  74. package/xo.config.ts +0 -2
@@ -1,200 +0,0 @@
1
- /* eslint-disable unicorn/prefer-module */
2
-
3
- const meta = {
4
- type: 'suggestion',
5
- docs: {
6
- description: 'Require all top-level functions to be named/regular functions.',
7
- category: 'Stylistic Issues',
8
- recommended: false,
9
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/top-level-functions.md',
10
- },
11
- fixable: 'code',
12
- schema: [],
13
- };
14
-
15
- /**
16
- * Build a replacement code string for an arrow function:
17
- *
18
- * @param {string} funcName - The name of the new function.
19
- * @param {ArrowFunctionExpression} arrowNode - The ArrowFunctionExpression node.
20
- * @param {import('eslint').SourceCode} sourceCode - The ESLint SourceCode object.
21
- * @param {boolean} isExport - Whether or not this function is exported (e.g., `export const foo = ...`).
22
- * @returns {string} The replacement code.
23
- */
24
- function buildArrowFunctionReplacement(functionName, arrowNode, sourceCode, isExport) {
25
- const asyncKeyword = arrowNode.async ? 'async ' : '';
26
- const exportKeyword = isExport ? 'export ' : '';
27
-
28
- const parametersText = arrowNode.params.map(parameter => sourceCode.getText(parameter)).join(', ');
29
-
30
- let bodyText;
31
- if (arrowNode.body.type === 'BlockStatement') {
32
- bodyText = sourceCode.getText(arrowNode.body);
33
- } else {
34
- const expressionText = sourceCode.getText(arrowNode.body);
35
- bodyText = `{ return ${expressionText}; }`;
36
- }
37
-
38
- return `${exportKeyword}${asyncKeyword}function ${functionName}(${parametersText}) ${bodyText}`;
39
- }
40
-
41
- /**
42
- * Build a replacement code string for a function expression:
43
- *
44
- * @param {string} funcName - The name of the new function.
45
- * @param {FunctionExpression} funcExprNode - The FunctionExpression node.
46
- * @param {import('eslint').SourceCode} sourceCode - The ESLint SourceCode object.
47
- * @param {boolean} isExport - Whether or not this function is exported.
48
- * @returns {string} The replacement code.
49
- */
50
- function buildFunctionExpressionReplacement(functionName, functionExprNode, sourceCode, isExport) {
51
- const asyncKeyword = functionExprNode.async ? 'async ' : '';
52
- const exportKeyword = isExport ? 'export ' : '';
53
-
54
- const parametersText = functionExprNode.params.map(parameter => sourceCode.getText(parameter)).join(', ');
55
- const bodyText = sourceCode.getText(functionExprNode.body);
56
-
57
- return `${exportKeyword}${asyncKeyword}function ${functionName}(${parametersText}) ${bodyText}`;
58
- }
59
-
60
- /**
61
- * Build a replacement for an anonymous top-level FunctionDeclaration (including async).
62
- *
63
- * @param {import('eslint').SourceCode} sourceCode
64
- * @param {import('estree').FunctionDeclaration} node
65
- * @param {string} [funcName='defaultFunction']
66
- * @param {boolean} [isExport=false]
67
- */
68
- function buildAnonymousFunctionDeclarationReplacement(sourceCode, node, functionName = 'defaultFunction', isExport = false) {
69
- const originalText = sourceCode.getText(node);
70
- const exportKeyword = isExport ? 'export ' : '';
71
-
72
- let replaced = originalText;
73
- const asyncFunctionRegex = /^\s*async\s+function\s*\(/;
74
- const functionRegex = /^\s*function\s*\(/;
75
-
76
- replaced = asyncFunctionRegex.test(replaced) ? replaced.replace(asyncFunctionRegex, `async function ${functionName}(`) : replaced.replace(functionRegex, `function ${functionName}(`);
77
-
78
- if (isExport && !replaced.trimStart().startsWith('export')) {
79
- replaced = `${exportKeyword}${replaced}`;
80
- }
81
-
82
- return replaced;
83
- }
84
-
85
- function create(context) {
86
- const sourceCode = context.getSourceCode();
87
-
88
- return {
89
- VariableDeclarator(node) {
90
- const declParent = node.parent;
91
- const grandParent = declParent.parent;
92
-
93
- const isTopLevel
94
- = grandParent.type === 'Program'
95
- || grandParent.type === 'ExportNamedDeclaration'
96
- || grandParent.type === 'ExportDefaultDeclaration';
97
-
98
- if (!isTopLevel) {
99
- return;
100
- }
101
-
102
- const isExport
103
- = grandParent.type === 'ExportNamedDeclaration'
104
- || grandParent.type === 'ExportDefaultDeclaration';
105
-
106
- if (!node.init) {
107
- return;
108
- }
109
-
110
- const functionName = node.id && node.id.name;
111
- if (!functionName) {
112
- return;
113
- }
114
-
115
- if (node.init.type === 'ArrowFunctionExpression') {
116
- context.report({
117
- node: node.init,
118
- message: 'Top-level arrow functions must be named/regular functions.',
119
- fix(fixer) {
120
- const replacement = buildArrowFunctionReplacement(
121
- functionName,
122
- node.init,
123
- sourceCode,
124
- isExport,
125
- );
126
-
127
- return fixer.replaceText(
128
- isExport ? grandParent : declParent,
129
- replacement,
130
- );
131
- },
132
- });
133
- } else if (node.init.type === 'FunctionExpression') {
134
- context.report({
135
- node: node.init,
136
- message: 'Top-level function expressions must be named/regular functions.',
137
- fix(fixer) {
138
- const replacement = buildFunctionExpressionReplacement(
139
- functionName,
140
- node.init,
141
- sourceCode,
142
- isExport,
143
- );
144
- return fixer.replaceText(
145
- isExport ? grandParent : declParent,
146
- replacement,
147
- );
148
- },
149
- });
150
- }
151
- },
152
-
153
- FunctionDeclaration(node) {
154
- if (node.id) {
155
- return;
156
- }
157
-
158
- const parent = node.parent;
159
-
160
- const isTopLevel
161
- = parent.type === 'Program'
162
- || parent.type === 'ExportNamedDeclaration'
163
- || parent.type === 'ExportDefaultDeclaration';
164
-
165
- if (!isTopLevel) {
166
- return;
167
- }
168
-
169
- const isExport
170
- = parent.type === 'ExportNamedDeclaration'
171
- || parent.type === 'ExportDefaultDeclaration';
172
-
173
- context.report({
174
- node,
175
- message: 'Top-level anonymous function declarations must be named.',
176
- fix(fixer) {
177
- const newName = 'defaultFunction';
178
- const replacement = buildAnonymousFunctionDeclarationReplacement(
179
- sourceCode,
180
- node,
181
- newName,
182
- isExport,
183
- );
184
-
185
- return fixer.replaceText(
186
- isExport ? parent : node,
187
- replacement,
188
- );
189
- },
190
- });
191
- },
192
- };
193
- }
194
-
195
- const rule = {
196
- meta,
197
- create,
198
- };
199
-
200
- module.exports = rule;
@@ -1,94 +0,0 @@
1
- /**
2
- * @fileoverview Enforce that TypeScript type declarations exist only in .d.ts files.
3
- */
4
-
5
- const meta = {
6
- type: 'problem',
7
- docs: {
8
- description: 'Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files',
9
- category: 'Best Practices',
10
- recommended: true,
11
- url: 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/types-in-dts.md',
12
- },
13
- schema: [
14
- {
15
- type: 'object',
16
- properties: {
17
- allowEnums: {type: 'boolean'},
18
- allowDeclare: {type: 'boolean'},
19
- },
20
- additionalProperties: false,
21
- },
22
- ],
23
- messages: {
24
- moveToDts:
25
- 'Type declarations must be defined in a a .d.ts file.',
26
- },
27
- };
28
-
29
- /**
30
- * @param {import('eslint').Rule.RuleContext} context
31
- */
32
- function create(context) {
33
- const options = context.options?.[0] ?? {};
34
- const allowEnums = Boolean(options.allowEnums);
35
- const allowDeclare = Boolean(options.allowDeclare);
36
-
37
- function isDtsFile(filename) {
38
- if (!filename || filename === '<input>') {
39
- return false;
40
- }
41
-
42
- return filename.endsWith('.d.ts');
43
- }
44
-
45
- function hasDeclareModifier(node) {
46
- if (node && node.declare === true) {
47
- return true;
48
- }
49
-
50
- const mods = node && Array.isArray(node.modifiers) ? node.modifiers : [];
51
- return mods.some(m => m && m.type === 'TSDeclareKeyword');
52
- }
53
-
54
- function reportIfNotDts(node) {
55
- const filename = context.getFilename();
56
- if (isDtsFile(filename)) {
57
- return;
58
- }
59
-
60
- if (allowDeclare && hasDeclareModifier(node)) {
61
- return;
62
- }
63
-
64
- context.report({
65
- node,
66
- messageId: 'moveToDts',
67
- data: {filename},
68
- });
69
- }
70
-
71
- return {
72
- TSTypeAliasDeclaration(node) {
73
- reportIfNotDts(node);
74
- },
75
-
76
- TSInterfaceDeclaration(node) {
77
- reportIfNotDts(node);
78
- },
79
-
80
- TSEnumDeclaration(node) {
81
- if (allowEnums) {
82
- return;
83
- }
84
-
85
- reportIfNotDts(node);
86
- },
87
-
88
- };
89
- }
90
-
91
- module.exports = {
92
- meta,
93
- create,
94
- };
@@ -1,83 +0,0 @@
1
- const {RuleTester} = require('@typescript-eslint/rule-tester');
2
- const rule = require('../src/rules/no-boolean-coercion');
3
-
4
- const ruleTester = new RuleTester({
5
- languageOptions: {
6
- ecmaVersion: 2020,
7
- sourceType: 'module',
8
- },
9
- });
10
-
11
- ruleTester.run('no-boolean-coercion', rule, {
12
- valid: [
13
- '_.isNil(value);',
14
- '_.isEmpty(list);',
15
- 'if (value != null) {}',
16
- 'if (list.length > 0) {}',
17
- 'Boolean;',
18
- 'const BooleanValue = true;',
19
- 'const fn = Boolean;',
20
- ],
21
-
22
- invalid: [
23
- {
24
- code: 'Boolean(foo);',
25
- errors: [
26
- {
27
- messageId: 'useIsNil',
28
- suggestions: [
29
- {
30
- desc: 'Replace with !_.isNil(foo)',
31
- output: '!_.isNil(foo);',
32
- },
33
- ],
34
- },
35
- ],
36
- },
37
-
38
- {
39
- code: 'const x = Boolean(bar);',
40
- errors: [
41
- {
42
- messageId: 'useIsNil',
43
- suggestions: [
44
- {
45
- desc: 'Replace with !_.isNil(bar)',
46
- output: 'const x = !_.isNil(bar);',
47
- },
48
- ],
49
- },
50
- ],
51
- },
52
-
53
- {
54
- code: '!!value;',
55
- errors: [
56
- {
57
- messageId: 'useIsNil',
58
- suggestions: [
59
- {
60
- desc: 'Replace with !_.isNil(value)',
61
- output: '!_.isNil(value);',
62
- },
63
- ],
64
- },
65
- ],
66
- },
67
-
68
- {
69
- code: 'Boolean([]);',
70
- errors: [
71
- {
72
- messageId: 'useIsEmpty',
73
- suggestions: [
74
- {
75
- desc: 'Replace with !_.isEmpty([])',
76
- output: '!_.isEmpty([]);',
77
- },
78
- ],
79
- },
80
- ],
81
- },
82
- ],
83
- });
@@ -1,148 +0,0 @@
1
- const {RuleTester} = require('@typescript-eslint/rule-tester');
2
- const rule = require('../src/rules/prefer-is-empty');
3
-
4
- const ruleTester = new RuleTester({
5
- languageOptions: {
6
- ecmaVersion: 2020,
7
- sourceType: 'module',
8
- },
9
- });
10
-
11
- ruleTester.run('prefer-is-empty', rule, {
12
- valid: [
13
- '_.isEmpty(values);',
14
- '!_.isEmpty(values);',
15
- 'Array.isArray(values);',
16
- 'values.size > 0;', // Non-length property
17
- ],
18
-
19
- invalid: [
20
- {
21
- code: 'values.length === 0;',
22
- errors: [
23
- {
24
- message:
25
- 'Use _.isEmpty(values) instead of checking values.length === 0',
26
- suggestions: [
27
- {
28
- desc: 'Replace with _.isEmpty(values)',
29
- output: '_.isEmpty(values);',
30
- },
31
- ],
32
- },
33
- ],
34
- },
35
-
36
- {
37
- code: 'values.length < 1;',
38
- errors: [
39
- {
40
- message:
41
- 'Use _.isEmpty(values) instead of checking values.length < 1',
42
- suggestions: [
43
- {
44
- desc: 'Replace with _.isEmpty(values)',
45
- output: '_.isEmpty(values);',
46
- },
47
- ],
48
- },
49
- ],
50
- },
51
-
52
- {
53
- code: 'values.length <= 0;',
54
- errors: [
55
- {
56
- message:
57
- 'Use _.isEmpty(values) instead of checking values.length <= 0',
58
- suggestions: [
59
- {
60
- desc: 'Replace with _.isEmpty(values)',
61
- output: '_.isEmpty(values);',
62
- },
63
- ],
64
- },
65
- ],
66
- },
67
-
68
- {
69
- code: 'values.length > 0;',
70
- errors: [
71
- {
72
- message:
73
- 'Use _.isEmpty(values) instead of checking values.length > 0',
74
- suggestions: [
75
- {
76
- desc: 'Replace with !_.isEmpty(values)',
77
- output: '!_.isEmpty(values);',
78
- },
79
- ],
80
- },
81
- ],
82
- },
83
-
84
- {
85
- code: 'values.length >= 1;',
86
- errors: [
87
- {
88
- message:
89
- 'Use _.isEmpty(values) instead of checking values.length >= 1',
90
- suggestions: [
91
- {
92
- desc: 'Replace with !_.isEmpty(values)',
93
- output: '!_.isEmpty(values);',
94
- },
95
- ],
96
- },
97
- ],
98
- },
99
-
100
- {
101
- code: 'values.length !== 0;',
102
- errors: [
103
- {
104
- message:
105
- 'Use _.isEmpty(values) instead of checking values.length !== 0',
106
- suggestions: [
107
- {
108
- desc: 'Replace with !_.isEmpty(values)',
109
- output: '!_.isEmpty(values);',
110
- },
111
- ],
112
- },
113
- ],
114
- },
115
-
116
- {
117
- code: 'if (items.length > 0) {}',
118
- errors: [
119
- {
120
- message:
121
- 'Use _.isEmpty(items) instead of checking items.length > 0',
122
- suggestions: [
123
- {
124
- desc: 'Replace with !_.isEmpty(items)',
125
- output: 'if (!_.isEmpty(items)) {}',
126
- },
127
- ],
128
- },
129
- ],
130
- },
131
-
132
- {
133
- code: '0 === items.length;',
134
- errors: [
135
- {
136
- message:
137
- 'Use _.isEmpty(items) instead of checking items.length === 0',
138
- suggestions: [
139
- {
140
- desc: 'Replace with _.isEmpty(items)',
141
- output: '_.isEmpty(items);',
142
- },
143
- ],
144
- },
145
- ],
146
- },
147
- ],
148
- });
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "lib": ["ESNext"],
4
- "module": "esnext",
5
- "target": "esnext",
6
- "moduleResolution": "bundler",
7
- "moduleDetection": "force",
8
- "allowImportingTsExtensions": true,
9
- "noEmit": true,
10
- "composite": true,
11
- "strict": true,
12
- "downlevelIteration": true,
13
- "skipLibCheck": true,
14
- "jsx": "react-jsx",
15
- "allowSyntheticDefaultImports": true,
16
- "forceConsistentCasingInFileNames": true,
17
- "allowJs": true,
18
- "types": [
19
- "bun-types" // add Bun global
20
- ]
21
- }
22
- }
package/xo.config.ts DELETED
@@ -1,2 +0,0 @@
1
-
2
- import {recommended} from 'eslint-plugin-th-rules';