@checkdigit/eslint-plugin 7.17.1 → 8.0.0-PR.141-56ee
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/dist-mjs/aws/is-aws-sdk-v3-used.mjs +7 -3
- package/dist-mjs/aws/require-aws-bare-bones.mjs +23 -5
- package/dist-mjs/aws/require-aws-config.mjs +5 -2
- package/dist-mjs/aws/require-consistent-read.mjs +12 -4
- package/dist-mjs/file-path-comment.mjs +15 -6
- package/dist-mjs/index.mjs +25 -9
- package/dist-mjs/invalid-json-stringify.mjs +8 -3
- package/dist-mjs/library/format.mjs +1 -1
- package/dist-mjs/library/tree.mjs +7 -2
- package/dist-mjs/library/ts-tree.mjs +7 -2
- package/dist-mjs/no-card-numbers.mjs +1 -1
- package/dist-mjs/no-duplicated-imports.mjs +25 -7
- package/dist-mjs/no-legacy-service-typing.mjs +18 -7
- package/dist-mjs/no-promise-instance-method.mjs +1 -1
- package/dist-mjs/no-random-v4-uuid.mjs +5 -2
- package/dist-mjs/no-side-effects.mjs +11 -7
- package/dist-mjs/no-status-code-assert.mjs +5 -2
- package/dist-mjs/no-test-import.mjs +5 -2
- package/dist-mjs/no-util.mjs +1 -1
- package/dist-mjs/no-uuid.mjs +1 -1
- package/dist-mjs/no-wallaby-comment.mjs +25 -7
- package/dist-mjs/object-literal-response.mjs +1 -1
- package/dist-mjs/regular-expression-comment.mjs +4 -2
- package/dist-mjs/require-assert-message.mjs +1 -1
- package/dist-mjs/require-assert-predicate-rejects-throws.mjs +1 -1
- package/dist-mjs/require-fixed-services-import.mjs +21 -6
- package/dist-mjs/require-resolve-full-response.mjs +32 -11
- package/dist-mjs/require-service-call-response-declaration.mjs +12 -4
- package/dist-mjs/require-strict-assert.mjs +5 -2
- package/dist-mjs/require-ts-extension-imports-exports.mjs +1 -1
- package/dist-mjs/require-type-out-of-type-only-imports.mjs +6 -2
- package/dist-types/no-legacy-service-typing.d.ts +3 -1
- package/package.json +1 -96
- package/src/aws/is-aws-sdk-v3-used.ts +6 -2
- package/src/aws/require-aws-bare-bones.ts +65 -42
- package/src/aws/require-aws-config.ts +85 -63
- package/src/aws/require-consistent-read.ts +97 -60
- package/src/file-path-comment.ts +12 -3
- package/src/index.ts +28 -10
- package/src/invalid-json-stringify.ts +9 -3
- package/src/library/format.ts +4 -1
- package/src/library/tree.ts +8 -2
- package/src/library/ts-tree.ts +24 -7
- package/src/no-card-numbers.ts +6 -1
- package/src/no-duplicated-imports.ts +48 -18
- package/src/no-legacy-service-typing.ts +25 -8
- package/src/no-promise-instance-method.ts +8 -3
- package/src/no-random-v4-uuid.ts +36 -11
- package/src/no-side-effects.ts +78 -29
- package/src/no-status-code-assert.ts +21 -7
- package/src/no-test-import.ts +8 -2
- package/src/no-util.ts +3 -1
- package/src/no-uuid.ts +8 -2
- package/src/no-wallaby-comment.ts +40 -9
- package/src/object-literal-response.ts +25 -9
- package/src/regular-expression-comment.ts +9 -3
- package/src/require-assert-message.ts +13 -5
- package/src/require-assert-predicate-rejects-throws.ts +6 -3
- package/src/require-fixed-services-import.ts +31 -10
- package/src/require-resolve-full-response.ts +221 -172
- package/src/require-service-call-response-declaration.ts +23 -8
- package/src/require-strict-assert.ts +21 -8
- package/src/require-ts-extension-imports-exports.ts +19 -6
- package/src/require-type-out-of-type-only-imports.ts +12 -4
|
@@ -10,8 +10,10 @@ import getDocumentationUrl from './get-documentation-url.ts';
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
export const ruleId = 'no-promise-instance-method';
|
|
13
|
-
export const NO_PROMISE_INSTANCE_METHOD_THEN =
|
|
14
|
-
|
|
13
|
+
export const NO_PROMISE_INSTANCE_METHOD_THEN =
|
|
14
|
+
'NO_PROMISE_INSTANCE_METHOD_THEN';
|
|
15
|
+
export const NO_PROMISE_INSTANCE_METHOD_CATCH_FINALLY =
|
|
16
|
+
'NO_PROMISE_INSTANCE_METHOD_CATCH_FINALLY';
|
|
15
17
|
|
|
16
18
|
export default {
|
|
17
19
|
meta: {
|
|
@@ -29,7 +31,10 @@ export default {
|
|
|
29
31
|
create(context) {
|
|
30
32
|
return {
|
|
31
33
|
MemberExpression(node) {
|
|
32
|
-
if (
|
|
34
|
+
if (
|
|
35
|
+
node.property.type === 'Identifier' &&
|
|
36
|
+
['then', 'catch', 'finally'].includes(node.property.name)
|
|
37
|
+
) {
|
|
33
38
|
context.report({
|
|
34
39
|
node,
|
|
35
40
|
messageId:
|
package/src/no-random-v4-uuid.ts
CHANGED
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
AST_NODE_TYPES,
|
|
11
|
+
ESLintUtils,
|
|
12
|
+
TSESLint,
|
|
13
|
+
TSESTree,
|
|
14
|
+
} from '@typescript-eslint/utils';
|
|
10
15
|
import getDocumentationUrl from './get-documentation-url.ts';
|
|
11
16
|
|
|
12
17
|
export const ruleId = 'no-random-v4-uuid';
|
|
@@ -21,7 +26,10 @@ interface Aliases {
|
|
|
21
26
|
nodeCryptoRandomUUIDAlias?: string;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
const processImportDeclaration = (
|
|
29
|
+
const processImportDeclaration = (
|
|
30
|
+
node: TSESTree.ImportDeclaration,
|
|
31
|
+
aliases: Aliases,
|
|
32
|
+
) => {
|
|
25
33
|
node.specifiers.forEach((specifier) => {
|
|
26
34
|
if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {
|
|
27
35
|
if (
|
|
@@ -37,39 +45,54 @@ const processImportDeclaration = (node: TSESTree.ImportDeclaration, aliases: Ali
|
|
|
37
45
|
) {
|
|
38
46
|
aliases.nodeCryptoRandomUUIDAlias = specifier.local.name;
|
|
39
47
|
}
|
|
40
|
-
} else if (
|
|
48
|
+
} else if (
|
|
49
|
+
specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier &&
|
|
50
|
+
node.source.value === 'uuid'
|
|
51
|
+
) {
|
|
41
52
|
aliases.uuidDefaultAlias = specifier.local.name;
|
|
42
53
|
}
|
|
43
54
|
});
|
|
44
55
|
};
|
|
45
56
|
|
|
46
|
-
const isUuid4Call = (
|
|
47
|
-
|
|
57
|
+
const isUuid4Call = (
|
|
58
|
+
node: TSESTree.CallExpression,
|
|
59
|
+
aliases: Aliases,
|
|
60
|
+
): boolean =>
|
|
61
|
+
(node.callee.type === AST_NODE_TYPES.Identifier &&
|
|
62
|
+
node.callee.name === aliases.uuid4Alias) ||
|
|
48
63
|
(node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
49
64
|
node.callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
50
65
|
node.callee.object.name === aliases.uuidDefaultAlias &&
|
|
51
66
|
node.callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
52
67
|
node.callee.property.name === 'v4');
|
|
53
68
|
|
|
54
|
-
const isCryptoRandomUUIDCall = (
|
|
55
|
-
|
|
69
|
+
const isCryptoRandomUUIDCall = (
|
|
70
|
+
node: TSESTree.CallExpression,
|
|
71
|
+
alias?: string,
|
|
72
|
+
): boolean =>
|
|
73
|
+
(node.callee.type === AST_NODE_TYPES.Identifier &&
|
|
74
|
+
node.callee.name === alias) ||
|
|
56
75
|
(node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
57
76
|
node.callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
58
77
|
node.callee.object.name === 'crypto' &&
|
|
59
78
|
node.callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
60
79
|
node.callee.property.name === 'randomUUID');
|
|
61
80
|
|
|
62
|
-
const rule: TSESLint.RuleModule<
|
|
81
|
+
const rule: TSESLint.RuleModule<
|
|
82
|
+
typeof NO_RANDOM_V4_UUID | typeof NO_UUID_MODULE_FOR_V4
|
|
83
|
+
> = createRule({
|
|
63
84
|
name: ruleId,
|
|
64
85
|
meta: {
|
|
65
86
|
type: 'problem',
|
|
66
87
|
docs: {
|
|
67
|
-
description:
|
|
88
|
+
description:
|
|
89
|
+
'Disallow the use of `uuid.v4` and `crypto.randomUUID` for generating random v4 UUIDs.',
|
|
68
90
|
},
|
|
69
91
|
schema: [],
|
|
70
92
|
messages: {
|
|
71
93
|
[NO_RANDOM_V4_UUID]: 'Avoid generating v4 UUIDs',
|
|
72
|
-
[NO_UUID_MODULE_FOR_V4]:
|
|
94
|
+
[NO_UUID_MODULE_FOR_V4]:
|
|
95
|
+
'Avoid using the `uuid` module for v4 UUID generation',
|
|
73
96
|
},
|
|
74
97
|
},
|
|
75
98
|
defaultOptions: [],
|
|
@@ -86,7 +109,9 @@ const rule: TSESLint.RuleModule<typeof NO_RANDOM_V4_UUID | typeof NO_UUID_MODULE
|
|
|
86
109
|
node,
|
|
87
110
|
messageId: NO_UUID_MODULE_FOR_V4,
|
|
88
111
|
});
|
|
89
|
-
} else if (
|
|
112
|
+
} else if (
|
|
113
|
+
isCryptoRandomUUIDCall(node, aliases.nodeCryptoRandomUUIDAlias)
|
|
114
|
+
) {
|
|
90
115
|
context.report({
|
|
91
116
|
node,
|
|
92
117
|
messageId: NO_RANDOM_V4_UUID,
|
package/src/no-side-effects.ts
CHANGED
|
@@ -20,12 +20,15 @@ const NO_SIDE_EFFECTS = 'NO_SIDE_EFFECTS';
|
|
|
20
20
|
// Type guards
|
|
21
21
|
|
|
22
22
|
// Checks if a node is an ExpressionStatement
|
|
23
|
-
const isExpressionStatement = (
|
|
23
|
+
const isExpressionStatement = (
|
|
24
|
+
node: TSESTree.Node,
|
|
25
|
+
): node is TSESTree.ExpressionStatement =>
|
|
24
26
|
node.type === TSESTree.AST_NODE_TYPES.ExpressionStatement;
|
|
25
27
|
|
|
26
28
|
// Checks if a statement is an AwaitExpression
|
|
27
29
|
const isAwaitExpression = (statement: TSESTree.Node): boolean =>
|
|
28
|
-
isExpressionStatement(statement) &&
|
|
30
|
+
isExpressionStatement(statement) &&
|
|
31
|
+
statement.expression.type === TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
29
32
|
|
|
30
33
|
// Checks if a node is a VariableDeclaration with an AwaitExpression
|
|
31
34
|
const isVariableDeclarationAwaitExpression = (node: TSESTree.Node): boolean =>
|
|
@@ -35,7 +38,9 @@ const isVariableDeclarationAwaitExpression = (node: TSESTree.Node): boolean =>
|
|
|
35
38
|
|
|
36
39
|
// Checks if a node is a VariableDeclaration that is not const or using
|
|
37
40
|
const isNotValidVariableDeclaration = (node: TSESTree.Node): boolean =>
|
|
38
|
-
node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration &&
|
|
41
|
+
node.type === TSESTree.AST_NODE_TYPES.VariableDeclaration &&
|
|
42
|
+
node.kind !== 'const' &&
|
|
43
|
+
node.kind !== 'using';
|
|
39
44
|
|
|
40
45
|
// Checks if a node is a control flow statement
|
|
41
46
|
const isControlFlowStatement = (node: TSESTree.Node): boolean =>
|
|
@@ -56,36 +61,59 @@ const isAssignmentExpression = (node: TSESTree.Node): boolean =>
|
|
|
56
61
|
// Helper functions
|
|
57
62
|
|
|
58
63
|
// Checks if the callee is an identifier and not excluded
|
|
59
|
-
const isIdentifierCallee = (
|
|
60
|
-
node
|
|
64
|
+
const isIdentifierCallee = (
|
|
65
|
+
node: TSESTree.CallExpression,
|
|
66
|
+
excludedIdentifiers: string[],
|
|
67
|
+
): boolean =>
|
|
68
|
+
node.callee.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
69
|
+
!excludedIdentifiers.includes(node.callee.name);
|
|
61
70
|
|
|
62
71
|
// Checks if the callee is a member expression and not excluded
|
|
63
|
-
const isMemberExpressionCallee = (
|
|
72
|
+
const isMemberExpressionCallee = (
|
|
73
|
+
node: TSESTree.CallExpression,
|
|
74
|
+
excludedIdentifiers: string[],
|
|
75
|
+
): boolean =>
|
|
64
76
|
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
65
77
|
node.callee.object.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
66
78
|
node.callee.property.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
67
|
-
!excludedIdentifiers.includes(
|
|
79
|
+
!excludedIdentifiers.includes(
|
|
80
|
+
`${node.callee.object.name}.${node.callee.property.name}`,
|
|
81
|
+
);
|
|
68
82
|
|
|
69
83
|
// Checks if the callee is a member expression with a non-identifier object
|
|
70
|
-
const isNonIdentifierObjectMemberExpressionCallee = (
|
|
84
|
+
const isNonIdentifierObjectMemberExpressionCallee = (
|
|
85
|
+
node: TSESTree.CallExpression,
|
|
86
|
+
): boolean =>
|
|
71
87
|
node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
72
88
|
node.callee.object.type !== TSESTree.AST_NODE_TYPES.Identifier;
|
|
73
89
|
|
|
74
90
|
// Checks if a statement is a CallExpression with a member expression callee
|
|
75
|
-
const isCallExpressionCalleeMemberExpression = (
|
|
91
|
+
const isCallExpressionCalleeMemberExpression = (
|
|
92
|
+
statement: TSESTree.Node,
|
|
93
|
+
excludedIdentifiers: string[],
|
|
94
|
+
): boolean =>
|
|
76
95
|
isExpressionStatement(statement) &&
|
|
77
96
|
statement.expression.type === TSESTree.AST_NODE_TYPES.CallExpression &&
|
|
78
|
-
statement.expression.callee.type ===
|
|
79
|
-
|
|
80
|
-
statement.expression.callee.
|
|
97
|
+
statement.expression.callee.type ===
|
|
98
|
+
TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
99
|
+
statement.expression.callee.object.type ===
|
|
100
|
+
TSESTree.AST_NODE_TYPES.Identifier &&
|
|
101
|
+
statement.expression.callee.property.type ===
|
|
102
|
+
TSESTree.AST_NODE_TYPES.Identifier &&
|
|
81
103
|
!excludedIdentifiers.includes(statement.expression.callee.object.name) &&
|
|
82
104
|
!excludedIdentifiers.includes(
|
|
83
105
|
`${statement.expression.callee.object.name}.${statement.expression.callee.property.name}`,
|
|
84
106
|
);
|
|
85
107
|
|
|
86
108
|
// Checks if a node is a VariableDeclaration with a CallExpression
|
|
87
|
-
const isVariableDeclarationCallExpression = (
|
|
88
|
-
|
|
109
|
+
const isVariableDeclarationCallExpression = (
|
|
110
|
+
node: TSESTree.Node,
|
|
111
|
+
excludedIdentifiers: string[],
|
|
112
|
+
): boolean => {
|
|
113
|
+
if (
|
|
114
|
+
node.type !== TSESTree.AST_NODE_TYPES.VariableDeclaration ||
|
|
115
|
+
node.declarations.length === 0
|
|
116
|
+
) {
|
|
89
117
|
return false;
|
|
90
118
|
}
|
|
91
119
|
|
|
@@ -110,28 +138,43 @@ const isVariableDeclarationCallExpression = (node: TSESTree.Node, excludedIdenti
|
|
|
110
138
|
};
|
|
111
139
|
|
|
112
140
|
// Checks if an ExportNamedDeclaration has side effects
|
|
113
|
-
const isExportNamedDeclarationWithSideEffects = (
|
|
141
|
+
const isExportNamedDeclarationWithSideEffects = (
|
|
142
|
+
statement: TSESTree.Node,
|
|
143
|
+
excludedIdentifiers: string[],
|
|
144
|
+
): boolean =>
|
|
114
145
|
statement.type === TSESTree.AST_NODE_TYPES.ExportNamedDeclaration &&
|
|
115
146
|
statement.declaration !== null &&
|
|
116
147
|
(isVariableDeclarationAwaitExpression(statement.declaration) ||
|
|
117
|
-
isVariableDeclarationCallExpression(
|
|
148
|
+
isVariableDeclarationCallExpression(
|
|
149
|
+
statement.declaration,
|
|
150
|
+
excludedIdentifiers,
|
|
151
|
+
));
|
|
118
152
|
|
|
119
153
|
// Checks if an ExpressionStatement has side effects
|
|
120
|
-
const isExpressionStatementWithSideEffects = (
|
|
154
|
+
const isExpressionStatementWithSideEffects = (
|
|
155
|
+
statement: TSESTree.Node,
|
|
156
|
+
excludedIdentifiers: string[],
|
|
157
|
+
): boolean =>
|
|
121
158
|
statement.type === TSESTree.AST_NODE_TYPES.ExpressionStatement &&
|
|
122
159
|
statement.expression.type === TSESTree.AST_NODE_TYPES.CallExpression &&
|
|
123
160
|
((statement.expression.callee.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
124
161
|
!excludedIdentifiers.includes(statement.expression.callee.name)) ||
|
|
125
|
-
(statement.expression.callee.type ===
|
|
126
|
-
|
|
127
|
-
statement.expression.callee.
|
|
162
|
+
(statement.expression.callee.type ===
|
|
163
|
+
TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
164
|
+
statement.expression.callee.object.type ===
|
|
165
|
+
TSESTree.AST_NODE_TYPES.Identifier &&
|
|
166
|
+
statement.expression.callee.property.type ===
|
|
167
|
+
TSESTree.AST_NODE_TYPES.Identifier &&
|
|
128
168
|
!excludedIdentifiers.includes(
|
|
129
169
|
`${statement.expression.callee.object.name}.${statement.expression.callee.property.name}`,
|
|
130
170
|
)));
|
|
131
171
|
|
|
132
172
|
// Checks if a node is a VariableDeclaration with a NewExpression
|
|
133
173
|
const isVariableDeclarationNewExpression = (node: TSESTree.Node): boolean => {
|
|
134
|
-
if (
|
|
174
|
+
if (
|
|
175
|
+
node.type !== TSESTree.AST_NODE_TYPES.VariableDeclaration ||
|
|
176
|
+
node.declarations.length === 0
|
|
177
|
+
) {
|
|
135
178
|
return false;
|
|
136
179
|
}
|
|
137
180
|
|
|
@@ -140,7 +183,10 @@ const isVariableDeclarationNewExpression = (node: TSESTree.Node): boolean => {
|
|
|
140
183
|
};
|
|
141
184
|
|
|
142
185
|
// Update the hasSideEffects function to return a string indicating the type of side effect
|
|
143
|
-
const hasSideEffects = (
|
|
186
|
+
const hasSideEffects = (
|
|
187
|
+
statement: TSESTree.Node,
|
|
188
|
+
excludedIdentifiers: string[],
|
|
189
|
+
): string | null => {
|
|
144
190
|
if (isAwaitExpression(statement)) {
|
|
145
191
|
return TSESTree.AST_NODE_TYPES.AwaitExpression;
|
|
146
192
|
}
|
|
@@ -174,21 +220,22 @@ const hasSideEffects = (statement: TSESTree.Node, excludedIdentifiers: string[])
|
|
|
174
220
|
return null;
|
|
175
221
|
};
|
|
176
222
|
|
|
177
|
-
const createRule: ReturnType<typeof ESLintUtils.RuleCreator> =
|
|
178
|
-
getDocumentationUrl(name)
|
|
179
|
-
);
|
|
223
|
+
const createRule: ReturnType<typeof ESLintUtils.RuleCreator> =
|
|
224
|
+
ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
180
225
|
|
|
181
226
|
const rule: ReturnType<typeof createRule> = createRule({
|
|
182
227
|
name: ruleId,
|
|
183
228
|
meta: {
|
|
184
229
|
type: 'problem',
|
|
185
230
|
docs: {
|
|
186
|
-
description:
|
|
231
|
+
description:
|
|
232
|
+
'Ensure no side effects can occur at the module-level only if exporting module',
|
|
187
233
|
},
|
|
188
234
|
schema: [
|
|
189
235
|
{
|
|
190
236
|
type: 'object',
|
|
191
237
|
properties: {
|
|
238
|
+
// eslint-disable-next-line eslint-plugin/require-meta-schema-description
|
|
192
239
|
excludedIdentifiers: {
|
|
193
240
|
type: 'array',
|
|
194
241
|
items: { type: 'string' },
|
|
@@ -197,20 +244,22 @@ const rule: ReturnType<typeof createRule> = createRule({
|
|
|
197
244
|
additionalProperties: false,
|
|
198
245
|
},
|
|
199
246
|
],
|
|
247
|
+
defaultOptions: [{ excludedIdentifiers: [''] }],
|
|
200
248
|
messages: {
|
|
201
249
|
[NO_SIDE_EFFECTS]: 'No side effects can occur at the module-level',
|
|
202
250
|
},
|
|
203
251
|
},
|
|
204
|
-
defaultOptions: [{ excludedIdentifiers: [''] }],
|
|
205
252
|
create(context) {
|
|
206
253
|
const options: RuleOptions = context.options[0] as RuleOptions;
|
|
207
|
-
const excludedIdentifiers =
|
|
254
|
+
const excludedIdentifiers =
|
|
255
|
+
options.excludedIdentifiers.length > 0 ? options.excludedIdentifiers : [];
|
|
208
256
|
return {
|
|
209
257
|
Program(node: TSESTree.Program) {
|
|
210
258
|
const hasExport = node.body.some(
|
|
211
259
|
(statement: TSESTree.Node) =>
|
|
212
260
|
statement.type === TSESTree.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
213
|
-
statement.type ===
|
|
261
|
+
statement.type ===
|
|
262
|
+
TSESTree.AST_NODE_TYPES.ExportDefaultDeclaration ||
|
|
214
263
|
statement.type === TSESTree.AST_NODE_TYPES.ExportAllDeclaration,
|
|
215
264
|
);
|
|
216
265
|
|
|
@@ -7,8 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { StatusCodes } from 'http-status-codes';
|
|
10
|
-
import {
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
AST_NODE_TYPES,
|
|
12
|
+
ESLintUtils,
|
|
13
|
+
TSESLint,
|
|
14
|
+
TSESTree,
|
|
15
|
+
} from '@typescript-eslint/utils';
|
|
16
|
+
import getDocumentationUrl from './get-documentation-url.ts';
|
|
12
17
|
|
|
13
18
|
export const ruleId = 'no-status-code-assert';
|
|
14
19
|
const NO_STATUS_CODE_ASSERT = 'NO_STATUS_CODE_ASSERT';
|
|
@@ -31,7 +36,10 @@ const hasStatusCodeOrValue = (arg: TSESTree.Node): boolean => {
|
|
|
31
36
|
break;
|
|
32
37
|
}
|
|
33
38
|
case AST_NODE_TYPES.Literal:
|
|
34
|
-
if (
|
|
39
|
+
if (
|
|
40
|
+
typeof arg.value === 'number' &&
|
|
41
|
+
Object.values(StatusCodes).includes(arg.value)
|
|
42
|
+
) {
|
|
35
43
|
return true;
|
|
36
44
|
}
|
|
37
45
|
break;
|
|
@@ -52,19 +60,25 @@ const isAssertMemberExpression = (node: TSESTree.Node): boolean =>
|
|
|
52
60
|
node.object.name === 'assert' &&
|
|
53
61
|
node.property.type === AST_NODE_TYPES.Identifier;
|
|
54
62
|
|
|
55
|
-
const isAssertCallWithStatusCode = (
|
|
56
|
-
|
|
63
|
+
const isAssertCallWithStatusCode = (
|
|
64
|
+
callee: TSESTree.Node,
|
|
65
|
+
args: TSESTree.Node[],
|
|
66
|
+
): boolean =>
|
|
67
|
+
(isAssertIdentifier(callee) || isAssertMemberExpression(callee)) &&
|
|
68
|
+
args.some((arg) => hasStatusCodeOrValue(arg));
|
|
57
69
|
|
|
58
70
|
const rule: TSESLint.RuleModule<typeof NO_STATUS_CODE_ASSERT> = createRule({
|
|
59
71
|
name: ruleId,
|
|
60
72
|
meta: {
|
|
61
73
|
type: 'problem',
|
|
62
74
|
docs: {
|
|
63
|
-
description:
|
|
75
|
+
description:
|
|
76
|
+
'Disallow using status codes in assertions; use error handling instead',
|
|
64
77
|
},
|
|
65
78
|
schema: [],
|
|
66
79
|
messages: {
|
|
67
|
-
[NO_STATUS_CODE_ASSERT]:
|
|
80
|
+
[NO_STATUS_CODE_ASSERT]:
|
|
81
|
+
'Do not use status codes in assertions; use error handling instead',
|
|
68
82
|
},
|
|
69
83
|
},
|
|
70
84
|
defaultOptions: [],
|
package/src/no-test-import.ts
CHANGED
|
@@ -43,12 +43,18 @@ export default {
|
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
45
|
create(context) {
|
|
46
|
-
const options = {
|
|
46
|
+
const options = {
|
|
47
|
+
...DEFAULT_OPTIONS,
|
|
48
|
+
...(context.options[0] as NoTestImportRuleOptions),
|
|
49
|
+
};
|
|
47
50
|
const testFileRegexp = new RegExp(options.testFilePattern, 'u');
|
|
48
51
|
|
|
49
52
|
return {
|
|
50
53
|
ImportDeclaration(node) {
|
|
51
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
typeof node.source.value === 'string' &&
|
|
56
|
+
testFileRegexp.test(node.source.value)
|
|
57
|
+
) {
|
|
52
58
|
context.report({
|
|
53
59
|
node,
|
|
54
60
|
messageId: NO_TEST_IMPORT,
|
package/src/no-util.ts
CHANGED
|
@@ -35,7 +35,9 @@ const rule: ESLintUtils.RuleModule<typeof NO_UTIL> = createRule({
|
|
|
35
35
|
const sourceCode = context.sourceCode;
|
|
36
36
|
const tokens = sourceCode.tokensAndComments;
|
|
37
37
|
const firstNonCommentToken = tokens.find(
|
|
38
|
-
(token) =>
|
|
38
|
+
(token) =>
|
|
39
|
+
token.type !== AST_TOKEN_TYPES.Block &&
|
|
40
|
+
token.type !== AST_TOKEN_TYPES.Line,
|
|
39
41
|
);
|
|
40
42
|
if (firstNonCommentToken !== undefined) {
|
|
41
43
|
context.report({
|
package/src/no-uuid.ts
CHANGED
|
@@ -11,9 +11,15 @@ import type { Rule } from 'eslint';
|
|
|
11
11
|
|
|
12
12
|
const UUID_FOUND = 'UUID_FOUND';
|
|
13
13
|
const UUIDS_FOUND = 'UUIDS_FOUND';
|
|
14
|
-
const uuidRegex =
|
|
14
|
+
const uuidRegex =
|
|
15
|
+
/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/gmu;
|
|
15
16
|
|
|
16
|
-
function checkForUuid(
|
|
17
|
+
function checkForUuid(
|
|
18
|
+
value: string,
|
|
19
|
+
context: Rule.RuleContext,
|
|
20
|
+
node?: Node,
|
|
21
|
+
loc?: SourceLocation,
|
|
22
|
+
) {
|
|
17
23
|
const matches = value.match(uuidRegex);
|
|
18
24
|
if (matches === null) {
|
|
19
25
|
return;
|
|
@@ -9,9 +9,17 @@
|
|
|
9
9
|
import type { Rule, SourceCode } from 'eslint';
|
|
10
10
|
import type { Comment } from 'estree';
|
|
11
11
|
|
|
12
|
+
// eslint-disable-next-line sonarjs/slow-regex
|
|
12
13
|
const wallabyRegex = /(?<=(?:^|\*\/)\s*)[?]{1,2}|file\.only|file\.skip/gu;
|
|
13
|
-
const commentRegex =
|
|
14
|
-
|
|
14
|
+
const commentRegex =
|
|
15
|
+
// eslint-disable-next-line sonarjs/slow-regex
|
|
16
|
+
/\s*(?:\/\/|<!--)\s*(?:\?{1,2}\.?\s*|file\.(?:only|skip))\s*/gu;
|
|
17
|
+
function removeWallabyComment(
|
|
18
|
+
context: Rule.RuleContext,
|
|
19
|
+
sourceCode: SourceCode,
|
|
20
|
+
start: number,
|
|
21
|
+
end: number,
|
|
22
|
+
): void {
|
|
15
23
|
context.report({
|
|
16
24
|
loc: {
|
|
17
25
|
start: sourceCode.getLocFromIndex(start),
|
|
@@ -22,21 +30,35 @@ function removeWallabyComment(context: Rule.RuleContext, sourceCode: SourceCode,
|
|
|
22
30
|
});
|
|
23
31
|
}
|
|
24
32
|
|
|
25
|
-
function processLineComment(
|
|
33
|
+
function processLineComment(
|
|
34
|
+
context: Rule.RuleContext,
|
|
35
|
+
sourceCode: SourceCode,
|
|
36
|
+
comment: Comment,
|
|
37
|
+
): void {
|
|
26
38
|
if (comment.loc) {
|
|
27
39
|
const line = sourceCode.getLines()[comment.loc.start.line - 1];
|
|
28
40
|
if (line !== undefined) {
|
|
29
41
|
let match;
|
|
30
42
|
while ((match = commentRegex.exec(line)) !== null) {
|
|
31
|
-
const start = sourceCode.getIndexFromLoc({
|
|
32
|
-
|
|
43
|
+
const start = sourceCode.getIndexFromLoc({
|
|
44
|
+
line: comment.loc.start.line,
|
|
45
|
+
column: match.index,
|
|
46
|
+
});
|
|
47
|
+
const end = sourceCode.getIndexFromLoc({
|
|
48
|
+
line: comment.loc.start.line,
|
|
49
|
+
column: comment.loc.end.column,
|
|
50
|
+
});
|
|
33
51
|
removeWallabyComment(context, sourceCode, start, end);
|
|
34
52
|
}
|
|
35
53
|
}
|
|
36
54
|
}
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
function processBlockComment(
|
|
57
|
+
function processBlockComment(
|
|
58
|
+
context: Rule.RuleContext,
|
|
59
|
+
sourceCode: SourceCode,
|
|
60
|
+
comment: Comment,
|
|
61
|
+
): void {
|
|
40
62
|
const commentValues = comment.value.split('\n');
|
|
41
63
|
const blockCommentRegex = /^(?:\s*\*\s*)?(?:file\.only|file\.skip)$/gu;
|
|
42
64
|
commentValues.forEach((commentValue) => {
|
|
@@ -49,8 +71,14 @@ function processBlockComment(context: Rule.RuleContext, sourceCode: SourceCode,
|
|
|
49
71
|
while (comment.loc && (match = wallabyRegex.exec(commentValue)) !== null) {
|
|
50
72
|
const removeEntireComment = blockCommentRegex.test(comment.value.trim());
|
|
51
73
|
if (removeEntireComment) {
|
|
52
|
-
start = sourceCode.getIndexFromLoc({
|
|
53
|
-
|
|
74
|
+
start = sourceCode.getIndexFromLoc({
|
|
75
|
+
line: comment.loc.start.line,
|
|
76
|
+
column: comment.loc.start.column,
|
|
77
|
+
});
|
|
78
|
+
end = sourceCode.getIndexFromLoc({
|
|
79
|
+
line: comment.loc.end.line,
|
|
80
|
+
column: comment.loc.end.column,
|
|
81
|
+
});
|
|
54
82
|
} else {
|
|
55
83
|
let lineNumber = 0;
|
|
56
84
|
while (startLine <= endLine) {
|
|
@@ -61,7 +89,10 @@ function processBlockComment(context: Rule.RuleContext, sourceCode: SourceCode,
|
|
|
61
89
|
}
|
|
62
90
|
startLine++;
|
|
63
91
|
}
|
|
64
|
-
start = sourceCode.getIndexFromLoc({
|
|
92
|
+
start = sourceCode.getIndexFromLoc({
|
|
93
|
+
line: lineNumber + 1,
|
|
94
|
+
column: comment.loc.start.column,
|
|
95
|
+
});
|
|
65
96
|
end = sourceCode.getIndexFromLoc({ line: lineNumber + 2, column: 0 });
|
|
66
97
|
}
|
|
67
98
|
removeWallabyComment(context, sourceCode, start, end);
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
import type { CallExpression, Property } from 'estree';
|
|
10
10
|
import type { Rule } from 'eslint';
|
|
11
11
|
|
|
12
|
-
export const REQUIRE_OBJECT_LITERAL_MESSAGE_ID =
|
|
12
|
+
export const REQUIRE_OBJECT_LITERAL_MESSAGE_ID =
|
|
13
|
+
'REQUIRE_OBJECT_LITERAL_MESSAGE_ID';
|
|
13
14
|
export const REQUIRE_OBJECT_LITERAL_FOR_ERROR_RESPONSE_MESSAGE_ID =
|
|
14
15
|
'REQUIRE_OBJECT_LITERAL_FOR_ERROR_RESPONSE_MESSAGE_ID';
|
|
15
|
-
export const REQUIRE_OBJECT_LITERAL_FOR_HEADERS_MESSAGE_ID =
|
|
16
|
+
export const REQUIRE_OBJECT_LITERAL_FOR_HEADERS_MESSAGE_ID =
|
|
17
|
+
'REQUIRE_OBJECT_LITERAL_FOR_HEADERS_MESSAGE_ID';
|
|
16
18
|
|
|
17
19
|
// eslint-disable-next-line no-magic-numbers
|
|
18
20
|
const GOOD_STATUS_VALUES = [200, 201, 202, 203, 204, 205, 206, 207];
|
|
@@ -47,7 +49,10 @@ export default {
|
|
|
47
49
|
return {
|
|
48
50
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
49
51
|
CallExpression(node: CallExpression) {
|
|
50
|
-
if (
|
|
52
|
+
if (
|
|
53
|
+
node.callee.type === 'Identifier' &&
|
|
54
|
+
node.callee.name === 'setResponse'
|
|
55
|
+
) {
|
|
51
56
|
const responseContext = node.arguments[1];
|
|
52
57
|
|
|
53
58
|
// check top level object literal
|
|
@@ -62,9 +67,14 @@ export default {
|
|
|
62
67
|
if (responseContext?.type === 'ObjectExpression') {
|
|
63
68
|
const headers = responseContext.properties.find(
|
|
64
69
|
(property) =>
|
|
65
|
-
property.type === 'Property' &&
|
|
70
|
+
property.type === 'Property' &&
|
|
71
|
+
property.key.type === 'Identifier' &&
|
|
72
|
+
property.key.name === 'headers',
|
|
66
73
|
) as Property | undefined;
|
|
67
|
-
if (
|
|
74
|
+
if (
|
|
75
|
+
headers !== undefined &&
|
|
76
|
+
headers.value.type !== 'ObjectExpression'
|
|
77
|
+
) {
|
|
68
78
|
context.report({
|
|
69
79
|
node: headers,
|
|
70
80
|
messageId: REQUIRE_OBJECT_LITERAL_FOR_HEADERS_MESSAGE_ID,
|
|
@@ -76,7 +86,9 @@ export default {
|
|
|
76
86
|
if (responseContext?.type === 'ObjectExpression') {
|
|
77
87
|
const status = responseContext.properties.find(
|
|
78
88
|
(property) =>
|
|
79
|
-
property.type === 'Property' &&
|
|
89
|
+
property.type === 'Property' &&
|
|
90
|
+
property.key.type === 'Identifier' &&
|
|
91
|
+
property.key.name === 'status',
|
|
80
92
|
) as Property;
|
|
81
93
|
let isSuccessfulResponse;
|
|
82
94
|
if (status.value.type === 'MemberExpression') {
|
|
@@ -87,13 +99,16 @@ export default {
|
|
|
87
99
|
GOOD_STATUS_ENUM_NAMES.includes(status.value.property.name);
|
|
88
100
|
} else if (status.value.type === 'Literal') {
|
|
89
101
|
isSuccessfulResponse =
|
|
90
|
-
typeof status.value.value === 'number' &&
|
|
102
|
+
typeof status.value.value === 'number' &&
|
|
103
|
+
GOOD_STATUS_VALUES.includes(status.value.value);
|
|
91
104
|
}
|
|
92
105
|
if (isSuccessfulResponse === false) {
|
|
93
106
|
// handle error response
|
|
94
107
|
const body = responseContext.properties.find(
|
|
95
108
|
(property) =>
|
|
96
|
-
property.type === 'Property' &&
|
|
109
|
+
property.type === 'Property' &&
|
|
110
|
+
property.key.type === 'Identifier' &&
|
|
111
|
+
property.key.name === 'body',
|
|
97
112
|
) as Property | undefined;
|
|
98
113
|
if (body === undefined) {
|
|
99
114
|
return; // no body to lint for
|
|
@@ -102,7 +117,8 @@ export default {
|
|
|
102
117
|
if (body.value.type !== 'ObjectExpression') {
|
|
103
118
|
context.report({
|
|
104
119
|
node: body,
|
|
105
|
-
messageId:
|
|
120
|
+
messageId:
|
|
121
|
+
REQUIRE_OBJECT_LITERAL_FOR_ERROR_RESPONSE_MESSAGE_ID,
|
|
106
122
|
});
|
|
107
123
|
}
|
|
108
124
|
}
|
|
@@ -12,7 +12,8 @@ export default {
|
|
|
12
12
|
meta: {
|
|
13
13
|
type: 'problem',
|
|
14
14
|
docs: {
|
|
15
|
-
description:
|
|
15
|
+
description:
|
|
16
|
+
'Require comments for regular expressions before or on the same line',
|
|
16
17
|
url: 'https://github.com/checkdigit/eslint-plugin',
|
|
17
18
|
},
|
|
18
19
|
},
|
|
@@ -27,7 +28,9 @@ export default {
|
|
|
27
28
|
const previousLineComment = lines[previousLine - 1];
|
|
28
29
|
|
|
29
30
|
// Check if the regular expression contains any simple text
|
|
30
|
-
const isSimpleTextPattern = /^[0-9:./,\sa-zA-Z-]*$/gu.test(
|
|
31
|
+
const isSimpleTextPattern = /^[0-9:./,\sa-zA-Z-]*$/gu.test(
|
|
32
|
+
node.value.source,
|
|
33
|
+
);
|
|
31
34
|
if (isSimpleTextPattern) {
|
|
32
35
|
return;
|
|
33
36
|
}
|
|
@@ -40,7 +43,10 @@ export default {
|
|
|
40
43
|
// This regex is to check if the line starts with or without spaces and followed by two or more consecutive slashes // or start with /* and may have one or more asterisks, continuing until the first occurrence of */.
|
|
41
44
|
const commentRegularExpressionLine = /^\s*(?:\/{2,}|\/\*+)/gu;
|
|
42
45
|
const hasComment = regularExpression.test(comment.value.trim());
|
|
43
|
-
if (
|
|
46
|
+
if (
|
|
47
|
+
comment.type === 'Line' ||
|
|
48
|
+
comment.loc.start.line === comment.loc.end.line
|
|
49
|
+
) {
|
|
44
50
|
return (
|
|
45
51
|
(comment.loc.end.line === previousLine &&
|
|
46
52
|
previousLineComment !== undefined &&
|