eslint-plugin-th-rules 3.3.1 → 3.3.3

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/README.md CHANGED
@@ -172,8 +172,9 @@ Do not edit below this line.
172
172
  | [no-comments](docs/rules/no-comments.md) | Disallow comments except for specified allowed patterns. | ✅ ⚛️ 🟦 🎲 | 🔧 |
173
173
  | [no-default-export](docs/rules/no-default-export.md) | Convert unnamed default exports to named default exports based on the file name. | ✅ ⚛️ 🟦 🎲 | 🔧 |
174
174
  | [no-destructuring](docs/rules/no-destructuring.md) | Disallow destructuring that does not meet certain conditions. | ✅ ⚛️ 🟦 🎲 | |
175
+ | [no-explicit-nil-check](docs/rules/no-explicit-nil-check.md) | Disallow implicit truthy/falsy checks anywhere. Require explicit _.isNil(value). | ✅ ⚛️ 🟦 🎲 | 🔧 |
175
176
  | [no-explicit-nil-compare](docs/rules/no-explicit-nil-compare.md) | Disallow direct comparisons to null or undefined. Use _.isNull(x) / _.isUndefined(x) instead. | ✅ ⚛️ 🟦 🎲 | 🔧 |
176
- | [prefer-is-empty](docs/rules/prefer-is-empty.md) | Require _.isEmpty instead of length comparisons or !x.length checks. | ✅ ⚛️ 🟦 🎲 | 🔧 |
177
+ | [prefer-is-empty](docs/rules/prefer-is-empty.md) | Require _.isEmpty instead of length comparisons or boolean checks on .length. | ✅ ⚛️ 🟦 🎲 | 🔧 |
177
178
  | [schemas-in-schemas-file](docs/rules/schemas-in-schemas-file.md) | Require Zod schema declarations to be placed in a .schemas.ts file. | ✅ ⚛️ 🟦 🎲 | |
178
179
  | [top-level-functions](docs/rules/top-level-functions.md) | Require all top-level functions to be named regular functions. | ✅ ⚛️ 🟦 🎲 | 🔧 |
179
180
  | [types-in-dts](docs/rules/types-in-dts.md) | Require TypeScript type declarations (type/interface/enum) to be placed in .d.ts files. | ✅ ⚛️ 🟦 🎲 | |
package/dist/plugin.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare const rules: {
20
20
  'no-explicit-nil-compare': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
21
21
  name: string;
22
22
  };
23
- 'no-explicit-nil-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
23
+ 'prefer-explicit-nil-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
24
24
  name: string;
25
25
  };
26
26
  'prefer-is-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsEmptyUnary" | "useIsEmptyBoolean", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
@@ -66,7 +66,7 @@ declare const plugin: {
66
66
  'no-explicit-nil-compare': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNull" | "useIsUndefined", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
67
67
  name: string;
68
68
  };
69
- 'no-explicit-nil-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
69
+ 'prefer-explicit-nil-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNil", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
70
70
  name: string;
71
71
  };
72
72
  'prefer-is-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsEmptyUnary" | "useIsEmptyBoolean", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
package/dist/plugin.js CHANGED
@@ -14,7 +14,7 @@ export const rules = {
14
14
  'no-default-export': noDefaultExport,
15
15
  'no-destructuring': noDestructuring,
16
16
  'no-explicit-nil-compare': noExplicitNilCompare,
17
- 'no-explicit-nil-check': preferExplicitNilCheck,
17
+ 'prefer-explicit-nil-check': preferExplicitNilCheck,
18
18
  'prefer-is-empty': preferIsEmpty,
19
19
  'schemas-in-schemas-file': schemasInSchemasFile,
20
20
  'top-level-functions': topLevelFunctions,
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-explicit-nil-check.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-explicit-nil-check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAWtF,QAAA,MAAM,sBAAsB;;CAoW1B,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"prefer-explicit-nil-check.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-explicit-nil-check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAWtF,QAAA,MAAM,sBAAsB;;CA0Z1B,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
@@ -57,23 +57,46 @@ const preferExplicitNilCheck = createRule({
57
57
  return (flags & ts.TypeFlags.Null) !== 0 || (flags & ts.TypeFlags.Undefined) !== 0;
58
58
  }
59
59
  function isStringLikeFlag(flags) {
60
- return (flags & ts.TypeFlags.String) !== 0 || (flags & ts.TypeFlags.StringLiteral) !== 0;
60
+ return (flags & ts.TypeFlags.StringLike) !== 0;
61
61
  }
62
62
  function isNumberLikeFlag(flags) {
63
- return (flags & ts.TypeFlags.Number) !== 0 || (flags & ts.TypeFlags.NumberLiteral) !== 0;
63
+ return (flags & ts.TypeFlags.NumberLike) !== 0;
64
+ }
65
+ function isBooleanLikeFlag(flags) {
66
+ return (flags & ts.TypeFlags.BooleanLike) !== 0;
67
+ }
68
+ function isAnyOrUnknownFlag(flags) {
69
+ return (flags & ts.TypeFlags.Any) !== 0 || (flags & ts.TypeFlags.Unknown) !== 0;
70
+ }
71
+ /**
72
+ * Skip when the type is `any` or `unknown` (including union constituents),
73
+ * because we can't safely decide whether to prefer isNil/isEmpty without
74
+ * risking semantic changes.
75
+ */
76
+ function isAnyOrUnknownByTS(node) {
77
+ const type = getTsType(node);
78
+ if (_.isNil(type))
79
+ return false;
80
+ if (!type.isUnion()) {
81
+ return isAnyOrUnknownFlag(type.getFlags());
82
+ }
83
+ for (const t of type.types) {
84
+ if (isAnyOrUnknownFlag(t.getFlags()))
85
+ return true;
86
+ }
87
+ return false;
64
88
  }
65
89
  /**
66
90
  * Returns true iff the expression type is effectively:
67
91
  * string | null | undefined
68
- * (i.e., all non-nullish constituents are string/string-literal).
92
+ * (i.e., all non-nullish constituents are string/string-like).
69
93
  */
70
94
  function isStringByTS(node) {
71
95
  const type = getTsType(node);
72
96
  if (_.isNil(type))
73
97
  return false;
74
98
  if (!type.isUnion()) {
75
- const flags = type.getFlags();
76
- return isStringLikeFlag(flags);
99
+ return isStringLikeFlag(type.getFlags());
77
100
  }
78
101
  let sawNonNullish = false;
79
102
  for (const t of type.types) {
@@ -98,8 +121,7 @@ const preferExplicitNilCheck = createRule({
98
121
  if (_.isNil(type))
99
122
  return false;
100
123
  if (!type.isUnion()) {
101
- const flags = type.getFlags();
102
- return isNumberLikeFlag(flags);
124
+ return isNumberLikeFlag(type.getFlags());
103
125
  }
104
126
  let sawNonNullish = false;
105
127
  for (const t of type.types) {
@@ -112,12 +134,33 @@ const preferExplicitNilCheck = createRule({
112
134
  }
113
135
  return sawNonNullish;
114
136
  }
137
+ /**
138
+ * Returns true iff the expression type is effectively:
139
+ * boolean | null | undefined
140
+ * (i.e., all non-nullish constituents are boolean/boolean-literal).
141
+ *
142
+ * This ensures we do NOT rewrite checks like:
143
+ * - boolean | undefined
144
+ * - boolean | null
145
+ * - boolean | null | undefined
146
+ */
115
147
  function isBooleanByTS(node) {
116
148
  const type = getTsType(node);
117
149
  if (_.isNil(type))
118
150
  return false;
119
- const flags = type.getFlags();
120
- return (flags & ts.TypeFlags.Boolean) !== 0 || (flags & ts.TypeFlags.BooleanLiteral) !== 0;
151
+ if (!type.isUnion()) {
152
+ return isBooleanLikeFlag(type.getFlags());
153
+ }
154
+ let sawNonNullish = false;
155
+ for (const t of type.types) {
156
+ const flags = t.getFlags();
157
+ if (isNullableFlag(flags))
158
+ continue;
159
+ sawNonNullish = true;
160
+ if (!isBooleanLikeFlag(flags))
161
+ return false;
162
+ }
163
+ return sawNonNullish;
121
164
  }
122
165
  function isAlreadyExplicitCheck(node) {
123
166
  return (node.type === AST_NODE_TYPES.CallExpression &&
@@ -154,6 +197,8 @@ const preferExplicitNilCheck = createRule({
154
197
  });
155
198
  }
156
199
  function transformTruthy(node) {
200
+ if (isAnyOrUnknownByTS(node))
201
+ return;
157
202
  if (isNumberByTS(node))
158
203
  return;
159
204
  const text = context.sourceCode.getText(node);
@@ -165,6 +210,8 @@ const preferExplicitNilCheck = createRule({
165
210
  }
166
211
  function transformFalsyUnary(node) {
167
212
  const arg = node.argument;
213
+ if (isAnyOrUnknownByTS(arg))
214
+ return;
168
215
  if (isNumberByTS(arg))
169
216
  return;
170
217
  const text = context.sourceCode.getText(arg);
@@ -255,6 +302,8 @@ const preferExplicitNilCheck = createRule({
255
302
  return;
256
303
  }
257
304
  if (isImplicitOperand(arg)) {
305
+ if (isAnyOrUnknownByTS(arg))
306
+ return;
258
307
  if (isBooleanByTS(arg))
259
308
  return;
260
309
  if (isNumberByTS(arg))
@@ -271,6 +320,8 @@ const preferExplicitNilCheck = createRule({
271
320
  }
272
321
  default: {
273
322
  if (mode === 'test' && isImplicitOperand(node)) {
323
+ if (isAnyOrUnknownByTS(node))
324
+ return;
274
325
  if (isBooleanByTS(node))
275
326
  return;
276
327
  if (isNumberByTS(node))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "3.3.1",
3
+ "version": "3.3.3",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",