@kensio/smartass 1.24.0 → 1.26.0

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
@@ -161,3 +161,28 @@ structure.
161
161
  - [typeTypedArray](src/assert/type-typed-array/type-typed-array.match.ts)
162
162
  - [uuidV4](src/assert/uuid/uuid-v4.match.ts)
163
163
  <!-- matcher-functions:end -->
164
+
165
+ ## Optional ESLint config
166
+
167
+ The `@kensio/smartass` package also exports an optional ESLint flat config that discourages less
168
+ specific assertion usage. For example:
169
+
170
+ ```typescript
171
+ assertIdentical(foo.length, 2);
172
+ // suggested improvement:
173
+ assertArrayLength(foo, 2);
174
+ ```
175
+
176
+ You can use this in your ESLint config like this:
177
+
178
+ ```typescript
179
+ import { defineConfig } from "eslint/config";
180
+ import tseslint from "typescript-eslint";
181
+
182
+ import { smartassPreferSpecificAssertions } from "@kensio/smartass/eslint";
183
+
184
+ export default defineConfig(
185
+ ...tseslint.configs.recommended,
186
+ ...smartassPreferSpecificAssertions,
187
+ );
188
+ ```
@@ -0,0 +1,2 @@
1
+ export declare const smartassPreferSpecificAssertions: import("eslint/config").Config[];
2
+ //# sourceMappingURL=smartass-eslint.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smartass-eslint.config.d.ts","sourceRoot":"","sources":["../../src/eslint/smartass-eslint.config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gCAAgC,kCAiP3C,CAAC"}
@@ -0,0 +1,166 @@
1
+ import { defineConfig } from "eslint/config";
2
+ export const smartassPreferSpecificAssertions = defineConfig({
3
+ name: "@kensio/smartass/prefer-specific-assertions",
4
+ rules: {
5
+ "no-restricted-syntax": [
6
+ "warn",
7
+ {
8
+ selector: "CallExpression[callee.name='assertIdentical'] > Literal[value=true]:nth-child(2)",
9
+ message: "Use assertTrue(value) instead of assertIdentical(value, true).",
10
+ },
11
+ {
12
+ selector: "CallExpression[callee.name='assertIdentical'] > Literal[value=false]:nth-child(2)",
13
+ message: "Use assertFalse(value) instead of assertIdentical(value, false).",
14
+ },
15
+ {
16
+ selector: "CallExpression[callee.name='assertIdentical'] > Identifier[name='undefined']:nth-child(2)",
17
+ message: "Use assertUndefined(value) instead of assertIdentical(value, undefined).",
18
+ },
19
+ {
20
+ selector: "CallExpression[callee.name='assertIdentical'] > UnaryExpression[operator='typeof']:first-child",
21
+ message: "Use a more specific type assertion, such as assertTypeString(value), assertTypeNumber(value), or assertTypeBoolean(value), instead of assertIdentical(typeof value, expectedType).",
22
+ },
23
+ {
24
+ selector: "CallExpression[callee.name='assertIdentical'] > MemberExpression[property.name='length']:first-child",
25
+ message: "Use a more specific length assertion, such as assertArrayLength(value, expectedLength) or assertStringLength(value, expectedLength), instead of assertIdentical(value.length, expectedLength).",
26
+ },
27
+ {
28
+ selector: "CallExpression[callee.name='assertIdentical'] > MemberExpression[property.name='size']:first-child",
29
+ message: "Use a more specific size assertion, such as assertSetSize(value, expectedSize) or assertMapSize(value, expectedSize), instead of assertIdentical(value.size, expectedSize).",
30
+ },
31
+ {
32
+ selector: "CallExpression[callee.name='assertIdentical'] > BinaryExpression[operator='===']:first-child",
33
+ message: "Use a more specific assertion where available instead of assertIdentical(condition, true). For example, use assertTypeString(value), assertInstanceOf(value, ExpectedClass), assertArrayLength(value, expectedLength), or assertSetSize(value, expectedSize).",
34
+ },
35
+ {
36
+ selector: "CallExpression[callee.name='assertIdentical'] > BinaryExpression[operator='!==']:first-child",
37
+ message: "Use a more specific assertion where available instead of assertIdentical(condition, false). For example, use assertUndefined(value) or assertNonNullable(value) when appropriate.",
38
+ },
39
+ {
40
+ selector: "CallExpression[callee.name='assertIdentical'] > BinaryExpression[operator='instanceof']:first-child",
41
+ message: "Use assertInstanceOf(value, ExpectedClass) instead of assertIdentical(value instanceof ExpectedClass, true).",
42
+ },
43
+ {
44
+ selector: "CallExpression[callee.name='assertIdentical'] > BinaryExpression[operator='in']:first-child",
45
+ message: "Use assertObjectHasProperty(value, key) instead of assertIdentical(key in value, true).",
46
+ },
47
+ {
48
+ selector: "CallExpression[callee.name='assertIdentical'] > CallExpression[callee.property.name='includes']:first-child",
49
+ message: "Use a more specific includes assertion where available, such as assertArrayIncludes(value, expectedItem), assertStringIncludes(value, expectedSubstring), assertStringNotIncludes(value, unexpectedSubstring), or assertOneOf(value, allowedValues).",
50
+ },
51
+ {
52
+ selector: "CallExpression[callee.name='assertIdentical'] > CallExpression[callee.property.name='startsWith']:first-child",
53
+ message: "Use assertStringStartsWith(value, expectedPrefix) instead of assertIdentical(value.startsWith(expectedPrefix), true).",
54
+ },
55
+ {
56
+ selector: "CallExpression[callee.name='assertIdentical'] > CallExpression[callee.property.name='endsWith']:first-child",
57
+ message: "Use assertStringEndsWith(value, expectedSuffix) instead of assertIdentical(value.endsWith(expectedSuffix), true).",
58
+ },
59
+ {
60
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='==='] > UnaryExpression[operator='typeof']",
61
+ message: "Use a more specific type assertion, such as assertTypeString(value), assertTypeNumber(value), or assertTypeBoolean(value), instead of assertTrue(typeof value === expectedType).",
62
+ },
63
+ {
64
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='!=='] > UnaryExpression[operator='typeof']",
65
+ message: "Use assertFalse(typeof value === expectedType) only when you mean to assert the value is not that type. If you mean the value has that type, use a specific assertion such as assertTypeString(value), assertTypeNumber(value), or assertTypeBoolean(value).",
66
+ },
67
+ {
68
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='==='] > Identifier[name='undefined']",
69
+ message: "Use assertUndefined(value) instead of assertTrue(value === undefined).",
70
+ },
71
+ {
72
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='=='] > Literal[value=null]",
73
+ message: "Use assertIdentical(value, null) or a more specific null assertion if available. Do not use assertTrue(value == null) unless you intentionally want loose nullish equality.",
74
+ },
75
+ {
76
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='!='] > Literal[value=null]",
77
+ message: "Use assertNonNullable(value) instead of assertTrue(value != null).",
78
+ },
79
+ {
80
+ selector: "CallExpression[callee.name='assertTrue'] > LogicalExpression[operator='&&'] BinaryExpression[operator='!=='] > Literal[value=null]",
81
+ message: "Use assertNonNullable(value) instead of manually checking value !== null && value !== undefined.",
82
+ },
83
+ {
84
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='instanceof']",
85
+ message: "Use assertInstanceOf(value, ExpectedClass) instead of assertTrue(value instanceof ExpectedClass).",
86
+ },
87
+ {
88
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='in']",
89
+ message: "Use assertObjectHasProperty(value, key) instead of assertTrue(key in value).",
90
+ },
91
+ {
92
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='==='] > MemberExpression[property.name='length']",
93
+ message: "Use a more specific length assertion, such as assertArrayLength(value, expectedLength) or assertStringLength(value, expectedLength), instead of assertTrue(value.length === expectedLength).",
94
+ },
95
+ {
96
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='>='] > MemberExpression[property.name='length']",
97
+ message: "Use assertArrayMinLength(value, minimumLength) instead of assertTrue(value.length >= minimumLength) where the value is an array.",
98
+ },
99
+ {
100
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='>'] > MemberExpression[property.name='length']",
101
+ message: "Use assertArrayNotEmpty(value) instead of assertTrue(value.length > 0) where the value is an array.",
102
+ },
103
+ {
104
+ selector: "CallExpression[callee.name='assertTrue'] > BinaryExpression[operator='==='] > MemberExpression[property.name='size']",
105
+ message: "Use a more specific size assertion, such as assertSetSize(value, expectedSize) or assertMapSize(value, expectedSize), instead of assertTrue(value.size === expectedSize).",
106
+ },
107
+ {
108
+ selector: "CallExpression[callee.name='assertTrue'] > CallExpression[callee.property.name='includes']",
109
+ message: "Use a more specific includes assertion, such as assertArrayIncludes(value, expectedItem), assertStringIncludes(value, expectedSubstring), or assertOneOf(value, allowedValues), instead of assertTrue(value.includes(expected)).",
110
+ },
111
+ {
112
+ selector: "CallExpression[callee.name='assertTrue'] > CallExpression[callee.property.name='includes'][callee.object.type='ArrayExpression']",
113
+ message: "Use assertOneOf(value, allowedValues) instead of assertTrue(allowedValues.includes(value)).",
114
+ },
115
+ {
116
+ selector: "CallExpression[callee.name='assertTrue'] > CallExpression[callee.property.name='startsWith']",
117
+ message: "Use assertStringStartsWith(value, expectedPrefix) instead of assertTrue(value.startsWith(expectedPrefix)).",
118
+ },
119
+ {
120
+ selector: "CallExpression[callee.name='assertTrue'] > CallExpression[callee.property.name='endsWith']",
121
+ message: "Use assertStringEndsWith(value, expectedSuffix) instead of assertTrue(value.endsWith(expectedSuffix)).",
122
+ },
123
+ {
124
+ selector: "CallExpression[callee.name='assertTrue'] > LogicalExpression[operator='&&']",
125
+ message: "Use a more specific assertion where available instead of assertTrue(left && right). For example, use assertNumberBetween(value, min, max) for inclusive range checks.",
126
+ },
127
+ {
128
+ selector: "CallExpression[callee.name='assertFalse'] > BinaryExpression[operator='!=='] > Identifier[name='undefined']",
129
+ message: "Use assertUndefined(value) instead of assertFalse(value !== undefined).",
130
+ },
131
+ {
132
+ selector: "CallExpression[callee.name='assertFalse'] > BinaryExpression[operator='=='] > Literal[value=null]",
133
+ message: "Use assertNonNullable(value) instead of assertFalse(value == null).",
134
+ },
135
+ {
136
+ selector: "CallExpression[callee.name='assertFalse'] > BinaryExpression[operator='==='] > MemberExpression[property.name='length']",
137
+ message: "Use assertArrayNotEmpty(value) instead of assertFalse(value.length === 0) where the value is an array.",
138
+ },
139
+ {
140
+ selector: "CallExpression[callee.name='assertFalse'] > CallExpression[callee.property.name='includes']",
141
+ message: "Use a more specific negative includes assertion where available, such as assertStringNotIncludes(value, unexpectedSubstring), instead of assertFalse(value.includes(unexpected)).",
142
+ },
143
+ {
144
+ selector: "CallExpression[callee.name='assertFalse'] > UnaryExpression[operator='!'] > BinaryExpression[operator='instanceof']",
145
+ message: "Use assertInstanceOf(value, ExpectedClass) instead of assertFalse(!(value instanceof ExpectedClass)).",
146
+ },
147
+ {
148
+ selector: "CallExpression[callee.name='assertFalse'] > UnaryExpression[operator='!'] > BinaryExpression[operator='in']",
149
+ message: "Use assertObjectHasProperty(value, key) instead of assertFalse(!(key in value)).",
150
+ },
151
+ {
152
+ selector: "CallExpression[callee.name='assertFalse'] > UnaryExpression[operator='!'] > CallExpression[callee.property.name='includes']",
153
+ message: "Use a more specific includes assertion where available instead of assertFalse(!value.includes(expected)).",
154
+ },
155
+ {
156
+ selector: "CallExpression[callee.name='assertFalse'] > UnaryExpression[operator='!'] > CallExpression[callee.property.name='startsWith']",
157
+ message: "Use assertStringStartsWith(value, expectedPrefix) instead of assertFalse(!value.startsWith(expectedPrefix)).",
158
+ },
159
+ {
160
+ selector: "CallExpression[callee.name='assertFalse'] > UnaryExpression[operator='!'] > CallExpression[callee.property.name='endsWith']",
161
+ message: "Use assertStringEndsWith(value, expectedSuffix) instead of assertFalse(!value.endsWith(expectedSuffix)).",
162
+ },
163
+ ],
164
+ },
165
+ });
166
+ //# sourceMappingURL=smartass-eslint.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smartass-eslint.config.js","sourceRoot":"","sources":["../../src/eslint/smartass-eslint.config.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/KensioSoftware/smartass"
8
8
  },
9
9
  "license": "Apache-2.0",
10
- "version": "1.24.0",
10
+ "version": "1.26.0",
11
11
  "type": "module",
12
12
  "main": "./dist/index.js",
13
13
  "types": "./dist/index.d.ts",
@@ -15,6 +15,10 @@
15
15
  ".": {
16
16
  "types": "./dist/index.d.ts",
17
17
  "import": "./dist/index.js"
18
+ },
19
+ "./eslint": {
20
+ "types": "./dist/eslint/smartass-eslint.config.d.ts",
21
+ "import": "./dist/eslint/smartass-eslint.config.js"
18
22
  }
19
23
  },
20
24
  "files": [