@checkdigit/eslint-plugin 7.17.1 → 8.0.0-PR.141-7ea9

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.
Files changed (64) hide show
  1. package/dist-mjs/aws/is-aws-sdk-v3-used.mjs +7 -3
  2. package/dist-mjs/aws/require-aws-bare-bones.mjs +23 -5
  3. package/dist-mjs/aws/require-aws-config.mjs +5 -2
  4. package/dist-mjs/aws/require-consistent-read.mjs +12 -4
  5. package/dist-mjs/file-path-comment.mjs +15 -6
  6. package/dist-mjs/index.mjs +25 -9
  7. package/dist-mjs/invalid-json-stringify.mjs +8 -3
  8. package/dist-mjs/library/format.mjs +1 -1
  9. package/dist-mjs/library/tree.mjs +7 -2
  10. package/dist-mjs/library/ts-tree.mjs +7 -2
  11. package/dist-mjs/no-card-numbers.mjs +1 -1
  12. package/dist-mjs/no-duplicated-imports.mjs +25 -7
  13. package/dist-mjs/no-legacy-service-typing.mjs +18 -7
  14. package/dist-mjs/no-promise-instance-method.mjs +1 -1
  15. package/dist-mjs/no-random-v4-uuid.mjs +5 -2
  16. package/dist-mjs/no-side-effects.mjs +11 -7
  17. package/dist-mjs/no-status-code-assert.mjs +5 -2
  18. package/dist-mjs/no-test-import.mjs +5 -2
  19. package/dist-mjs/no-util.mjs +1 -1
  20. package/dist-mjs/no-uuid.mjs +1 -1
  21. package/dist-mjs/no-wallaby-comment.mjs +25 -7
  22. package/dist-mjs/object-literal-response.mjs +1 -1
  23. package/dist-mjs/regular-expression-comment.mjs +4 -2
  24. package/dist-mjs/require-assert-message.mjs +1 -1
  25. package/dist-mjs/require-assert-predicate-rejects-throws.mjs +1 -1
  26. package/dist-mjs/require-fixed-services-import.mjs +21 -6
  27. package/dist-mjs/require-resolve-full-response.mjs +32 -11
  28. package/dist-mjs/require-service-call-response-declaration.mjs +12 -4
  29. package/dist-mjs/require-strict-assert.mjs +5 -2
  30. package/dist-mjs/require-ts-extension-imports-exports.mjs +1 -1
  31. package/dist-mjs/require-type-out-of-type-only-imports.mjs +6 -2
  32. package/dist-types/no-legacy-service-typing.d.ts +3 -1
  33. package/package.json +1 -96
  34. package/src/aws/is-aws-sdk-v3-used.ts +6 -2
  35. package/src/aws/require-aws-bare-bones.ts +65 -42
  36. package/src/aws/require-aws-config.ts +85 -63
  37. package/src/aws/require-consistent-read.ts +97 -60
  38. package/src/file-path-comment.ts +12 -3
  39. package/src/index.ts +28 -10
  40. package/src/invalid-json-stringify.ts +9 -3
  41. package/src/library/format.ts +4 -1
  42. package/src/library/tree.ts +8 -2
  43. package/src/library/ts-tree.ts +24 -7
  44. package/src/no-card-numbers.ts +6 -1
  45. package/src/no-duplicated-imports.ts +48 -18
  46. package/src/no-legacy-service-typing.ts +25 -8
  47. package/src/no-promise-instance-method.ts +8 -3
  48. package/src/no-random-v4-uuid.ts +36 -11
  49. package/src/no-side-effects.ts +78 -29
  50. package/src/no-status-code-assert.ts +21 -7
  51. package/src/no-test-import.ts +8 -2
  52. package/src/no-util.ts +3 -1
  53. package/src/no-uuid.ts +8 -2
  54. package/src/no-wallaby-comment.ts +40 -9
  55. package/src/object-literal-response.ts +25 -9
  56. package/src/regular-expression-comment.ts +9 -3
  57. package/src/require-assert-message.ts +13 -5
  58. package/src/require-assert-predicate-rejects-throws.ts +6 -3
  59. package/src/require-fixed-services-import.ts +31 -10
  60. package/src/require-resolve-full-response.ts +221 -172
  61. package/src/require-service-call-response-declaration.ts +23 -8
  62. package/src/require-strict-assert.ts +21 -8
  63. package/src/require-ts-extension-imports-exports.ts +19 -6
  64. package/src/require-type-out-of-type-only-imports.ts +12 -4
@@ -6,12 +6,18 @@
6
6
  * This code is licensed under the MIT license (see LICENSE.txt for details).
7
7
  */
8
8
 
9
- import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
9
+ import {
10
+ AST_NODE_TYPES,
11
+ ESLintUtils,
12
+ TSESTree,
13
+ } from '@typescript-eslint/utils';
10
14
  import getDocumentationUrl from '../get-documentation-url.ts';
11
15
 
12
16
  export const ruleId = 'require-consistent-read';
13
- export const MESSAGE_ID_CONSISTENT_READ_TRUE = 'MESSAGE_ID_CONSISTENT_READ_TRUE';
14
- export const MESSAGE_ID_CONSISTENT_READ_FALSE = 'MESSAGE_ID_CONSISTENT_READ_FALSE';
17
+ export const MESSAGE_ID_CONSISTENT_READ_TRUE =
18
+ 'MESSAGE_ID_CONSISTENT_READ_TRUE';
19
+ export const MESSAGE_ID_CONSISTENT_READ_FALSE =
20
+ 'MESSAGE_ID_CONSISTENT_READ_FALSE';
15
21
 
16
22
  interface ReadCommandInfo {
17
23
  type: 'Get' | 'Query' | 'BatchGet';
@@ -27,12 +33,15 @@ function getPropertyName(property: TSESTree.Property): string | undefined {
27
33
  // eslint-disable-next-line no-nested-ternary
28
34
  return propertyKey.type === AST_NODE_TYPES.Identifier
29
35
  ? propertyKey.name
30
- : typeof propertyKey.value === 'string'
36
+ : // eslint-disable-next-line sonarjs/no-nested-conditional
37
+ typeof propertyKey.value === 'string'
31
38
  ? propertyKey.value
32
39
  : undefined;
33
40
  }
34
41
 
35
- function extractObjectProperties(objectExpression: TSESTree.ObjectExpression): Record<string, TSESTree.Property> {
42
+ function extractObjectProperties(
43
+ objectExpression: TSESTree.ObjectExpression,
44
+ ): Record<string, TSESTree.Property> {
36
45
  const objectProperties: Record<string, TSESTree.Property> = {};
37
46
  for (const property of objectExpression.properties) {
38
47
  if (property.type !== AST_NODE_TYPES.Property || property.computed) {
@@ -46,7 +55,9 @@ function extractObjectProperties(objectExpression: TSESTree.ObjectExpression): R
46
55
  return objectProperties;
47
56
  }
48
57
 
49
- function existsRequestItemsProperty(extractedObjectProperties: Record<string, TSESTree.Property>): boolean {
58
+ function existsRequestItemsProperty(
59
+ extractedObjectProperties: Record<string, TSESTree.Property>,
60
+ ): boolean {
50
61
  const requestItemProperty = extractedObjectProperties['RequestItems'];
51
62
  if (requestItemProperty?.type !== AST_NODE_TYPES.Property) {
52
63
  return false;
@@ -60,17 +71,25 @@ function existsRequestItemsProperty(extractedObjectProperties: Record<string, TS
60
71
  (property) =>
61
72
  property.type === AST_NODE_TYPES.Property &&
62
73
  property.value.type === AST_NODE_TYPES.ObjectExpression &&
63
- extractObjectProperties(property.value)['Keys']?.value.type === AST_NODE_TYPES.ArrayExpression,
74
+ extractObjectProperties(property.value)['Keys']?.value.type ===
75
+ AST_NODE_TYPES.ArrayExpression,
64
76
  );
65
77
  }
66
78
 
67
- function existsKeyProperty(extractedObjectProperties: Record<string, TSESTree.Property>): boolean {
79
+ function existsKeyProperty(
80
+ extractedObjectProperties: Record<string, TSESTree.Property>,
81
+ ): boolean {
68
82
  const keyProperty = extractedObjectProperties['Key'];
69
83
  // Relaxed: just ensure it's an object (works for both low-level and DocumentClient)
70
- return keyProperty?.type === AST_NODE_TYPES.Property && keyProperty.value.type === AST_NODE_TYPES.ObjectExpression;
84
+ return (
85
+ keyProperty?.type === AST_NODE_TYPES.Property &&
86
+ keyProperty.value.type === AST_NODE_TYPES.ObjectExpression
87
+ );
71
88
  }
72
89
 
73
- export function getReadCommandInfo(objectExpression: TSESTree.ObjectExpression): ReadCommandInfo | undefined {
90
+ export function getReadCommandInfo(
91
+ objectExpression: TSESTree.ObjectExpression,
92
+ ): ReadCommandInfo | undefined {
74
93
  const extractedProperties = extractObjectProperties(objectExpression);
75
94
  let readCommandInfo: ReadCommandInfo | undefined;
76
95
 
@@ -82,13 +101,19 @@ export function getReadCommandInfo(objectExpression: TSESTree.ObjectExpression):
82
101
  if (hasTableName && hasKey) {
83
102
  // make sure it is not an update or conditional write;
84
103
  // we can't really tell if it's a delete, but we simply ignore that case assuming they'll never be used
85
- if (!('UpdateExpression' in extractedProperties) && !('ConditionExpression' in extractedProperties)) {
104
+ if (
105
+ !('UpdateExpression' in extractedProperties) &&
106
+ !('ConditionExpression' in extractedProperties)
107
+ ) {
86
108
  readCommandInfo = { type: 'Get' };
87
109
  }
88
110
  } else {
89
- const hasKeyCondExpr = extractedProperties['KeyConditionExpression']?.value.type === AST_NODE_TYPES.Literal;
111
+ const hasKeyCondExpr =
112
+ extractedProperties['KeyConditionExpression']?.value.type ===
113
+ AST_NODE_TYPES.Literal;
90
114
  const hasLegacyKeyConditions =
91
- extractedProperties['KeyConditions']?.value.type === AST_NODE_TYPES.ObjectExpression;
115
+ extractedProperties['KeyConditions']?.value.type ===
116
+ AST_NODE_TYPES.ObjectExpression;
92
117
  if (hasTableName && (hasKeyCondExpr || hasLegacyKeyConditions)) {
93
118
  readCommandInfo = { type: 'Query' };
94
119
  }
@@ -122,54 +147,66 @@ export function getReadCommandInfo(objectExpression: TSESTree.ObjectExpression):
122
147
 
123
148
  const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
124
149
 
125
- const rule: ESLintUtils.RuleModule<typeof MESSAGE_ID_CONSISTENT_READ_TRUE | typeof MESSAGE_ID_CONSISTENT_READ_FALSE> =
126
- createRule({
127
- name: ruleId,
128
- meta: {
129
- type: 'problem',
130
- docs: {
131
- description:
132
- 'For AWS dynamodb commands Query/Get/BatchGet, ConsistentRead option should always be set as true unless global index is used. This will make the service more robust at the ignorable cost of RCU.',
133
- },
134
- messages: {
135
- [MESSAGE_ID_CONSISTENT_READ_TRUE]:
136
- 'ConsistentRead option should always be set as true for {{readCommandType}} command.',
137
- [MESSAGE_ID_CONSISTENT_READ_FALSE]:
138
- 'ConsistentRead option should not be set as true for {{readCommandType}} command when using a global secondary index.',
139
- },
140
- schema: [],
150
+ const rule: ESLintUtils.RuleModule<
151
+ | typeof MESSAGE_ID_CONSISTENT_READ_TRUE
152
+ | typeof MESSAGE_ID_CONSISTENT_READ_FALSE
153
+ > = createRule({
154
+ name: ruleId,
155
+ meta: {
156
+ type: 'problem',
157
+ docs: {
158
+ description:
159
+ 'For AWS dynamodb commands Query/Get/BatchGet, ConsistentRead option should always be set as true unless global index is used. This will make the service more robust at the ignorable cost of RCU.',
141
160
  },
142
- defaultOptions: [],
143
- create(context) {
144
- const sourceCode = context.sourceCode;
145
-
146
- return {
147
- ObjectExpression(node) {
148
- // Quick prefilter: only look at objects that mention table/read-ish keys
149
- const text = sourceCode.getText(node);
150
- if (!/TableName|RequestItems|KeyConditionExpression|KeyConditions/u.test(text)) {
151
- return;
152
- }
153
-
154
- const readCommandInfo = getReadCommandInfo(node);
155
- if (readCommandInfo !== undefined) {
156
- if (readCommandInfo.usedIndex !== true && readCommandInfo.consistentRead !== true) {
157
- context.report({
158
- node,
159
- messageId: MESSAGE_ID_CONSISTENT_READ_TRUE,
160
- data: { readCommandType: readCommandInfo.type },
161
- });
162
- } else if (readCommandInfo.usedIndex === true && readCommandInfo.consistentRead !== false) {
163
- context.report({
164
- node,
165
- messageId: MESSAGE_ID_CONSISTENT_READ_FALSE,
166
- data: { readCommandType: readCommandInfo.type },
167
- });
168
- }
169
- }
170
- },
171
- };
161
+ messages: {
162
+ [MESSAGE_ID_CONSISTENT_READ_TRUE]:
163
+ 'ConsistentRead option should always be set as true for {{readCommandType}} command.',
164
+ [MESSAGE_ID_CONSISTENT_READ_FALSE]:
165
+ 'ConsistentRead option should not be set as true for {{readCommandType}} command when using a global secondary index.',
172
166
  },
173
- });
167
+ schema: [],
168
+ },
169
+ defaultOptions: [],
170
+ create(context) {
171
+ const sourceCode = context.sourceCode;
172
+
173
+ return {
174
+ ObjectExpression(node) {
175
+ // Quick prefilter: only look at objects that mention table/read-ish keys
176
+ const text = sourceCode.getText(node);
177
+ if (
178
+ !/TableName|RequestItems|KeyConditionExpression|KeyConditions/u.test(
179
+ text,
180
+ )
181
+ ) {
182
+ return;
183
+ }
184
+
185
+ const readCommandInfo = getReadCommandInfo(node);
186
+ if (readCommandInfo !== undefined) {
187
+ if (
188
+ readCommandInfo.usedIndex !== true &&
189
+ readCommandInfo.consistentRead !== true
190
+ ) {
191
+ context.report({
192
+ node,
193
+ messageId: MESSAGE_ID_CONSISTENT_READ_TRUE,
194
+ data: { readCommandType: readCommandInfo.type },
195
+ });
196
+ } else if (
197
+ readCommandInfo.usedIndex === true &&
198
+ readCommandInfo.consistentRead !== false
199
+ ) {
200
+ context.report({
201
+ node,
202
+ messageId: MESSAGE_ID_CONSISTENT_READ_FALSE,
203
+ data: { readCommandType: readCommandInfo.type },
204
+ });
205
+ }
206
+ }
207
+ },
208
+ };
209
+ },
210
+ });
174
211
 
175
212
  export default rule;
@@ -40,7 +40,10 @@ export default {
40
40
  },
41
41
  message: 'first line cannot be a block comment',
42
42
  fix(fixer: Rule.RuleFixer) {
43
- return fixer.insertTextBeforeRange([0, 0], `// ${expectedPath}\n\n`);
43
+ return fixer.insertTextBeforeRange(
44
+ [0, 0],
45
+ `// ${expectedPath}\n\n`,
46
+ );
44
47
  },
45
48
  });
46
49
  } else {
@@ -57,7 +60,10 @@ export default {
57
60
  },
58
61
  message: 'first line is not a comment with the file path',
59
62
  fix(fixer: Rule.RuleFixer) {
60
- return fixer.insertTextBeforeRange([0, 0], `// ${expectedPath}\n\n`);
63
+ return fixer.insertTextBeforeRange(
64
+ [0, 0],
65
+ `// ${expectedPath}\n\n`,
66
+ );
61
67
  },
62
68
  });
63
69
  }
@@ -77,7 +83,10 @@ export default {
77
83
  },
78
84
  message: 'first line is a comment but is not a path to the file',
79
85
  fix(fixer: Rule.RuleFixer) {
80
- return fixer.replaceTextRange([0, firstLine.length], `// ${expectedPath}`);
86
+ return fixer.replaceTextRange(
87
+ [0, firstLine.length],
88
+ `// ${expectedPath}`,
89
+ );
81
90
  },
82
91
  });
83
92
  }
package/src/index.ts CHANGED
@@ -8,10 +8,18 @@
8
8
 
9
9
  import type { TSESLint } from '@typescript-eslint/utils';
10
10
 
11
- import invalidJsonStringify, { ruleId as invalidJsonStringifyRuleId } from './invalid-json-stringify.ts';
12
- import noDuplicatedImports, { ruleId as noDuplicatedImportsRuleId } from './no-duplicated-imports.ts';
13
- import noLegacyServiceTyping, { ruleId as noLegacyServiceTypingRuleId } from './no-legacy-service-typing.ts';
14
- import noPromiseInstanceMethod, { ruleId as noPromiseInstanceMethodRuleId } from './no-promise-instance-method.ts';
11
+ import invalidJsonStringify, {
12
+ ruleId as invalidJsonStringifyRuleId,
13
+ } from './invalid-json-stringify.ts';
14
+ import noDuplicatedImports, {
15
+ ruleId as noDuplicatedImportsRuleId,
16
+ } from './no-duplicated-imports.ts';
17
+ import noLegacyServiceTyping, {
18
+ ruleId as noLegacyServiceTypingRuleId,
19
+ } from './no-legacy-service-typing.ts';
20
+ import noPromiseInstanceMethod, {
21
+ ruleId as noPromiseInstanceMethodRuleId,
22
+ } from './no-promise-instance-method.ts';
15
23
  import noStatusCodeAssert from './no-status-code-assert.ts';
16
24
  import requireFixedServicesImport, {
17
25
  ruleId as requireFixedServicesImportRuleId,
@@ -22,13 +30,21 @@ import requireResolveFullResponse, {
22
30
  import requireTypeOutOfTypeOnlyImports, {
23
31
  ruleId as requireTypeOutOfTypeOnlyImportsRuleId,
24
32
  } from './require-type-out-of-type-only-imports.ts';
25
- import noServeRuntime, { ruleId as noServeRuntimeRuleId } from './no-serve-runtime.ts';
33
+ import noServeRuntime, {
34
+ ruleId as noServeRuntimeRuleId,
35
+ } from './no-serve-runtime.ts';
26
36
  import requireServiceCallResponseDeclaration, {
27
37
  ruleId as requireServiceCallResponseDeclarationRuleId,
28
38
  } from './require-service-call-response-declaration.ts';
29
- import requireAwsConfig, { ruleId as requireAwsConfigRuleId } from './aws/require-aws-config.ts';
30
- import requireAWSBareBones, { ruleId as requireAWSBareBonesRuleId } from './aws/require-aws-bare-bones.ts';
31
- import requireConsistentRead, { ruleId as requireConsistentReadRuleId } from './aws/require-consistent-read.ts';
39
+ import requireAwsConfig, {
40
+ ruleId as requireAwsConfigRuleId,
41
+ } from './aws/require-aws-config.ts';
42
+ import requireAWSBareBones, {
43
+ ruleId as requireAWSBareBonesRuleId,
44
+ } from './aws/require-aws-bare-bones.ts';
45
+ import requireConsistentRead, {
46
+ ruleId as requireConsistentReadRuleId,
47
+ } from './aws/require-consistent-read.ts';
32
48
  import filePathComment from './file-path-comment.ts';
33
49
  import noCardNumbers from './no-card-numbers.ts';
34
50
  import noEnum from './no-enum.ts';
@@ -62,7 +78,8 @@ const rules: Record<string, TSESLint.LooseRuleDefinition> = {
62
78
  'no-wallaby-comment': noWallabyComment,
63
79
  'no-side-effects': noSideEffects,
64
80
  'regular-expression-comment': regexComment,
65
- 'require-assert-predicate-rejects-throws': requireAssertPredicateRejectsThrows,
81
+ 'require-assert-predicate-rejects-throws':
82
+ requireAssertPredicateRejectsThrows,
66
83
  'object-literal-response': objectLiteralResponse,
67
84
  [invalidJsonStringifyRuleId]: invalidJsonStringify,
68
85
  [noPromiseInstanceMethodRuleId]: noPromiseInstanceMethod,
@@ -70,7 +87,8 @@ const rules: Record<string, TSESLint.LooseRuleDefinition> = {
70
87
  [requireResolveFullResponseRuleId]: requireResolveFullResponse,
71
88
  [noDuplicatedImportsRuleId]: noDuplicatedImports,
72
89
  [noServeRuntimeRuleId]: noServeRuntime,
73
- [requireServiceCallResponseDeclarationRuleId]: requireServiceCallResponseDeclaration,
90
+ [requireServiceCallResponseDeclarationRuleId]:
91
+ requireServiceCallResponseDeclaration,
74
92
  [requireAwsConfigRuleId]: requireAwsConfig,
75
93
  [requireFixedServicesImportRuleId]: requireFixedServicesImport,
76
94
  [requireTypeOutOfTypeOnlyImportsRuleId]: requireTypeOutOfTypeOnlyImports,
@@ -25,7 +25,8 @@ export default {
25
25
  {
26
26
  type: 'array',
27
27
  items: {
28
- description: 'Regular expression pattern to match the name of the first parameter of JSON.stringify().',
28
+ description:
29
+ 'Regular expression pattern to match the name of the first parameter of JSON.stringify().',
29
30
  type: 'string',
30
31
  minItems: 1,
31
32
  },
@@ -39,7 +40,9 @@ export default {
39
40
  },
40
41
  create(context) {
41
42
  const options = (context.options[0] ?? DEFAULT_OPTIONS) as string[];
42
- const invalidParameterNamePatterns = options.map((option) => new RegExp(option, 'u'));
43
+ const invalidParameterNamePatterns = options.map(
44
+ (option) => new RegExp(option, 'u'),
45
+ );
43
46
 
44
47
  return {
45
48
  CallExpression(node) {
@@ -67,7 +70,10 @@ export default {
67
70
  parameterName: argument.name,
68
71
  },
69
72
  fix(fixer) {
70
- return fixer.replaceText(node, `String(${argument.name})`);
73
+ return fixer.replaceText(
74
+ node,
75
+ `String(${argument.name})`,
76
+ );
71
77
  },
72
78
  },
73
79
  ],
@@ -11,7 +11,10 @@ import { TSESLint, TSESTree } from '@typescript-eslint/utils';
11
11
  import type { Node } from 'estree';
12
12
  import type { SourceCode } from 'eslint';
13
13
 
14
- export function getIndentation(node: Node | TSESTree.Node, sourceCode: SourceCode | TSESLint.SourceCode): string {
14
+ export function getIndentation(
15
+ node: Node | TSESTree.Node,
16
+ sourceCode: SourceCode | TSESLint.SourceCode,
17
+ ): string {
15
18
  assert.ok(node.loc);
16
19
  const line = sourceCode.lines[node.loc.start.line - 1];
17
20
  assert.ok(line !== undefined);
@@ -48,7 +48,12 @@ export function getEnclosingStatement(node: Node): Node | undefined {
48
48
 
49
49
  export function getEnclosingScopeNode(node: Node): Node | undefined {
50
50
  return getAncestor(node, (parentNode) =>
51
- ['FunctionExpression', 'FunctionDeclaration', 'ArrowFunctionExpression', 'Program'].includes(parentNode.type),
51
+ [
52
+ 'FunctionExpression',
53
+ 'FunctionDeclaration',
54
+ 'ArrowFunctionExpression',
55
+ 'Program',
56
+ ].includes(parentNode.type),
52
57
  );
53
58
  }
54
59
 
@@ -65,7 +70,8 @@ export function isUsedInArrayOrAsArgument(node: Node): boolean {
65
70
  if (
66
71
  parent.type === 'ArrayExpression' ||
67
72
  parent.type === 'ArrowFunctionExpression' ||
68
- (parent.type === 'CallExpression' && parent.arguments.includes(node as Expression))
73
+ (parent.type === 'CallExpression' &&
74
+ parent.arguments.includes(node as Expression))
69
75
  ) {
70
76
  return true;
71
77
  }
@@ -14,7 +14,9 @@ interface NodeParentExtension {
14
14
  parent: NodeParent;
15
15
  }
16
16
 
17
- export function getParent(node: TSESTree.Node): TSESTree.Node | undefined | null {
17
+ export function getParent(
18
+ node: TSESTree.Node,
19
+ ): TSESTree.Node | undefined | null {
18
20
  return (node as unknown as NodeParentExtension).parent;
19
21
  }
20
22
 
@@ -42,13 +44,22 @@ export function isBlockStatement(node: TSESTree.Node): boolean {
42
44
  return node.type.endsWith('Statement') || node.type.endsWith('Declaration');
43
45
  }
44
46
 
45
- export function getEnclosingStatement(node: TSESTree.Node): TSESTree.Node | undefined {
47
+ export function getEnclosingStatement(
48
+ node: TSESTree.Node,
49
+ ): TSESTree.Node | undefined {
46
50
  return getAncestor(node, isBlockStatement);
47
51
  }
48
52
 
49
- export function getEnclosingScopeNode(node: TSESTree.Node): TSESTree.Node | undefined {
53
+ export function getEnclosingScopeNode(
54
+ node: TSESTree.Node,
55
+ ): TSESTree.Node | undefined {
50
56
  return getAncestor(node, (parentNode) =>
51
- ['FunctionExpression', 'FunctionDeclaration', 'ArrowFunctionExpression', 'Program'].includes(parentNode.type),
57
+ [
58
+ 'FunctionExpression',
59
+ 'FunctionDeclaration',
60
+ 'ArrowFunctionExpression',
61
+ 'Program',
62
+ ].includes(parentNode.type),
52
63
  );
53
64
  }
54
65
 
@@ -64,7 +75,8 @@ export function isUsedInArrayOrAsArgument(node: TSESTree.Node): boolean {
64
75
 
65
76
  if (
66
77
  parent.type === AST_NODE_TYPES.ArrayExpression ||
67
- (parent.type === AST_NODE_TYPES.CallExpression && parent.arguments.includes(node as TSESTree.Expression))
78
+ (parent.type === AST_NODE_TYPES.CallExpression &&
79
+ parent.arguments.includes(node as TSESTree.Expression))
68
80
  ) {
69
81
  return true;
70
82
  }
@@ -75,7 +87,11 @@ export function isUsedInArrayOrAsArgument(node: TSESTree.Node): boolean {
75
87
 
76
88
  export function getEnclosingFunction(
77
89
  node: TSESTree.Node,
78
- ): TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | undefined {
90
+ ):
91
+ | TSESTree.ArrowFunctionExpression
92
+ | TSESTree.FunctionDeclaration
93
+ | TSESTree.FunctionExpression
94
+ | undefined {
79
95
  if (
80
96
  node.type === AST_NODE_TYPES.FunctionDeclaration ||
81
97
  node.type === AST_NODE_TYPES.FunctionExpression ||
@@ -97,7 +113,8 @@ export function getTypeParentNode(
97
113
  if (!node) {
98
114
  return undefined;
99
115
  }
100
- return node.type === AST_NODE_TYPES.TSTypeAnnotation || node.type === AST_NODE_TYPES.TSAsExpression
116
+ return node.type === AST_NODE_TYPES.TSTypeAnnotation ||
117
+ node.type === AST_NODE_TYPES.TSAsExpression
101
118
  ? node
102
119
  : getTypeParentNode(node.parent);
103
120
  }
@@ -44,7 +44,12 @@ function luhnCheck(cardNumber: string) {
44
44
  );
45
45
  }
46
46
 
47
- function checkForCardNumbers(value: string, context: Rule.RuleContext, node?: Node, loc?: SourceLocation) {
47
+ function checkForCardNumbers(
48
+ value: string,
49
+ context: Rule.RuleContext,
50
+ node?: Node,
51
+ loc?: SourceLocation,
52
+ ) {
48
53
  const matches = value.match(cardNumberRegex);
49
54
  if (matches === null) {
50
55
  return;
@@ -24,7 +24,8 @@ const rule: ESLintUtils.RuleModule<'mergeDuplicatedImports'> = createRule({
24
24
  description: 'Merge duplicated import statements with the same "from".',
25
25
  },
26
26
  messages: {
27
- mergeDuplicatedImports: 'Merge duplicated import statements with the same "from".',
27
+ mergeDuplicatedImports:
28
+ 'Merge duplicated import statements with the same "from".',
28
29
  },
29
30
  fixable: 'code',
30
31
  schema: [],
@@ -45,11 +46,16 @@ const rule: ESLintUtils.RuleModule<'mergeDuplicatedImports'> = createRule({
45
46
  declarations.push(node);
46
47
  },
47
48
  'Program:exit'() {
48
- for (const [moduleName, allDeclarations] of importDeclarations.entries()) {
49
+ for (const [
50
+ moduleName,
51
+ allDeclarations,
52
+ ] of importDeclarations.entries()) {
49
53
  const declarations = allDeclarations.filter(
50
54
  (declaration) =>
51
55
  !declaration.specifiers.some(
52
- (specifier) => specifier.type === TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier,
56
+ (specifier) =>
57
+ specifier.type ===
58
+ TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier,
53
59
  ),
54
60
  );
55
61
  if (declarations.length <= 1) {
@@ -64,7 +70,8 @@ const rule: ESLintUtils.RuleModule<'mergeDuplicatedImports'> = createRule({
64
70
  declaration.importKind === 'type' ||
65
71
  declaration.specifiers.every(
66
72
  (specifier) =>
67
- specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === 'type',
73
+ specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier &&
74
+ specifier.importKind === 'type',
68
75
  ),
69
76
  );
70
77
 
@@ -76,30 +83,48 @@ const rule: ESLintUtils.RuleModule<'mergeDuplicatedImports'> = createRule({
76
83
 
77
84
  const defaultSpecifier = declarations
78
85
  .flatMap((declaration) =>
86
+ // eslint-disable-next-line sonarjs/no-nested-functions
79
87
  declaration.specifiers.map((specifier) =>
80
- specifier.type === TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier ? specifier : undefined,
88
+ specifier.type ===
89
+ TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier
90
+ ? specifier
91
+ : undefined,
81
92
  ),
82
93
  )
83
94
  .filter(Boolean);
84
- const defaultSpecifierText = defaultSpecifier[0] ? sourceCode.getText(defaultSpecifier[0]) : undefined;
95
+ const defaultSpecifierText = defaultSpecifier[0]
96
+ ? sourceCode.getText(defaultSpecifier[0])
97
+ : undefined;
85
98
 
86
99
  const mergedSpecifiers = declarations.flatMap((declaration) => {
87
100
  const isCurrentDeclarationTypeOnly =
88
101
  declaration.importKind === 'type' ||
89
102
  declaration.specifiers.every(
103
+ // eslint-disable-next-line sonarjs/no-nested-functions
90
104
  (specifier) =>
91
- specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === 'type',
92
- );
93
- return declaration.specifiers
94
- .filter((specifier) => specifier.type !== TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier)
95
- .map((specifier) =>
96
- // eslint-disable-next-line no-nested-ternary
97
- isAllTypeOnly
98
- ? sourceCode.getText(specifier).replace('type ', '')
99
- : isCurrentDeclarationTypeOnly
100
- ? `type ${sourceCode.getText(specifier)}`
101
- : sourceCode.getText(specifier),
105
+ specifier.type ===
106
+ TSESTree.AST_NODE_TYPES.ImportSpecifier &&
107
+ specifier.importKind === 'type',
102
108
  );
109
+ return (
110
+ declaration.specifiers
111
+ .filter(
112
+ // eslint-disable-next-line sonarjs/no-nested-functions
113
+ (specifier) =>
114
+ specifier.type !==
115
+ TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier,
116
+ )
117
+ // eslint-disable-next-line sonarjs/no-nested-functions
118
+ .map((specifier) =>
119
+ // eslint-disable-next-line no-nested-ternary
120
+ isAllTypeOnly
121
+ ? sourceCode.getText(specifier).replace('type ', '')
122
+ : // eslint-disable-next-line sonarjs/no-nested-conditional
123
+ isCurrentDeclarationTypeOnly
124
+ ? `type ${sourceCode.getText(specifier)}`
125
+ : sourceCode.getText(specifier),
126
+ )
127
+ );
103
128
  });
104
129
  const mergedSpecifiersText = `${isAllTypeOnly ? 'type ' : ''}{ ${mergedSpecifiers.join(', ')} }`;
105
130
 
@@ -109,7 +134,12 @@ const rule: ESLintUtils.RuleModule<'mergeDuplicatedImports'> = createRule({
109
134
 
110
135
  // Remove the remaining imports
111
136
  declarations.slice(1).forEach((declaration) => {
112
- fixes.push(fixer.removeRange([declaration.range[0], declaration.range[1] + 1]));
137
+ fixes.push(
138
+ fixer.removeRange([
139
+ declaration.range[0],
140
+ declaration.range[1] + 1,
141
+ ]),
142
+ );
113
143
  });
114
144
 
115
145
  return fixes;
@@ -6,16 +6,26 @@
6
6
  * This code is licensed under the MIT license (see LICENSE.txt for details).
7
7
  */
8
8
 
9
- import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
9
+ import {
10
+ AST_NODE_TYPES,
11
+ ESLintUtils,
12
+ TSESTree,
13
+ } from '@typescript-eslint/utils';
10
14
  import getDocumentationUrl from './get-documentation-url.ts';
11
15
 
12
16
  export const ruleId = 'no-legacy-service-typing';
13
17
 
14
18
  const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
15
19
 
16
- const DISALLOWED_SERVICE_TYPINGS: string[] | undefined = ['FullResponse', 'Endpoint'];
20
+ const DISALLOWED_SERVICE_TYPINGS: string[] | undefined = [
21
+ 'FullResponse',
22
+ 'Endpoint',
23
+ ];
17
24
 
18
- const rule: ESLintUtils.RuleModule<'noLegacyServiceTyping', [typeof DISALLOWED_SERVICE_TYPINGS]> = createRule({
25
+ const rule: ESLintUtils.RuleModule<
26
+ 'noLegacyServiceTyping',
27
+ [typeof DISALLOWED_SERVICE_TYPINGS]
28
+ > = createRule({
19
29
  name: ruleId,
20
30
  meta: {
21
31
  type: 'problem',
@@ -23,18 +33,25 @@ const rule: ESLintUtils.RuleModule<'noLegacyServiceTyping', [typeof DISALLOWED_S
23
33
  description: 'Legacy service typings should not be used.',
24
34
  },
25
35
  messages: {
26
- noLegacyServiceTyping: 'Please remove the usage of legacy service typings.',
36
+ noLegacyServiceTyping:
37
+ 'Please remove the usage of legacy service typings.',
27
38
  },
28
- schema: [{ type: 'array', items: { type: 'string' } }],
39
+
40
+ schema: [
41
+ {
42
+ type: 'array',
43
+ items: { type: 'string' },
44
+ description: 'Legacy service typings should not be used.',
45
+ },
46
+ ],
47
+ defaultOptions: [DISALLOWED_SERVICE_TYPINGS],
29
48
  },
30
- defaultOptions: [DISALLOWED_SERVICE_TYPINGS],
31
49
  create(context) {
32
50
  return {
33
51
  TSTypeReference: (typeReference: TSESTree.TSTypeReference) => {
34
52
  if (
35
53
  typeReference.typeName.type === AST_NODE_TYPES.Identifier &&
36
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
37
- (context.options[0] ?? DISALLOWED_SERVICE_TYPINGS).includes(typeReference.typeName.name)
54
+ context.options[0].includes(typeReference.typeName.name)
38
55
  ) {
39
56
  context.report({
40
57
  messageId: 'noLegacyServiceTyping',