eslint-plugin-th-rules 3.3.0 → 3.3.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":"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;;CAoW1B,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,83 @@ 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.String) !== 0 || (flags & ts.TypeFlags.StringLiteral) !== 0;
61
+ }
62
+ function isNumberLikeFlag(flags) {
63
+ return (flags & ts.TypeFlags.Number) !== 0 || (flags & ts.TypeFlags.NumberLiteral) !== 0;
64
+ }
65
+ /**
66
+ * Returns true iff the expression type is effectively:
67
+ * string | null | undefined
68
+ * (i.e., all non-nullish constituents are string/string-literal).
69
+ */
70
+ function isStringByTS(node) {
71
+ const type = getTsType(node);
72
+ if (_.isNil(type))
47
73
  return false;
48
- const type = checker.getTypeAtLocation(tsNode);
49
- return (type.flags & ts.TypeFlags.Boolean) !== 0 || (type.flags & ts.TypeFlags.BooleanLiteral) !== 0;
74
+ if (!type.isUnion()) {
75
+ const flags = type.getFlags();
76
+ return isStringLikeFlag(flags);
77
+ }
78
+ let sawNonNullish = false;
79
+ for (const t of type.types) {
80
+ const flags = t.getFlags();
81
+ if (isNullableFlag(flags))
82
+ continue;
83
+ sawNonNullish = true;
84
+ if (!isStringLikeFlag(flags))
85
+ return false;
86
+ }
87
+ return sawNonNullish;
88
+ }
89
+ /**
90
+ * Returns true iff the expression type is effectively:
91
+ * number | null | undefined
92
+ *
93
+ * We intentionally DO NOT auto-fix numeric truthy/falsy checks because
94
+ * converting `if (n)` into `!_.isNil(n)` changes semantics for `0`.
95
+ */
96
+ function isNumberByTS(node) {
97
+ const type = getTsType(node);
98
+ if (_.isNil(type))
99
+ return false;
100
+ if (!type.isUnion()) {
101
+ const flags = type.getFlags();
102
+ return isNumberLikeFlag(flags);
103
+ }
104
+ let sawNonNullish = false;
105
+ for (const t of type.types) {
106
+ const flags = t.getFlags();
107
+ if (isNullableFlag(flags))
108
+ continue;
109
+ sawNonNullish = true;
110
+ if (!isNumberLikeFlag(flags))
111
+ return false;
112
+ }
113
+ return sawNonNullish;
114
+ }
115
+ function isBooleanByTS(node) {
116
+ const type = getTsType(node);
117
+ if (_.isNil(type))
118
+ return false;
119
+ const flags = type.getFlags();
120
+ return (flags & ts.TypeFlags.Boolean) !== 0 || (flags & ts.TypeFlags.BooleanLiteral) !== 0;
50
121
  }
51
122
  function isAlreadyExplicitCheck(node) {
52
123
  return (node.type === AST_NODE_TYPES.CallExpression &&
@@ -62,8 +133,7 @@ const preferExplicitNilCheck = createRule({
62
133
  if (node.type === AST_NODE_TYPES.MemberExpression)
63
134
  return true;
64
135
  if (node.type === AST_NODE_TYPES.ChainExpression) {
65
- const inner = node.expression;
66
- return inner.type === AST_NODE_TYPES.MemberExpression;
136
+ return node.expression.type === AST_NODE_TYPES.MemberExpression;
67
137
  }
68
138
  return false;
69
139
  }
@@ -84,12 +154,24 @@ const preferExplicitNilCheck = createRule({
84
154
  });
85
155
  }
86
156
  function transformTruthy(node) {
157
+ if (isNumberByTS(node))
158
+ return;
87
159
  const text = context.sourceCode.getText(node);
160
+ if (isStringByTS(node)) {
161
+ reportFull(node, `!${LODASH_IDENT}.isEmpty(${text})`);
162
+ return;
163
+ }
88
164
  reportFull(node, `!${LODASH_IDENT}.isNil(${text})`);
89
165
  }
90
166
  function transformFalsyUnary(node) {
91
167
  const arg = node.argument;
168
+ if (isNumberByTS(arg))
169
+ return;
92
170
  const text = context.sourceCode.getText(arg);
171
+ if (isStringByTS(arg)) {
172
+ reportFull(node, `${LODASH_IDENT}.isEmpty(${text})`);
173
+ return;
174
+ }
93
175
  reportFull(node, `${LODASH_IDENT}.isNil(${text})`);
94
176
  }
95
177
  function expressionHasSideEffects(node) {
@@ -175,6 +257,8 @@ const preferExplicitNilCheck = createRule({
175
257
  if (isImplicitOperand(arg)) {
176
258
  if (isBooleanByTS(arg))
177
259
  return;
260
+ if (isNumberByTS(arg))
261
+ return;
178
262
  transformFalsyUnary(node);
179
263
  return;
180
264
  }
@@ -189,6 +273,8 @@ const preferExplicitNilCheck = createRule({
189
273
  if (mode === 'test' && isImplicitOperand(node)) {
190
274
  if (isBooleanByTS(node))
191
275
  return;
276
+ if (isNumberByTS(node))
277
+ return;
192
278
  transformTruthy(node);
193
279
  }
194
280
  }
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.1",
4
4
  "description": "A List of custom ESLint rules created by Tomer Horowitz",
5
5
  "keywords": [
6
6
  "eslint",