eslint-plugin-th-rules 3.2.0 → 3.2.2

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/plugin.d.ts CHANGED
@@ -1,85 +1,85 @@
1
1
  export declare const rules: {
2
- noBooleanCoercion: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ 'no-boolean-coercion': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
3
3
  name: string;
4
4
  };
5
- noComments: import("@typescript-eslint/utils/ts-eslint").RuleModule<"commentNotAllowed", [({
5
+ 'no-comments': import("@typescript-eslint/utils/ts-eslint").RuleModule<"commentNotAllowed", [({
6
6
  allow?: string[];
7
7
  disallow?: string[];
8
8
  } | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
9
9
  name: string;
10
10
  };
11
- noDefaultExport: import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
11
+ 'no-default-export': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
12
12
  name: string;
13
13
  };
14
- noDestructuring: import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
14
+ 'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
15
15
  maximumDestructuredVariables: number;
16
16
  maximumLineLength: number;
17
17
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
18
18
  name: string;
19
19
  };
20
- preferIsEmpty: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
20
+ 'prefer-is-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsEmptyUnary", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
21
21
  name: string;
22
22
  };
23
- schemasInSchemasFile: import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveSchema", [{
23
+ 'schemas-in-schemas-file': import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveSchema", [{
24
24
  allowedSuffixes: string[];
25
25
  onlyWhenAssigned: boolean;
26
26
  allowInTests: boolean;
27
27
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
28
28
  name: string;
29
29
  };
30
- topLevelFunctions: import("@typescript-eslint/utils/ts-eslint").RuleModule<"arrow" | "funcExpr" | "anonDecl", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
30
+ 'top-level-functions': import("@typescript-eslint/utils/ts-eslint").RuleModule<"arrow" | "funcExpr" | "anonDecl", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
31
31
  name: string;
32
32
  };
33
- typesInDts: import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveToDts", [{
33
+ 'types-in-dts': import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveToDts", [{
34
34
  allowEnums: boolean;
35
35
  allowDeclare: boolean;
36
36
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
37
37
  name: string;
38
38
  };
39
- noExplicitNilCompare: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
39
+ 'no-explicit-nil-compare': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
40
40
  name: string;
41
41
  };
42
42
  };
43
43
  declare const plugin: {
44
44
  rules: {
45
- noBooleanCoercion: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
45
+ 'no-boolean-coercion': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
46
46
  name: string;
47
47
  };
48
- noComments: import("@typescript-eslint/utils/ts-eslint").RuleModule<"commentNotAllowed", [({
48
+ 'no-comments': import("@typescript-eslint/utils/ts-eslint").RuleModule<"commentNotAllowed", [({
49
49
  allow?: string[];
50
50
  disallow?: string[];
51
51
  } | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
52
52
  name: string;
53
53
  };
54
- noDefaultExport: import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
54
+ 'no-default-export': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
55
55
  name: string;
56
56
  };
57
- noDestructuring: import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
57
+ 'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
58
58
  maximumDestructuredVariables: number;
59
59
  maximumLineLength: number;
60
60
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
61
61
  name: string;
62
62
  };
63
- preferIsEmpty: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
63
+ 'prefer-is-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsEmptyUnary", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
64
64
  name: string;
65
65
  };
66
- schemasInSchemasFile: import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveSchema", [{
66
+ 'schemas-in-schemas-file': import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveSchema", [{
67
67
  allowedSuffixes: string[];
68
68
  onlyWhenAssigned: boolean;
69
69
  allowInTests: boolean;
70
70
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
71
71
  name: string;
72
72
  };
73
- topLevelFunctions: import("@typescript-eslint/utils/ts-eslint").RuleModule<"arrow" | "funcExpr" | "anonDecl", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
73
+ 'top-level-functions': import("@typescript-eslint/utils/ts-eslint").RuleModule<"arrow" | "funcExpr" | "anonDecl", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
74
74
  name: string;
75
75
  };
76
- typesInDts: import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveToDts", [{
76
+ 'types-in-dts': import("@typescript-eslint/utils/ts-eslint").RuleModule<"moveToDts", [{
77
77
  allowEnums: boolean;
78
78
  allowDeclare: boolean;
79
79
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
80
80
  name: string;
81
81
  };
82
- noExplicitNilCompare: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
82
+ 'no-explicit-nil-compare': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
83
83
  name: string;
84
84
  };
85
85
  };
package/dist/plugin.js CHANGED
@@ -8,15 +8,15 @@ import schemasInSchemasFile from './rules/schemas-in-schemas-file.js';
8
8
  import topLevelFunctions from './rules/top-level-functions.js';
9
9
  import typesInDts from './rules/types-in-dts.js';
10
10
  export const rules = {
11
- noBooleanCoercion,
12
- noComments,
13
- noDefaultExport,
14
- noDestructuring,
15
- preferIsEmpty,
16
- schemasInSchemasFile,
17
- topLevelFunctions,
18
- typesInDts,
19
- noExplicitNilCompare,
11
+ 'no-boolean-coercion': noBooleanCoercion,
12
+ 'no-comments': noComments,
13
+ 'no-default-export': noDefaultExport,
14
+ 'no-destructuring': noDestructuring,
15
+ 'prefer-is-empty': preferIsEmpty,
16
+ 'schemas-in-schemas-file': schemasInSchemasFile,
17
+ 'top-level-functions': topLevelFunctions,
18
+ 'types-in-dts': typesInDts,
19
+ 'no-explicit-nil-compare': noExplicitNilCompare,
20
20
  };
21
21
  const plugin = { rules };
22
22
  export default plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"no-boolean-coercion.d.ts","sourceRoot":"","sources":["../../src/rules/no-boolean-coercion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAItE,QAAA,MAAM,iBAAiB;;CAwFrB,CAAC;AACH,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"no-boolean-coercion.d.ts","sourceRoot":"","sources":["../../src/rules/no-boolean-coercion.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAStF,QAAA,MAAM,iBAAiB;;CAyHrB,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
@@ -1,13 +1,18 @@
1
- import { ESLintUtils } from '@typescript-eslint/utils';
1
+ /* eslint-disable new-cap */
2
+ /* eslint-disable @typescript-eslint/no-unnecessary-condition */
3
+ import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
4
+ import * as ts from 'typescript';
2
5
  const createRule = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-boolean-coercion.md');
6
+ const LODASH_MODULE = 'lodash';
7
+ const LODASH_IDENT = '_';
3
8
  const noBooleanCoercion = createRule({
4
9
  name: 'no-boolean-coercion',
5
10
  meta: {
6
11
  type: 'problem',
12
+ fixable: 'code',
7
13
  docs: {
8
- description: 'Disallow Boolean(value) or !!value. Enforce explicit checks: !_.isNil(value) for scalars and !_.isEmpty(value) for strings, arrays, and objects.',
14
+ description: 'Disallow Boolean(value) or !!value. Enforce explicit checks: !_.isNil(value) for scalars and !_.isEmpty(value) for strings, arrays, and objects. If the value is already boolean, remove coercion.',
9
15
  },
10
- hasSuggestions: true,
11
16
  schema: [],
12
17
  messages: {
13
18
  useIsEmpty: 'Boolean coercion is not allowed. Use !_.isEmpty(value) for strings, arrays, and objects.',
@@ -16,62 +21,85 @@ const noBooleanCoercion = createRule({
16
21
  },
17
22
  defaultOptions: [],
18
23
  create(context) {
19
- const { sourceCode } = context;
20
24
  const services = ESLintUtils.getParserServices(context);
21
- const checker = services?.program?.getTypeChecker?.();
25
+ const checker = services.program.getTypeChecker();
26
+ function hasLodashImport() {
27
+ return context.sourceCode.ast.body.some((node) => node.type === AST_NODE_TYPES.ImportDeclaration && node.source.value === LODASH_MODULE);
28
+ }
29
+ function getLodashImportFixer(fixer) {
30
+ if (hasLodashImport()) {
31
+ return null;
32
+ }
33
+ const firstNode = context.sourceCode.ast.body[0];
34
+ const importText = `import ${LODASH_IDENT} from '${LODASH_MODULE}';\n`;
35
+ if (!firstNode) {
36
+ return fixer.insertTextAfterRange([0, 0], importText);
37
+ }
38
+ return fixer.insertTextBefore(firstNode, importText);
39
+ }
22
40
  function isBooleanCall(node) {
23
- return node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'Boolean' && node.arguments.length === 1;
41
+ return node.type === AST_NODE_TYPES.CallExpression && node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 'Boolean' && node.arguments.length === 1;
24
42
  }
25
43
  function isDoubleNegation(node) {
26
- return node.type === 'UnaryExpression' && node.operator === '!' && node.argument.type === 'UnaryExpression' && node.argument.operator === '!';
44
+ return node.type === AST_NODE_TYPES.UnaryExpression && node.operator === '!' && node.argument.type === AST_NODE_TYPES.UnaryExpression && node.argument.operator === '!';
27
45
  }
28
- function isCollectionLikeByTS(node) {
29
- if (!checker || !services.esTreeNodeToTSNodeMap) {
46
+ function isBooleanByTS(node) {
47
+ const tsNode = services.esTreeNodeToTSNodeMap.get(node);
48
+ if (!tsNode) {
30
49
  return false;
31
50
  }
51
+ const type = checker.getTypeAtLocation(tsNode);
52
+ return (type.flags & ts.TypeFlags.Boolean) !== 0 || (type.flags & ts.TypeFlags.BooleanLiteral) !== 0; // eslint-disable-line no-bitwise
53
+ }
54
+ function isCollectionLikeByTS(node) {
32
55
  const tsNode = services.esTreeNodeToTSNodeMap.get(node);
33
56
  if (!tsNode) {
34
57
  return false;
35
58
  }
36
59
  const type = checker.getTypeAtLocation(tsNode);
37
60
  const typeString = checker.typeToString(type);
38
- return typeString.includes('[]') || typeString === 'string' || typeString === 'object' || typeString.startsWith('Array<') || typeString.startsWith('ReadonlyArray<');
61
+ return typeString === 'string' || typeString === 'object' || typeString.includes('[]') || typeString.startsWith('Array<') || typeString.startsWith('ReadonlyArray<');
39
62
  }
40
63
  function isCollectionLikeBySyntax(node) {
41
- return node.type === 'ArrayExpression' || node.type === 'ObjectExpression' || (node.type === 'Literal' && typeof node.value === 'string');
64
+ return node.type === AST_NODE_TYPES.ArrayExpression || node.type === AST_NODE_TYPES.ObjectExpression || (node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string');
42
65
  }
43
66
  function report(node, valueNode) {
67
+ if (isBooleanByTS(valueNode)) {
68
+ context.report({
69
+ node,
70
+ messageId: 'useIsNil',
71
+ fix(fixer) {
72
+ return fixer.replaceText(node, context.sourceCode.getText(valueNode));
73
+ },
74
+ });
75
+ return;
76
+ }
44
77
  const isCollection = isCollectionLikeBySyntax(valueNode) || isCollectionLikeByTS(valueNode);
45
- const suggestedFn = isCollection ? '_.isEmpty' : '_.isNil';
46
- const replacement = `!${suggestedFn}(${sourceCode.getText(valueNode)})`;
78
+ const fnName = isCollection ? 'isEmpty' : 'isNil';
79
+ const replacement = `!${LODASH_IDENT}.${fnName}(${context.sourceCode.getText(valueNode)})`;
47
80
  context.report({
48
81
  node,
49
82
  messageId: isCollection ? 'useIsEmpty' : 'useIsNil',
50
- suggest: [
51
- {
52
- messageId: isCollection ? 'useIsEmpty' : 'useIsNil',
53
- fix(fixer) {
54
- return fixer.replaceText(node, replacement);
55
- },
56
- },
57
- ],
83
+ fix(fixer) {
84
+ const fixes = [fixer.replaceText(node, replacement)];
85
+ const importFix = getLodashImportFixer(fixer);
86
+ if (importFix) {
87
+ fixes.push(importFix);
88
+ }
89
+ return fixes;
90
+ },
58
91
  });
59
92
  }
60
93
  return {
61
94
  CallExpression(node) {
62
95
  if (isBooleanCall(node)) {
63
- const arg = node.arguments[0];
64
- if (arg) {
65
- report(node, arg);
66
- }
96
+ report(node, node.arguments[0]);
67
97
  }
68
98
  },
69
99
  UnaryExpression(node) {
70
100
  if (isDoubleNegation(node)) {
71
101
  const valueNode = node.argument.argument;
72
- if (valueNode) {
73
- report(node, valueNode);
74
- }
102
+ report(node, valueNode);
75
103
  }
76
104
  },
77
105
  };
@@ -1 +1 @@
1
- {"version":3,"file":"no-default-export.d.ts","sourceRoot":"","sources":["../../src/rules/no-default-export.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAEtE,QAAA,MAAM,eAAe;;CA2DnB,CAAC;AACH,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"no-default-export.d.ts","sourceRoot":"","sources":["../../src/rules/no-default-export.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAEtE,QAAA,MAAM,eAAe;;CA2DnB,CAAC;AACH,eAAe,eAAe,CAAC"}
@@ -1,4 +1,6 @@
1
+ /* eslint-disable new-cap */
1
2
  import * as path from 'node:path';
3
+ import _ from 'lodash';
2
4
  import { ESLintUtils } from '@typescript-eslint/utils';
3
5
  const noDefaultExport = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/no-default-export.md')({
4
6
  name: 'no-default-export',
@@ -29,7 +31,7 @@ const noDefaultExport = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
29
31
  if (node.declaration.type === 'Identifier') {
30
32
  return;
31
33
  }
32
- if ('id' in node.declaration && node.declaration.id != null) {
34
+ if ('id' in node.declaration && !_.isNull(node.declaration.id)) {
33
35
  return;
34
36
  }
35
37
  const fileName = context.getFilename();
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const preferIsEmpty: ESLintUtils.RuleModule<"useIsEmpty", [], unknown, ESLintUtils.RuleListener> & {
2
+ declare const preferIsEmpty: ESLintUtils.RuleModule<"useIsEmpty" | "useIsEmptyUnary", [], unknown, ESLintUtils.RuleListener> & {
3
3
  name: string;
4
4
  };
5
5
  export default preferIsEmpty;
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-is-empty.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-is-empty.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAEtF,QAAA,MAAM,aAAa;;CAoFjB,CAAC;AACH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"prefer-is-empty.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-is-empty.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,WAAW,EAAgC,MAAM,0BAA0B,CAAC;AAIrG,QAAA,MAAM,aAAa;;CAiHjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -1,76 +1,100 @@
1
+ /* eslint-disable th-rules/types-in-dts */
1
2
  /* eslint-disable new-cap */
2
3
  /* eslint-disable complexity */
4
+ import _ from 'lodash';
3
5
  import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
4
6
  const preferIsEmpty = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/prefer-is-empty.md')({
5
7
  name: 'prefer-is-empty',
6
8
  meta: {
7
9
  type: 'problem',
8
10
  docs: {
9
- description: 'Require _.isEmpty instead of length comparisons.',
11
+ description: 'Require _.isEmpty instead of length comparisons or !x.length checks.',
10
12
  },
11
- hasSuggestions: true,
13
+ fixable: 'code',
12
14
  schema: [],
13
15
  messages: {
14
16
  useIsEmpty: 'Use _.isEmpty({{collection}}) instead of checking {{collection}}.length {{operator}} {{value}}.',
17
+ useIsEmptyUnary: 'Use _.isEmpty({{collection}}) instead of negating {{collection}}.length.',
15
18
  },
16
19
  },
17
20
  defaultOptions: [],
18
21
  create(context) {
22
+ const { sourceCode } = context;
23
+ function ensureLodashImport(fixer) {
24
+ const imports = sourceCode.ast.body.filter((node) => node.type === AST_NODE_TYPES.ImportDeclaration);
25
+ const hasLodash = imports.some((imp) => imp.source.value === 'lodash' && imp.specifiers.some((s) => s.type === AST_NODE_TYPES.ImportDefaultSpecifier || s.type === AST_NODE_TYPES.ImportNamespaceSpecifier));
26
+ if (hasLodash)
27
+ return null;
28
+ const firstImport = imports[0];
29
+ return firstImport ? fixer.insertTextBefore(firstImport, `import _ from 'lodash';\n`) : fixer.insertTextBeforeRange([0, 0], `import _ from 'lodash';\n`);
30
+ }
19
31
  function isLengthAccess(node) {
20
- return node?.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === 'length' && !node.computed;
32
+ return !_.isNil(node) && node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier && node.property.name === 'length' && !node.computed;
21
33
  }
22
34
  function isNumericLiteral(node) {
23
- return node?.type === AST_NODE_TYPES.Literal && typeof node.value === 'number';
35
+ return !_.isNil(node) && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'number';
24
36
  }
25
- function report(node, collectionNode, operator, value, isEmptyCheck) {
26
- const collectionText = context.sourceCode.getText(collectionNode.object);
27
- const replacement = isEmptyCheck ? `_.isEmpty(${collectionText})` : `!_.isEmpty(${collectionText})`;
37
+ function reportBinary(node, lengthNode, operator, value, isEmptyCheck) {
38
+ const collection = sourceCode.getText(lengthNode.object);
39
+ const replacement = isEmptyCheck ? `_.isEmpty(${collection})` : `!_.isEmpty(${collection})`;
28
40
  context.report({
29
41
  node,
30
42
  messageId: 'useIsEmpty',
31
- data: {
32
- collection: collectionText,
33
- operator,
34
- value,
43
+ data: { collection, operator, value },
44
+ fix(fixer) {
45
+ const fixes = [fixer.replaceText(node, replacement)];
46
+ const importFix = ensureLodashImport(fixer);
47
+ if (importFix)
48
+ fixes.push(importFix);
49
+ return fixes;
50
+ },
51
+ });
52
+ }
53
+ function reportUnary(node, lengthNode) {
54
+ const collection = sourceCode.getText(lengthNode.object);
55
+ context.report({
56
+ node,
57
+ messageId: 'useIsEmptyUnary',
58
+ data: { collection },
59
+ fix(fixer) {
60
+ const fixes = [fixer.replaceText(node, `_.isEmpty(${collection})`)];
61
+ const importFix = ensureLodashImport(fixer);
62
+ if (importFix)
63
+ fixes.push(importFix);
64
+ return fixes;
35
65
  },
36
- suggest: [
37
- {
38
- messageId: 'useIsEmpty',
39
- data: {
40
- collection: collectionText,
41
- operator,
42
- value,
43
- },
44
- fix(fixer) {
45
- return fixer.replaceText(node, replacement);
46
- },
47
- },
48
- ],
49
66
  });
50
67
  }
51
68
  return {
52
69
  BinaryExpression(node) {
53
70
  if (isLengthAccess(node.left) && isNumericLiteral(node.right)) {
54
71
  if ((node.operator === '===' && node.right.value === 0) || (node.operator === '<=' && node.right.value === 0) || (node.operator === '<' && node.right.value === 1)) {
55
- report(node, node.left, node.operator, node.right.value, true);
72
+ reportBinary(node, node.left, node.operator, node.right.value, true);
56
73
  return;
57
74
  }
58
75
  if ((node.operator === '>' && node.right.value === 0) ||
59
76
  (node.operator === '>=' && node.right.value === 1) ||
60
77
  ((node.operator === '!=' || node.operator === '!==') && node.right.value === 0)) {
61
- report(node, node.left, node.operator, node.right.value, false);
78
+ reportBinary(node, node.left, node.operator, node.right.value, false);
62
79
  }
63
80
  }
64
81
  if (isNumericLiteral(node.left) && isLengthAccess(node.right)) {
65
82
  if ((node.operator === '===' && node.left.value === 0) || (node.operator === '>=' && node.left.value === 0) || (node.operator === '>' && node.left.value === 0)) {
66
- report(node, node.right, node.operator, node.left.value, true);
83
+ reportBinary(node, node.right, node.operator, node.left.value, true);
67
84
  return;
68
85
  }
69
86
  if ((node.operator === '<' && node.left.value === 1) || (node.operator === '<=' && node.left.value === 0)) {
70
- report(node, node.right, node.operator, node.left.value, false);
87
+ reportBinary(node, node.right, node.operator, node.left.value, false);
71
88
  }
72
89
  }
73
90
  },
91
+ UnaryExpression(node) {
92
+ if (node.operator !== '!')
93
+ return;
94
+ if (isLengthAccess(node.argument)) {
95
+ reportUnary(node, node.argument);
96
+ }
97
+ },
74
98
  };
75
99
  },
76
100
  });
@@ -1 +1 @@
1
- {"version":3,"file":"types-in-dts.d.ts","sourceRoot":"","sources":["../../src/rules/types-in-dts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAStE,QAAA,MAAM,UAAU;;;;;CAuFd,CAAC;AACH,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"types-in-dts.d.ts","sourceRoot":"","sources":["../../src/rules/types-in-dts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAEtE,QAAA,MAAM,UAAU;;;;;CAkFd,CAAC;AACH,eAAe,UAAU,CAAC"}
@@ -1,3 +1,4 @@
1
+ /* eslint-disable new-cap */
1
2
  import { ESLintUtils } from '@typescript-eslint/utils';
2
3
  const typesInDts = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/eslint-plugin-th-rules/blob/main/docs/rules/types-in-dts.md')({
3
4
  name: 'types-in-dts',
@@ -27,8 +28,6 @@ const typesInDts = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/
27
28
  },
28
29
  ],
29
30
  create(context, [options]) {
30
- const allowEnums = Boolean(options.allowEnums);
31
- const allowDeclare = Boolean(options.allowDeclare);
32
31
  function isDtsFile(filename) {
33
32
  if (!filename || filename === '<input>') {
34
33
  return false;
@@ -43,11 +42,10 @@ const typesInDts = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/
43
42
  return modifiers.some((m) => m?.type === 'TSDeclareKeyword');
44
43
  }
45
44
  function reportIfNotDts(node) {
46
- const filename = context.getFilename();
47
- if (isDtsFile(filename)) {
45
+ if (isDtsFile(context.filename)) {
48
46
  return;
49
47
  }
50
- if (allowDeclare && hasDeclareModifier(node)) {
48
+ if (options.allowDeclare && hasDeclareModifier(node)) {
51
49
  return;
52
50
  }
53
51
  context.report({
@@ -63,7 +61,7 @@ const typesInDts = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh2001/
63
61
  reportIfNotDts(node);
64
62
  },
65
63
  TSEnumDeclaration(node) {
66
- if (allowEnums) {
64
+ if (options.allowEnums) {
67
65
  return;
68
66
  }
69
67
  reportIfNotDts(node);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",