eslint-plugin-th-rules 3.2.0 → 3.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAC;AAQhD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,eAAO,MAAM,OAAO;;;;;CAKnB,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E,QAAA,MAAM,KAAK,EAAoC;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC;AACpI,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAC;AAQhD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,eAAO,MAAM,OAAO;;;;;CAKnB,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E,QAAA,MAAM,KAAK,EAAoC;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC;AACpI,eAAe,KAAK,CAAC"}
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", [], 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", [], 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":"AAGA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAQtF,QAAA,MAAM,iBAAiB;;CAmGrB,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
@@ -1,13 +1,18 @@
1
- import { ESLintUtils } from '@typescript-eslint/utils';
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable new-cap */
3
+ /* eslint-disable @typescript-eslint/no-unnecessary-condition */
4
+ import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
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
14
  description: 'Disallow Boolean(value) or !!value. Enforce explicit checks: !_.isNil(value) for scalars and !_.isEmpty(value) for strings, arrays, and objects.',
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,67 @@ 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
46
  function isCollectionLikeByTS(node) {
29
- if (!checker || !services.esTreeNodeToTSNodeMap) {
30
- return false;
31
- }
32
47
  const tsNode = services.esTreeNodeToTSNodeMap.get(node);
33
48
  if (!tsNode) {
34
49
  return false;
35
50
  }
36
51
  const type = checker.getTypeAtLocation(tsNode);
37
52
  const typeString = checker.typeToString(type);
38
- return typeString.includes('[]') || typeString === 'string' || typeString === 'object' || typeString.startsWith('Array<') || typeString.startsWith('ReadonlyArray<');
53
+ return typeString === 'string' || typeString === 'object' || typeString.includes('[]') || typeString.startsWith('Array<') || typeString.startsWith('ReadonlyArray<');
39
54
  }
40
55
  function isCollectionLikeBySyntax(node) {
41
- return node.type === 'ArrayExpression' || node.type === 'ObjectExpression' || (node.type === 'Literal' && typeof node.value === 'string');
56
+ return node.type === AST_NODE_TYPES.ArrayExpression || node.type === AST_NODE_TYPES.ObjectExpression || (node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string');
42
57
  }
43
58
  function report(node, valueNode) {
44
59
  const isCollection = isCollectionLikeBySyntax(valueNode) || isCollectionLikeByTS(valueNode);
45
- const suggestedFn = isCollection ? '_.isEmpty' : '_.isNil';
46
- const replacement = `!${suggestedFn}(${sourceCode.getText(valueNode)})`;
60
+ const fnName = isCollection ? 'isEmpty' : 'isNil';
61
+ const replacement = `!${LODASH_IDENT}.${fnName}(${context.sourceCode.getText(valueNode)})`;
47
62
  context.report({
48
63
  node,
49
64
  messageId: isCollection ? 'useIsEmpty' : 'useIsNil',
50
- suggest: [
51
- {
52
- messageId: isCollection ? 'useIsEmpty' : 'useIsNil',
53
- fix(fixer) {
54
- return fixer.replaceText(node, replacement);
55
- },
56
- },
57
- ],
65
+ fix(fixer) {
66
+ const fixes = [fixer.replaceText(node, replacement)];
67
+ const importFix = getLodashImportFixer(fixer);
68
+ if (importFix) {
69
+ fixes.push(importFix);
70
+ }
71
+ return fixes;
72
+ },
58
73
  });
59
74
  }
60
75
  return {
61
76
  CallExpression(node) {
62
77
  if (isBooleanCall(node)) {
63
- const arg = node.arguments[0];
64
- if (arg) {
65
- report(node, arg);
66
- }
78
+ report(node, node.arguments[0]);
67
79
  }
68
80
  },
69
81
  UnaryExpression(node) {
70
82
  if (isDoubleNegation(node)) {
71
83
  const valueNode = node.argument.argument;
72
- if (valueNode) {
73
- report(node, valueNode);
74
- }
84
+ report(node, valueNode);
75
85
  }
76
86
  },
77
87
  };
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.1",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",