eslint-plugin-th-rules 3.0.0 → 3.1.1

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.
package/dist/index.js CHANGED
@@ -3,7 +3,9 @@ import { rules } from './plugin.js';
3
3
  import { recommended } from './configs/bundles/recommended.js';
4
4
  import { recommendedReact } from './configs/bundles/recommended-react.js';
5
5
  import { recommendedTypescript } from './configs/bundles/recommended-typescript.js';
6
+ import { recommendedTypescriptReact } from './configs/bundles/recommended-typescript-react.js';
6
7
  // Internal layers (named exports only)
8
+ export { rules } from './plugin.js';
7
9
  export { coreBase } from './configs/core/base.js';
8
10
  export { coreTypescript } from './configs/core/typescript.js';
9
11
  export { coreReact } from './configs/core/react.js';
@@ -11,11 +13,8 @@ export { externalsBase } from './configs/externals/base.js';
11
13
  export { externalsOpinionated } from './configs/externals/opinionated.js';
12
14
  export const configs = {
13
15
  recommended,
14
- 'recommended-react': recommendedReact,
15
- 'recommended-typescript': recommendedTypescript,
16
- };
17
- export default {
18
- rules,
19
- configs,
16
+ recommendedReact,
17
+ recommendedTypescript,
18
+ recommendedTypescriptReact,
20
19
  };
21
- export { rules } from './plugin.js';
20
+ export default { rules, configs };
@@ -5,7 +5,7 @@ type Options = [
5
5
  disallow?: string[];
6
6
  }?
7
7
  ];
8
- declare const _default: ESLintUtils.RuleModule<"commentNotAllowed", Options, unknown, ESLintUtils.RuleListener> & {
8
+ declare const noComments: ESLintUtils.RuleModule<"commentNotAllowed", Options, unknown, ESLintUtils.RuleListener> & {
9
9
  name: string;
10
10
  };
11
- export default _default;
11
+ export default noComments;
@@ -6,7 +6,7 @@ const DEFAULT_ALLOWED_PATTERNS = [
6
6
  /info/i, // Allow INFO (case-insensitive)
7
7
  /^\s*eslint-(disable|enable|env|globals|ignore|directive)/,
8
8
  ];
9
- export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-comments.md')({
9
+ const noComments = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-comments.md')({
10
10
  name: 'no-comments',
11
11
  meta: {
12
12
  type: 'problem',
@@ -43,7 +43,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
43
43
  const userDisallowedPatterns = (option.disallow ?? []).map(pattern => new RegExp(pattern));
44
44
  function isCommentAllowed(comment) {
45
45
  const text = comment.value.trim();
46
- // Allow JSDoc
47
46
  if (comment.type === 'Block' && comment.value.startsWith('*')) {
48
47
  return true;
49
48
  }
@@ -81,3 +80,4 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
81
80
  };
82
81
  },
83
82
  });
83
+ export default noComments;
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"unnamed", [], unknown, ESLintUtils.RuleListener> & {
2
+ declare const noDefaultExport: ESLintUtils.RuleModule<"unnamed", [], unknown, ESLintUtils.RuleListener> & {
3
3
  name: string;
4
4
  };
5
- export default _default;
5
+ export default noDefaultExport;
@@ -1,6 +1,6 @@
1
1
  import * as path from 'node:path';
2
2
  import { ESLintUtils } from '@typescript-eslint/utils';
3
- export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-default-export.md')({
3
+ const noDefaultExport = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-default-export.md')({
4
4
  name: 'no-default-export',
5
5
  meta: {
6
6
  type: 'problem',
@@ -16,9 +16,7 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
16
16
  defaultOptions: [],
17
17
  create(context) {
18
18
  function generateExportNameFromFileName(fileName) {
19
- // Remove all invalid characters, replace with spaces
20
19
  const cleaned = fileName.replaceAll(/[^a-zA-Z\d]+/g, ' ');
21
- // Split into tokens
22
20
  const parts = cleaned
23
21
  .trim()
24
22
  .split(/\s+/g)
@@ -26,7 +24,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
26
24
  if (parts.length === 0) {
27
25
  return 'defaultExport';
28
26
  }
29
- // Build camelCase
30
27
  const [first, ...rest] = parts;
31
28
  return (first.charAt(0).toLowerCase() + first.slice(1)) + rest
32
29
  .map(p => p.charAt(0).toUpperCase() + p.slice(1))
@@ -34,11 +31,9 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
34
31
  }
35
32
  return {
36
33
  ExportDefaultDeclaration(node) {
37
- // 1. skip `export default Foo`
38
34
  if (node.declaration.type === 'Identifier') {
39
35
  return;
40
36
  }
41
- // 2. skip named function/class: `export default function Foo() {}`
42
37
  if ('id' in node.declaration && node.declaration.id != null) {
43
38
  return;
44
39
  }
@@ -59,3 +54,4 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
59
54
  };
60
55
  },
61
56
  });
57
+ export default noDefaultExport;
@@ -1,8 +1,8 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
2
+ declare const noDestructuring: ESLintUtils.RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
3
3
  maximumDestructuredVariables: number;
4
4
  maximumLineLength: number;
5
5
  }], unknown, ESLintUtils.RuleListener> & {
6
6
  name: string;
7
7
  };
8
- export default _default;
8
+ export default noDestructuring;
@@ -1,6 +1,6 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
2
  const MAX_TAB_COUNT = 3;
3
- export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-destructuring.md')({
3
+ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-destructuring.md')({
4
4
  name: 'no-destructuring',
5
5
  meta: {
6
6
  type: 'problem',
@@ -85,7 +85,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
85
85
  if (!p) {
86
86
  continue;
87
87
  }
88
- // (...args = {}) pattern
89
88
  if (p.type === 'AssignmentPattern') {
90
89
  reportIfNeeded(p.left, p);
91
90
  continue;
@@ -119,3 +118,4 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
119
118
  };
120
119
  },
121
120
  });
121
+ export default noDestructuring;
@@ -1,5 +1,5 @@
1
- import { ESLintUtils } from "@typescript-eslint/utils";
2
- declare const _default: ESLintUtils.RuleModule<"useIsEmpty", [], unknown, ESLintUtils.RuleListener> & {
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const preferIsEmpty: ESLintUtils.RuleModule<"useIsEmpty", [], unknown, ESLintUtils.RuleListener> & {
3
3
  name: string;
4
4
  };
5
- export default _default;
5
+ export default preferIsEmpty;
@@ -1,31 +1,31 @@
1
- import { ESLintUtils } from "@typescript-eslint/utils";
2
- export default ESLintUtils.RuleCreator(() => "https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/prefer-is-empty.md")({
3
- name: "prefer-is-empty",
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const preferIsEmpty = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/prefer-is-empty.md')({
3
+ name: 'prefer-is-empty',
4
4
  meta: {
5
- type: "problem",
5
+ type: 'problem',
6
6
  docs: {
7
- description: "Require _.isEmpty instead of length comparisons."
7
+ description: 'Require _.isEmpty instead of length comparisons.',
8
8
  },
9
9
  hasSuggestions: true,
10
10
  schema: [],
11
11
  messages: {
12
- useIsEmpty: "Use _.isEmpty({{collection}}) instead of checking {{collection}}.length {{operator}} {{value}}."
13
- }
12
+ useIsEmpty: 'Use _.isEmpty({{collection}}) instead of checking {{collection}}.length {{operator}} {{value}}.',
13
+ },
14
14
  },
15
15
  defaultOptions: [],
16
16
  create(context) {
17
17
  const sourceCode = context.getSourceCode();
18
18
  function isLengthAccess(node) {
19
- return (!!node &&
20
- node.type === "MemberExpression" &&
21
- node.property.type === "Identifier" &&
22
- node.property.name === "length" &&
23
- node.computed === false);
19
+ return (Boolean(node)
20
+ && node.type === 'MemberExpression'
21
+ && node.property.type === 'Identifier'
22
+ && node.property.name === 'length'
23
+ && !node.computed);
24
24
  }
25
25
  function isNumericLiteral(node) {
26
- return (!!node &&
27
- node.type === "Literal" &&
28
- typeof node.value === "number");
26
+ return (Boolean(node)
27
+ && node.type === 'Literal'
28
+ && typeof node.value === 'number');
29
29
  }
30
30
  function report(node, collectionNode, operator, value, isEmptyCheck) {
31
31
  const collectionText = sourceCode.getText(collectionNode.object);
@@ -34,68 +34,59 @@ export default ESLintUtils.RuleCreator(() => "https://github.com/tomerh2001/esli
34
34
  : `!_.isEmpty(${collectionText})`;
35
35
  context.report({
36
36
  node,
37
- messageId: "useIsEmpty",
37
+ messageId: 'useIsEmpty',
38
38
  data: {
39
39
  collection: collectionText,
40
40
  operator,
41
- value
41
+ value,
42
42
  },
43
43
  suggest: [
44
44
  {
45
- messageId: "useIsEmpty",
45
+ messageId: 'useIsEmpty',
46
46
  data: {
47
47
  collection: collectionText,
48
48
  operator,
49
- value
49
+ value,
50
50
  },
51
51
  fix(fixer) {
52
52
  return fixer.replaceText(node, replacement);
53
- }
54
- }
55
- ]
53
+ },
54
+ },
55
+ ],
56
56
  });
57
57
  }
58
58
  return {
59
59
  BinaryExpression(node) {
60
60
  const { left, right, operator } = node;
61
- //
62
- // Case 1: values.length <op> N
63
- //
64
61
  if (isLengthAccess(left) && isNumericLiteral(right)) {
65
- const value = right.value;
66
- // EMPTY checks
67
- if ((operator === "===" && value === 0) ||
68
- (operator === "<=" && value === 0) ||
69
- (operator === "<" && value === 1)) {
62
+ const { value } = right;
63
+ if ((operator === '===' && value === 0)
64
+ || (operator === '<=' && value === 0)
65
+ || (operator === '<' && value === 1)) {
70
66
  report(node, left, operator, value, true);
71
67
  return;
72
68
  }
73
- // NOT EMPTY checks
74
- if ((operator === ">" && value === 0) ||
75
- (operator === ">=" && value === 1) ||
76
- ((operator === "!=" || operator === "!==") && value === 0)) {
69
+ if ((operator === '>' && value === 0)
70
+ || (operator === '>=' && value === 1)
71
+ || ((operator === '!=' || operator === '!==') && value === 0)) {
77
72
  report(node, left, operator, value, false);
78
73
  }
79
74
  }
80
- //
81
- // Case 2: N <op> values.length (reverse order)
82
- //
83
75
  if (isNumericLiteral(left) && isLengthAccess(right)) {
84
- const value = left.value;
85
- // EMPTY checks
86
- if ((operator === "===" && value === 0) ||
87
- (operator === ">=" && value === 0) ||
88
- (operator === ">" && value === 0)) {
76
+ const { value } = left;
77
+ if ((operator === '===' && value === 0)
78
+ || (operator === '>=' && value === 0)
79
+ || (operator === '>' && value === 0)) {
89
80
  report(node, right, operator, value, true);
90
81
  return;
91
82
  }
92
- // NOT EMPTY checks
93
- if ((operator === "<" && value === 1) ||
94
- (operator === "<=" && value === 0)) {
83
+ if ((operator === '<' && value === 1)
84
+ || (operator === '<=' && value === 0)) {
95
85
  report(node, right, operator, value, false);
96
86
  }
97
87
  }
98
- }
88
+ },
99
89
  };
100
- }
90
+ },
101
91
  });
92
+ export default preferIsEmpty;
@@ -1,9 +1,9 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"moveSchema", [{
2
+ declare const schemasInSchemasFile: ESLintUtils.RuleModule<"moveSchema", [{
3
3
  allowedSuffixes: string[];
4
4
  onlyWhenAssigned: boolean;
5
5
  allowInTests: boolean;
6
6
  }], unknown, ESLintUtils.RuleListener> & {
7
7
  name: string;
8
8
  };
9
- export default _default;
9
+ export default schemasInSchemasFile;
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/schemas-in-schemas-file.md')({
2
+ const schemasInSchemasFile = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/schemas-in-schemas-file.md')({
3
3
  name: 'schemas-in-schemas-file',
4
4
  meta: {
5
5
  type: 'problem',
@@ -139,3 +139,4 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
139
139
  };
140
140
  },
141
141
  });
142
+ export default schemasInSchemasFile;
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"arrow" | "funcExpr" | "anonDecl", [], unknown, ESLintUtils.RuleListener> & {
2
+ declare const topLevelFunctions: ESLintUtils.RuleModule<"arrow" | "funcExpr" | "anonDecl", [], unknown, ESLintUtils.RuleListener> & {
3
3
  name: string;
4
4
  };
5
- export default _default;
5
+ export default topLevelFunctions;
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/top-level-functions.md')({
2
+ const topLevelFunctions = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/top-level-functions.md')({
3
3
  name: 'top-level-functions',
4
4
  meta: {
5
5
  type: 'suggestion',
@@ -17,9 +17,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
17
17
  defaultOptions: [],
18
18
  create(context) {
19
19
  const sourceCode = context.getSourceCode();
20
- //
21
- // Helpers
22
- //
23
20
  function buildArrowFunctionReplacement(functionName, arrow, isExport) {
24
21
  const asyncKeyword = arrow.async ? 'async ' : '';
25
22
  const exportKeyword = isExport ? 'export ' : '';
@@ -31,7 +28,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
31
28
  bodyText = sourceCode.getText(arrow.body);
32
29
  }
33
30
  else {
34
- // Expression → convert to return
35
31
  const expressionText = sourceCode.getText(arrow.body);
36
32
  bodyText = `{ return ${expressionText}; }`;
37
33
  }
@@ -63,9 +59,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
63
59
  }
64
60
  return replaced;
65
61
  }
66
- //
67
- // Utility
68
- //
69
62
  function isTopLevel(node) {
70
63
  const { parent } = node;
71
64
  return (parent?.type === 'Program'
@@ -77,9 +70,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
77
70
  return (p?.type === 'ExportNamedDeclaration'
78
71
  || p?.type === 'ExportDefaultDeclaration');
79
72
  }
80
- //
81
- // Rule
82
- //
83
73
  return {
84
74
  VariableDeclarator(node) {
85
75
  const declParent = node.parent;
@@ -102,9 +92,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
102
92
  if (!functionName) {
103
93
  return;
104
94
  }
105
- //
106
- // Arrow functions
107
- //
108
95
  if (node.init.type === 'ArrowFunctionExpression') {
109
96
  const arrowFunc = node.init;
110
97
  context.report({
@@ -116,9 +103,6 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
116
103
  },
117
104
  });
118
105
  }
119
- //
120
- // Function expressions
121
- //
122
106
  if (node.init.type === 'FunctionExpression') {
123
107
  const funcExpr = node.init;
124
108
  context.report({
@@ -134,7 +118,7 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
134
118
  FunctionDeclaration(node) {
135
119
  if (node.id) {
136
120
  return;
137
- } // Already named
121
+ }
138
122
  if (!isTopLevel(node)) {
139
123
  return;
140
124
  }
@@ -151,3 +135,4 @@ export default ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/esli
151
135
  };
152
136
  },
153
137
  });
138
+ export default topLevelFunctions;
@@ -1,8 +1,8 @@
1
- import { ESLintUtils } from "@typescript-eslint/utils";
2
- declare const _default: ESLintUtils.RuleModule<"moveToDts", [{
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const typesInDts: ESLintUtils.RuleModule<"moveToDts", [{
3
3
  allowEnums: boolean;
4
4
  allowDeclare: boolean;
5
5
  }], unknown, ESLintUtils.RuleListener> & {
6
6
  name: string;
7
7
  };
8
- export default _default;
8
+ export default typesInDts;
@@ -1,49 +1,48 @@
1
- import { ESLintUtils } from "@typescript-eslint/utils";
2
- export default ESLintUtils.RuleCreator(() => "https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/types-in-dts.md")({
3
- name: "types-in-dts",
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ const typesInDts = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/types-in-dts.md')({
3
+ name: 'types-in-dts',
4
4
  meta: {
5
- type: "problem",
5
+ type: 'problem',
6
6
  docs: {
7
- description: "Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files."
7
+ description: 'Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files.',
8
8
  },
9
9
  schema: [
10
10
  {
11
- type: "object",
11
+ type: 'object',
12
12
  properties: {
13
- allowEnums: { type: "boolean" },
14
- allowDeclare: { type: "boolean" }
13
+ allowEnums: { type: 'boolean' },
14
+ allowDeclare: { type: 'boolean' },
15
15
  },
16
- additionalProperties: false
17
- }
16
+ additionalProperties: false,
17
+ },
18
18
  ],
19
19
  messages: {
20
- moveToDts: "Type declarations must be defined in a .d.ts file."
21
- }
20
+ moveToDts: 'Type declarations must be defined in a .d.ts file.',
21
+ },
22
22
  },
23
23
  defaultOptions: [
24
24
  {
25
25
  allowEnums: false,
26
- allowDeclare: false
27
- }
26
+ allowDeclare: false,
27
+ },
28
28
  ],
29
29
  create(context, [options]) {
30
30
  const allowEnums = Boolean(options.allowEnums);
31
31
  const allowDeclare = Boolean(options.allowDeclare);
32
32
  function isDtsFile(filename) {
33
- if (!filename || filename === "<input>") {
33
+ if (!filename || filename === '<input>') {
34
34
  return false;
35
35
  }
36
- return filename.endsWith(".d.ts");
36
+ return filename.endsWith('.d.ts');
37
37
  }
38
38
  function hasDeclareModifier(node) {
39
- // `declare type Foo = ...`
40
- if ("declare" in node && node.declare === true) {
39
+ if ('declare' in node && node.declare) {
41
40
  return true;
42
41
  }
43
- const modifiers = "modifiers" in node && Array.isArray(node.modifiers)
42
+ const modifiers = 'modifiers' in node && Array.isArray(node.modifiers)
44
43
  ? node.modifiers
45
44
  : [];
46
- return modifiers.some(m => m?.type === "TSDeclareKeyword");
45
+ return modifiers.some(m => m?.type === 'TSDeclareKeyword');
47
46
  }
48
47
  function reportIfNotDts(node) {
49
48
  const filename = context.getFilename();
@@ -55,7 +54,7 @@ export default ESLintUtils.RuleCreator(() => "https://github.com/tomerh2001/esli
55
54
  }
56
55
  context.report({
57
56
  node,
58
- messageId: "moveToDts"
57
+ messageId: 'moveToDts',
59
58
  });
60
59
  }
61
60
  return {
@@ -70,7 +69,8 @@ export default ESLintUtils.RuleCreator(() => "https://github.com/tomerh2001/esli
70
69
  return;
71
70
  }
72
71
  reportIfNotDts(node);
73
- }
72
+ },
74
73
  };
75
- }
74
+ },
76
75
  });
76
+ export default typesInDts;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",
@@ -44,6 +44,7 @@
44
44
  "devDependencies": {
45
45
  "@codedependant/semantic-release-docker": "^5.1.1",
46
46
  "@eslint/js": "^9.39.2",
47
+ "@leancodepl/resolve-eslint-flat-config": "^9.7.0",
47
48
  "@semantic-release/changelog": "^6.0.3",
48
49
  "@semantic-release/commit-analyzer": "^13.0.1",
49
50
  "@semantic-release/git": "^10.0.1",