eslint-plugin-th-rules 3.3.0 → 3.3.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.
@@ -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;;CA2P1B,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;;CA4X1B,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
@@ -12,11 +12,11 @@ const preferExplicitNilCheck = createRule({
12
12
  type: 'problem',
13
13
  fixable: 'code',
14
14
  docs: {
15
- description: 'Disallow implicit truthy/falsy checks anywhere. Require explicit _.isNil(value).',
15
+ description: 'Disallow implicit truthy/falsy checks in boolean-test positions. Prefer explicit _.isNil(value) or _.isEmpty(value) (depending on the value type).',
16
16
  },
17
17
  schema: [],
18
18
  messages: {
19
- useIsNil: 'Implicit truthy/falsy checks are not allowed. Use _.isNil(value) or !_.isNil(value).',
19
+ useIsNil: 'Implicit truthy/falsy checks are not allowed. Use _.isNil(value) / !_.isNil(value) or _.isEmpty(value) / !_.isEmpty(value).',
20
20
  },
21
21
  },
22
22
  defaultOptions: [],
@@ -41,12 +41,105 @@ const preferExplicitNilCheck = createRule({
41
41
  }
42
42
  return fixer.insertTextBefore(firstNode, importText);
43
43
  }
44
- function isBooleanByTS(node) {
45
- const tsNode = services.esTreeNodeToTSNodeMap.get(node);
44
+ function unwrapChainExpression(node) {
45
+ if (node.type === AST_NODE_TYPES.ChainExpression)
46
+ return node.expression;
47
+ return node;
48
+ }
49
+ function getTsType(node) {
50
+ const unwrapped = unwrapChainExpression(node);
51
+ const tsNode = services.esTreeNodeToTSNodeMap.get(unwrapped);
46
52
  if (_.isNil(tsNode))
53
+ return null;
54
+ return checker.getTypeAtLocation(tsNode);
55
+ }
56
+ function isNullableFlag(flags) {
57
+ return (flags & ts.TypeFlags.Null) !== 0 || (flags & ts.TypeFlags.Undefined) !== 0;
58
+ }
59
+ function isStringLikeFlag(flags) {
60
+ return (flags & ts.TypeFlags.StringLike) !== 0;
61
+ }
62
+ function isNumberLikeFlag(flags) {
63
+ return (flags & ts.TypeFlags.NumberLike) !== 0;
64
+ }
65
+ function isBooleanLikeFlag(flags) {
66
+ return (flags & ts.TypeFlags.BooleanLike) !== 0;
67
+ }
68
+ /**
69
+ * Returns true iff the expression type is effectively:
70
+ * string | null | undefined
71
+ * (i.e., all non-nullish constituents are string/string-like).
72
+ */
73
+ function isStringByTS(node) {
74
+ const type = getTsType(node);
75
+ if (_.isNil(type))
76
+ return false;
77
+ if (!type.isUnion()) {
78
+ return isStringLikeFlag(type.getFlags());
79
+ }
80
+ let sawNonNullish = false;
81
+ for (const t of type.types) {
82
+ const flags = t.getFlags();
83
+ if (isNullableFlag(flags))
84
+ continue;
85
+ sawNonNullish = true;
86
+ if (!isStringLikeFlag(flags))
87
+ return false;
88
+ }
89
+ return sawNonNullish;
90
+ }
91
+ /**
92
+ * Returns true iff the expression type is effectively:
93
+ * number | null | undefined
94
+ *
95
+ * We intentionally DO NOT auto-fix numeric truthy/falsy checks because
96
+ * converting `if (n)` into `!_.isNil(n)` changes semantics for `0`.
97
+ */
98
+ function isNumberByTS(node) {
99
+ const type = getTsType(node);
100
+ if (_.isNil(type))
101
+ return false;
102
+ if (!type.isUnion()) {
103
+ return isNumberLikeFlag(type.getFlags());
104
+ }
105
+ let sawNonNullish = false;
106
+ for (const t of type.types) {
107
+ const flags = t.getFlags();
108
+ if (isNullableFlag(flags))
109
+ continue;
110
+ sawNonNullish = true;
111
+ if (!isNumberLikeFlag(flags))
112
+ return false;
113
+ }
114
+ return sawNonNullish;
115
+ }
116
+ /**
117
+ * Returns true iff the expression type is effectively:
118
+ * boolean | null | undefined
119
+ * (i.e., all non-nullish constituents are boolean/boolean-literal).
120
+ *
121
+ * This ensures we do NOT rewrite checks like:
122
+ * - boolean | undefined
123
+ * - boolean | null
124
+ * - boolean | null | undefined
125
+ */
126
+ function isBooleanByTS(node) {
127
+ const type = getTsType(node);
128
+ if (_.isNil(type))
47
129
  return false;
48
- const type = checker.getTypeAtLocation(tsNode);
49
- return (type.flags & ts.TypeFlags.Boolean) !== 0 || (type.flags & ts.TypeFlags.BooleanLiteral) !== 0;
130
+ if (!type.isUnion()) {
131
+ return isBooleanLikeFlag(type.getFlags());
132
+ }
133
+ let sawNonNullish = false;
134
+ for (const t of type.types) {
135
+ const flags = t.getFlags();
136
+ if (isNullableFlag(flags))
137
+ continue;
138
+ sawNonNullish = true;
139
+ if (!isBooleanLikeFlag(flags))
140
+ return false;
141
+ }
142
+ return sawNonNullish;
50
143
  }
51
144
  function isAlreadyExplicitCheck(node) {
52
145
  return (node.type === AST_NODE_TYPES.CallExpression &&
@@ -62,8 +155,7 @@ const preferExplicitNilCheck = createRule({
62
155
  if (node.type === AST_NODE_TYPES.MemberExpression)
63
156
  return true;
64
157
  if (node.type === AST_NODE_TYPES.ChainExpression) {
65
- const inner = node.expression;
66
- return inner.type === AST_NODE_TYPES.MemberExpression;
158
+ return node.expression.type === AST_NODE_TYPES.MemberExpression;
67
159
  }
68
160
  return false;
69
161
  }
@@ -84,12 +176,24 @@ const preferExplicitNilCheck = createRule({
84
176
  });
85
177
  }
86
178
  function transformTruthy(node) {
179
+ if (isNumberByTS(node))
180
+ return;
87
181
  const text = context.sourceCode.getText(node);
182
+ if (isStringByTS(node)) {
183
+ reportFull(node, `!${LODASH_IDENT}.isEmpty(${text})`);
184
+ return;
185
+ }
88
186
  reportFull(node, `!${LODASH_IDENT}.isNil(${text})`);
89
187
  }
90
188
  function transformFalsyUnary(node) {
91
189
  const arg = node.argument;
190
+ if (isNumberByTS(arg))
191
+ return;
92
192
  const text = context.sourceCode.getText(arg);
193
+ if (isStringByTS(arg)) {
194
+ reportFull(node, `${LODASH_IDENT}.isEmpty(${text})`);
195
+ return;
196
+ }
93
197
  reportFull(node, `${LODASH_IDENT}.isNil(${text})`);
94
198
  }
95
199
  function expressionHasSideEffects(node) {
@@ -175,6 +279,8 @@ const preferExplicitNilCheck = createRule({
175
279
  if (isImplicitOperand(arg)) {
176
280
  if (isBooleanByTS(arg))
177
281
  return;
282
+ if (isNumberByTS(arg))
283
+ return;
178
284
  transformFalsyUnary(node);
179
285
  return;
180
286
  }
@@ -189,6 +295,8 @@ const preferExplicitNilCheck = createRule({
189
295
  if (mode === 'test' && isImplicitOperand(node)) {
190
296
  if (isBooleanByTS(node))
191
297
  return;
298
+ if (isNumberByTS(node))
299
+ return;
192
300
  transformTruthy(node);
193
301
  }
194
302
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-th-rules",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",