eslint-plugin-complete 1.0.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.
Files changed (171) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +114 -0
  3. package/dist/comments.d.ts +22 -0
  4. package/dist/comments.d.ts.map +1 -0
  5. package/dist/comments.js +66 -0
  6. package/dist/completeCommon.d.ts +25 -0
  7. package/dist/completeCommon.d.ts.map +1 -0
  8. package/dist/completeCommon.js +53 -0
  9. package/dist/completeSentence.d.ts +9 -0
  10. package/dist/completeSentence.d.ts.map +1 -0
  11. package/dist/completeSentence.js +267 -0
  12. package/dist/configs/recommended.d.ts +3 -0
  13. package/dist/configs/recommended.d.ts.map +1 -0
  14. package/dist/configs/recommended.js +63 -0
  15. package/dist/configs.d.ts +4 -0
  16. package/dist/configs.d.ts.map +1 -0
  17. package/dist/configs.js +4 -0
  18. package/dist/constants.d.ts +8 -0
  19. package/dist/constants.d.ts.map +1 -0
  20. package/dist/constants.js +73 -0
  21. package/dist/format.d.ts +18 -0
  22. package/dist/format.d.ts.map +1 -0
  23. package/dist/format.js +246 -0
  24. package/dist/index.d.ts +60 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +49 -0
  27. package/dist/interfaces/MyPluginDocs.d.ts +6 -0
  28. package/dist/interfaces/MyPluginDocs.d.ts.map +1 -0
  29. package/dist/interfaces/MyPluginDocs.js +1 -0
  30. package/dist/jsdoc.d.ts +4 -0
  31. package/dist/jsdoc.d.ts.map +1 -0
  32. package/dist/jsdoc.js +24 -0
  33. package/dist/leadingLineComments.d.ts +32 -0
  34. package/dist/leadingLineComments.d.ts.map +1 -0
  35. package/dist/leadingLineComments.js +77 -0
  36. package/dist/list.d.ts +49 -0
  37. package/dist/list.d.ts.map +1 -0
  38. package/dist/list.js +140 -0
  39. package/dist/rules/complete-sentences-jsdoc.d.ts +4 -0
  40. package/dist/rules/complete-sentences-jsdoc.d.ts.map +1 -0
  41. package/dist/rules/complete-sentences-jsdoc.js +48 -0
  42. package/dist/rules/complete-sentences-line-comments.d.ts +4 -0
  43. package/dist/rules/complete-sentences-line-comments.d.ts.map +1 -0
  44. package/dist/rules/complete-sentences-line-comments.js +88 -0
  45. package/dist/rules/consistent-enum-values.d.ts +2 -0
  46. package/dist/rules/consistent-enum-values.d.ts.map +1 -0
  47. package/dist/rules/consistent-enum-values.js +46 -0
  48. package/dist/rules/consistent-named-tuples.d.ts +2 -0
  49. package/dist/rules/consistent-named-tuples.d.ts.map +1 -0
  50. package/dist/rules/consistent-named-tuples.js +34 -0
  51. package/dist/rules/eqeqeq-fix.d.ts +2 -0
  52. package/dist/rules/eqeqeq-fix.d.ts.map +1 -0
  53. package/dist/rules/eqeqeq-fix.js +173 -0
  54. package/dist/rules/format-jsdoc-comments.d.ts +8 -0
  55. package/dist/rules/format-jsdoc-comments.d.ts.map +1 -0
  56. package/dist/rules/format-jsdoc-comments.js +117 -0
  57. package/dist/rules/format-line-comments.d.ts +8 -0
  58. package/dist/rules/format-line-comments.d.ts.map +1 -0
  59. package/dist/rules/format-line-comments.js +118 -0
  60. package/dist/rules/jsdoc-code-block-language.d.ts +2 -0
  61. package/dist/rules/jsdoc-code-block-language.d.ts.map +1 -0
  62. package/dist/rules/jsdoc-code-block-language.js +52 -0
  63. package/dist/rules/newline-between-switch-case.d.ts +4 -0
  64. package/dist/rules/newline-between-switch-case.d.ts.map +1 -0
  65. package/dist/rules/newline-between-switch-case.js +63 -0
  66. package/dist/rules/no-confusing-set-methods.d.ts +5 -0
  67. package/dist/rules/no-confusing-set-methods.d.ts.map +1 -0
  68. package/dist/rules/no-confusing-set-methods.js +51 -0
  69. package/dist/rules/no-empty-jsdoc.d.ts +2 -0
  70. package/dist/rules/no-empty-jsdoc.d.ts.map +1 -0
  71. package/dist/rules/no-empty-jsdoc.js +45 -0
  72. package/dist/rules/no-empty-line-comments.d.ts +2 -0
  73. package/dist/rules/no-empty-line-comments.d.ts.map +1 -0
  74. package/dist/rules/no-empty-line-comments.js +40 -0
  75. package/dist/rules/no-explicit-array-loops.d.ts +5 -0
  76. package/dist/rules/no-explicit-array-loops.d.ts.map +1 -0
  77. package/dist/rules/no-explicit-array-loops.js +114 -0
  78. package/dist/rules/no-explicit-map-set-loops.d.ts +5 -0
  79. package/dist/rules/no-explicit-map-set-loops.d.ts.map +1 -0
  80. package/dist/rules/no-explicit-map-set-loops.js +74 -0
  81. package/dist/rules/no-for-in.d.ts +2 -0
  82. package/dist/rules/no-for-in.d.ts.map +1 -0
  83. package/dist/rules/no-for-in.js +27 -0
  84. package/dist/rules/no-let-any.d.ts +3 -0
  85. package/dist/rules/no-let-any.d.ts.map +1 -0
  86. package/dist/rules/no-let-any.js +45 -0
  87. package/dist/rules/no-mutable-return.d.ts +5 -0
  88. package/dist/rules/no-mutable-return.d.ts.map +1 -0
  89. package/dist/rules/no-mutable-return.js +63 -0
  90. package/dist/rules/no-number-enums.d.ts +2 -0
  91. package/dist/rules/no-number-enums.d.ts.map +1 -0
  92. package/dist/rules/no-number-enums.js +27 -0
  93. package/dist/rules/no-object-any.d.ts +3 -0
  94. package/dist/rules/no-object-any.d.ts.map +1 -0
  95. package/dist/rules/no-object-any.js +51 -0
  96. package/dist/rules/no-object-methods-with-map-set.d.ts +5 -0
  97. package/dist/rules/no-object-methods-with-map-set.d.ts.map +1 -0
  98. package/dist/rules/no-object-methods-with-map-set.js +84 -0
  99. package/dist/rules/no-string-length-0.d.ts +3 -0
  100. package/dist/rules/no-string-length-0.d.ts.map +1 -0
  101. package/dist/rules/no-string-length-0.js +52 -0
  102. package/dist/rules/no-template-curly-in-string-fix.d.ts +6 -0
  103. package/dist/rules/no-template-curly-in-string-fix.d.ts.map +1 -0
  104. package/dist/rules/no-template-curly-in-string-fix.js +39 -0
  105. package/dist/rules/no-undefined-return-type.d.ts +3 -0
  106. package/dist/rules/no-undefined-return-type.d.ts.map +1 -0
  107. package/dist/rules/no-undefined-return-type.js +40 -0
  108. package/dist/rules/no-unnecessary-assignment.d.ts +5 -0
  109. package/dist/rules/no-unnecessary-assignment.d.ts.map +1 -0
  110. package/dist/rules/no-unnecessary-assignment.js +255 -0
  111. package/dist/rules/no-unsafe-plusplus.d.ts +2 -0
  112. package/dist/rules/no-unsafe-plusplus.d.ts.map +1 -0
  113. package/dist/rules/no-unsafe-plusplus.js +34 -0
  114. package/dist/rules/no-useless-return.d.ts +2 -0
  115. package/dist/rules/no-useless-return.d.ts.map +1 -0
  116. package/dist/rules/no-useless-return.js +347 -0
  117. package/dist/rules/no-void-return-type.d.ts +2 -0
  118. package/dist/rules/no-void-return-type.d.ts.map +1 -0
  119. package/dist/rules/no-void-return-type.js +49 -0
  120. package/dist/rules/prefer-const.d.ts +2 -0
  121. package/dist/rules/prefer-const.d.ts.map +1 -0
  122. package/dist/rules/prefer-const.js +426 -0
  123. package/dist/rules/prefer-plusplus.d.ts +5 -0
  124. package/dist/rules/prefer-plusplus.d.ts.map +1 -0
  125. package/dist/rules/prefer-plusplus.js +49 -0
  126. package/dist/rules/prefer-postfix-plusplus.d.ts +2 -0
  127. package/dist/rules/prefer-postfix-plusplus.d.ts.map +1 -0
  128. package/dist/rules/prefer-postfix-plusplus.js +32 -0
  129. package/dist/rules/prefer-readonly-parameter-types.d.ts +13 -0
  130. package/dist/rules/prefer-readonly-parameter-types.d.ts.map +1 -0
  131. package/dist/rules/prefer-readonly-parameter-types.js +140 -0
  132. package/dist/rules/require-break.d.ts +5 -0
  133. package/dist/rules/require-break.d.ts.map +1 -0
  134. package/dist/rules/require-break.js +76 -0
  135. package/dist/rules/require-capital-const-assertions.d.ts +4 -0
  136. package/dist/rules/require-capital-const-assertions.d.ts.map +1 -0
  137. package/dist/rules/require-capital-const-assertions.js +112 -0
  138. package/dist/rules/require-capital-read-only.d.ts +5 -0
  139. package/dist/rules/require-capital-read-only.d.ts.map +1 -0
  140. package/dist/rules/require-capital-read-only.js +111 -0
  141. package/dist/rules/require-unannotated-const-assertions.d.ts +2 -0
  142. package/dist/rules/require-unannotated-const-assertions.d.ts.map +1 -0
  143. package/dist/rules/require-unannotated-const-assertions.js +27 -0
  144. package/dist/rules/require-variadic-function-argument.d.ts +5 -0
  145. package/dist/rules/require-variadic-function-argument.d.ts.map +1 -0
  146. package/dist/rules/require-variadic-function-argument.js +86 -0
  147. package/dist/rules/strict-array-methods.d.ts +3 -0
  148. package/dist/rules/strict-array-methods.d.ts.map +1 -0
  149. package/dist/rules/strict-array-methods.js +83 -0
  150. package/dist/rules/strict-enums.d.ts +5 -0
  151. package/dist/rules/strict-enums.d.ts.map +1 -0
  152. package/dist/rules/strict-enums.js +445 -0
  153. package/dist/rules/strict-undefined-functions.d.ts +5 -0
  154. package/dist/rules/strict-undefined-functions.d.ts.map +1 -0
  155. package/dist/rules/strict-undefined-functions.js +49 -0
  156. package/dist/rules/strict-void-functions.d.ts +2 -0
  157. package/dist/rules/strict-void-functions.d.ts.map +1 -0
  158. package/dist/rules/strict-void-functions.js +43 -0
  159. package/dist/rules.d.ts +49 -0
  160. package/dist/rules.d.ts.map +1 -0
  161. package/dist/rules.js +85 -0
  162. package/dist/template.d.ts +2 -0
  163. package/dist/template.d.ts.map +1 -0
  164. package/dist/template.js +29 -0
  165. package/dist/typeUtils.d.ts +28 -0
  166. package/dist/typeUtils.d.ts.map +1 -0
  167. package/dist/typeUtils.js +76 -0
  168. package/dist/utils.d.ts +13 -0
  169. package/dist/utils.d.ts.map +1 -0
  170. package/dist/utils.js +54 -0
  171. package/package.json +55 -0
@@ -0,0 +1,140 @@
1
+ // cspell:ignore readonlyness
2
+ import { isTypeReadonly, readonlynessOptionsDefaults, readonlynessOptionsSchema, } from "@typescript-eslint/type-utils";
3
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
+ import { getParserServices } from "@typescript-eslint/utils/eslint-utils";
5
+ import { getTypeName, unionTypeParts } from "../typeUtils.js";
6
+ import { createRule } from "../utils.js";
7
+ export const preferReadonlyParameterTypes = createRule({
8
+ name: "prefer-readonly-parameter-types",
9
+ meta: {
10
+ type: "suggestion",
11
+ docs: {
12
+ description: "Require function parameters to be typed as `readonly` to prevent accidental mutation of inputs",
13
+ recommended: true,
14
+ requiresTypeChecking: true,
15
+ },
16
+ schema: [
17
+ {
18
+ type: "object",
19
+ additionalProperties: false,
20
+ properties: {
21
+ allow: readonlynessOptionsSchema.properties.allow,
22
+ checkParameterProperties: {
23
+ type: "boolean",
24
+ },
25
+ ignoreInferredTypes: {
26
+ type: "boolean",
27
+ },
28
+ treatMethodsAsReadonly: readonlynessOptionsSchema.properties.treatMethodsAsReadonly,
29
+ onlyRecordsArraysMapsSet: {
30
+ type: "boolean",
31
+ },
32
+ },
33
+ },
34
+ ],
35
+ messages: {
36
+ shouldBeReadonly: "Parameter should be a read only type.",
37
+ },
38
+ },
39
+ defaultOptions: [
40
+ {
41
+ allow: readonlynessOptionsDefaults.allow,
42
+ checkParameterProperties: true,
43
+ ignoreInferredTypes: false,
44
+ treatMethodsAsReadonly: readonlynessOptionsDefaults.treatMethodsAsReadonly,
45
+ onlyRecordsArraysMapsSet: true,
46
+ },
47
+ ],
48
+ create(context, [{ allow, checkParameterProperties, ignoreInferredTypes, treatMethodsAsReadonly, onlyRecordsArraysMapsSet, },]) {
49
+ const services = getParserServices(context);
50
+ if (allow === undefined) {
51
+ allow = [];
52
+ }
53
+ allow.push("ReadonlyMap", "ReadonlySet");
54
+ return {
55
+ [[
56
+ AST_NODE_TYPES.ArrowFunctionExpression,
57
+ AST_NODE_TYPES.FunctionDeclaration,
58
+ AST_NODE_TYPES.FunctionExpression,
59
+ AST_NODE_TYPES.TSCallSignatureDeclaration,
60
+ AST_NODE_TYPES.TSConstructSignatureDeclaration,
61
+ AST_NODE_TYPES.TSDeclareFunction,
62
+ AST_NODE_TYPES.TSEmptyBodyFunctionExpression,
63
+ AST_NODE_TYPES.TSFunctionType,
64
+ AST_NODE_TYPES.TSMethodSignature,
65
+ ].join(", ")](node) {
66
+ for (const param of node.params) {
67
+ if (checkParameterProperties === false &&
68
+ param.type === AST_NODE_TYPES.TSParameterProperty) {
69
+ continue;
70
+ }
71
+ const actualParam = param.type === AST_NODE_TYPES.TSParameterProperty
72
+ ? param.parameter
73
+ : param;
74
+ if (ignoreInferredTypes === true &&
75
+ actualParam.typeAnnotation === undefined) {
76
+ continue;
77
+ }
78
+ const type = services.getTypeAtLocation(actualParam);
79
+ if (onlyRecordsArraysMapsSet === true) {
80
+ const parts = unionTypeParts(type);
81
+ const hasAllBasicDataStructures = parts.every((t) => {
82
+ const typeName = getTypeName(t);
83
+ return (typeName === "Array" ||
84
+ typeName === "Map" ||
85
+ typeName === "Set" ||
86
+ typeName === "Record");
87
+ });
88
+ if (!hasAllBasicDataStructures) {
89
+ return;
90
+ }
91
+ // Handle the case of only checking records, arrays, maps, and sets.
92
+ for (const t of parts) {
93
+ const typeName = getTypeName(t);
94
+ switch (typeName) {
95
+ case "Array":
96
+ case "Map":
97
+ case "Set": {
98
+ context.report({
99
+ node: actualParam,
100
+ messageId: "shouldBeReadonly",
101
+ });
102
+ break;
103
+ }
104
+ case "Record": {
105
+ const isReadOnly = isTypeReadonly(services.program, type, {
106
+ treatMethodsAsReadonly,
107
+ allow,
108
+ });
109
+ if (!isReadOnly) {
110
+ context.report({
111
+ node: actualParam,
112
+ messageId: "shouldBeReadonly",
113
+ });
114
+ }
115
+ break;
116
+ }
117
+ default: {
118
+ break;
119
+ }
120
+ }
121
+ }
122
+ }
123
+ else {
124
+ // Handle the standard case.
125
+ const isReadOnly = isTypeReadonly(services.program, type, {
126
+ treatMethodsAsReadonly,
127
+ allow,
128
+ });
129
+ if (!isReadOnly) {
130
+ context.report({
131
+ node: actualParam,
132
+ messageId: "shouldBeReadonly",
133
+ });
134
+ }
135
+ }
136
+ }
137
+ },
138
+ };
139
+ },
140
+ });
@@ -0,0 +1,5 @@
1
+ import type { TSESLint } from "@typescript-eslint/utils";
2
+ export type Options = [];
3
+ export type MessageIds = "noBreak";
4
+ export declare const requireBreak: TSESLint.RuleModule<"noBreak", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, TSESLint.RuleListener>;
5
+ //# sourceMappingURL=require-break.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-break.d.ts","sourceRoot":"","sources":["../../src/rules/require-break.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGzD,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC;AAEnC,eAAO,MAAM,YAAY,iHAqEvB,CAAC"}
@@ -0,0 +1,76 @@
1
+ // This rule is mostly copied from the `no-fallthrough` ESLint core rule:
2
+ // https://github.com/eslint/eslint/blob/main/lib/rules/no-fallthrough.js
3
+ import { createRule } from "../utils.js";
4
+ export const requireBreak = createRule({
5
+ name: "require-break",
6
+ meta: {
7
+ type: "layout",
8
+ docs: {
9
+ description: "Requires that each case of a switch statement has a `break` statement",
10
+ recommended: true,
11
+ requiresTypeChecking: false,
12
+ },
13
+ schema: [],
14
+ messages: {
15
+ noBreak: "Expected a 'break' statement.",
16
+ },
17
+ },
18
+ defaultOptions: [],
19
+ create(context) {
20
+ const codePathSegments = [];
21
+ let currentCodePathSegments = new Set();
22
+ return {
23
+ onCodePathStart() {
24
+ codePathSegments.push(currentCodePathSegments);
25
+ currentCodePathSegments = new Set();
26
+ },
27
+ onCodePathEnd() {
28
+ const codePathSegmentsSet = codePathSegments.pop();
29
+ if (codePathSegmentsSet !== undefined) {
30
+ currentCodePathSegments = codePathSegmentsSet;
31
+ }
32
+ },
33
+ onUnreachableCodePathSegmentStart(segment) {
34
+ currentCodePathSegments.add(segment);
35
+ },
36
+ onUnreachableCodePathSegmentEnd(segment) {
37
+ currentCodePathSegments.delete(segment);
38
+ },
39
+ onCodePathSegmentStart(segment) {
40
+ currentCodePathSegments.add(segment);
41
+ },
42
+ onCodePathSegmentEnd(segment) {
43
+ currentCodePathSegments.delete(segment);
44
+ },
45
+ "SwitchCase:exit"(node) {
46
+ /*
47
+ * `reachable` means fallthrough because statements preceded by
48
+ * `break`, `return`, or `throw` are unreachable.
49
+ * And allows empty cases and the last case.
50
+ */
51
+ const thisNodeIsReachable = isAnySegmentReachable(currentCodePathSegments);
52
+ const thisNodeIsFinalCase = node === node.parent.cases.at(-1);
53
+ if (thisNodeIsReachable && thisNodeIsFinalCase) {
54
+ context.report({
55
+ messageId: "noBreak",
56
+ node,
57
+ });
58
+ }
59
+ },
60
+ };
61
+ },
62
+ });
63
+ /**
64
+ * Checks all segments in a set and returns true if any are reachable.
65
+ *
66
+ * @param segments The segments to check.
67
+ * @returns True if any segment is reachable; false otherwise.
68
+ */
69
+ function isAnySegmentReachable(segments) {
70
+ for (const segment of segments) {
71
+ if (segment.reachable) {
72
+ return true;
73
+ }
74
+ }
75
+ return false;
76
+ }
@@ -0,0 +1,4 @@
1
+ export type Options = [];
2
+ export type MessageIds = "noConstAssertion";
3
+ export declare const requireCapitalConstAssertions: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noConstAssertion", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
4
+ //# sourceMappingURL=require-capital-const-assertions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-capital-const-assertions.d.ts","sourceRoot":"","sources":["../../src/rules/require-capital-const-assertions.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAkB5C,eAAO,MAAM,6BAA6B,kMA0ExC,CAAC"}
@@ -0,0 +1,112 @@
1
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
2
+ import { isFirstLetterCapitalized } from "../completeCommon.js";
3
+ import { createRule } from "../utils.js";
4
+ const ARRAY_OR_OBJECT_EXPRESSION_TYPES = new Set([
5
+ AST_NODE_TYPES.ObjectExpression,
6
+ AST_NODE_TYPES.ArrayExpression,
7
+ AST_NODE_TYPES.TSSatisfiesExpression,
8
+ ]);
9
+ const AUTO_FIX_TYPE_BLACKLIST = new Set([
10
+ // If the variable is already being casted to something with "as", then don't apply any fix to
11
+ // avoid stomping on the existing code.
12
+ AST_NODE_TYPES.TSAsExpression,
13
+ // The `satisfies` keyword will mess up the auto-fixer, because "as const" will be placed after
14
+ // the satisfies part.
15
+ AST_NODE_TYPES.TSSatisfiesExpression,
16
+ ]);
17
+ export const requireCapitalConstAssertions = createRule({
18
+ name: "require-capital-const-assertions",
19
+ meta: {
20
+ type: "problem",
21
+ docs: {
22
+ description: "Requires a capital letter for named objects and arrays that have a const assertion",
23
+ recommended: true,
24
+ requiresTypeChecking: false,
25
+ },
26
+ schema: [],
27
+ messages: {
28
+ noConstAssertion: "Variables with capital letters that assign an object or array must use const assertions.",
29
+ },
30
+ fixable: "code",
31
+ },
32
+ defaultOptions: [],
33
+ create(context) {
34
+ return {
35
+ VariableDeclaration(node) {
36
+ if (node.kind !== "const") {
37
+ return;
38
+ }
39
+ for (const declaration of node.declarations) {
40
+ const { id } = declaration;
41
+ if (id.type !== AST_NODE_TYPES.Identifier) {
42
+ continue;
43
+ }
44
+ if (!isFirstLetterCapitalized(id.name)) {
45
+ continue;
46
+ }
47
+ const { init } = declaration;
48
+ if (init === null) {
49
+ continue;
50
+ }
51
+ // Do nothing if this is not an object or array expression.
52
+ if (!ARRAY_OR_OBJECT_EXPRESSION_TYPES.has(init.type)) {
53
+ continue;
54
+ }
55
+ if (hasConstAssertion(init)) {
56
+ continue;
57
+ }
58
+ context.report({
59
+ loc: node.loc,
60
+ messageId: "noConstAssertion",
61
+ fix: (fixer) => {
62
+ // If this variable isn't being assigned to anything, then there is nothing we can
63
+ // fix.
64
+ if (declaration.init === null) {
65
+ return null;
66
+ }
67
+ if (declaration.init.type === AST_NODE_TYPES.TSAsExpression) {
68
+ return null;
69
+ }
70
+ if (AUTO_FIX_TYPE_BLACKLIST.has(declaration.init.type)) {
71
+ return null;
72
+ }
73
+ return fixer.insertTextAfter(declaration.init, " as const");
74
+ },
75
+ });
76
+ }
77
+ },
78
+ };
79
+ },
80
+ });
81
+ function hasConstAssertion(init) {
82
+ switch (init.type) {
83
+ case AST_NODE_TYPES.TSAsExpression: {
84
+ return hasConstAssertionWithoutSatisfies(init);
85
+ }
86
+ case AST_NODE_TYPES.TSSatisfiesExpression: {
87
+ return hasConstAssertionWithSatisfies(init);
88
+ }
89
+ default: {
90
+ return false;
91
+ }
92
+ }
93
+ }
94
+ function hasConstAssertionWithoutSatisfies(init) {
95
+ const { typeAnnotation } = init;
96
+ if (typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) {
97
+ return false;
98
+ }
99
+ const { typeName } = typeAnnotation;
100
+ if (typeName.type !== AST_NODE_TYPES.Identifier) {
101
+ return false;
102
+ }
103
+ return typeName.name === "const";
104
+ }
105
+ /** The "as const" part is nested within the `TSSatisfiesExpression` node as another expression. */
106
+ function hasConstAssertionWithSatisfies(init) {
107
+ const { expression } = init;
108
+ if (expression.type !== AST_NODE_TYPES.TSAsExpression) {
109
+ return false;
110
+ }
111
+ return hasConstAssertionWithoutSatisfies(expression);
112
+ }
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+ export type Options = [];
3
+ export type MessageIds = "readOnlyMap" | "readOnlySet" | "readOnlyArray" | "readOnlyObject";
4
+ export declare const requireCapitalReadOnly: ESLintUtils.RuleModule<MessageIds, [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, ESLintUtils.RuleListener>;
5
+ //# sourceMappingURL=require-capital-read-only.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-capital-read-only.d.ts","sourceRoot":"","sources":["../../src/rules/require-capital-read-only.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAMvE,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAClB,aAAa,GACb,aAAa,GACb,eAAe,GACf,gBAAgB,CAAC;AAErB,eAAO,MAAM,sBAAsB,wHA4DjC,CAAC"}
@@ -0,0 +1,111 @@
1
+ import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
2
+ import ts from "typescript";
3
+ import { isFirstLetterCapitalized } from "../completeCommon.js";
4
+ import { getTypeName } from "../typeUtils.js";
5
+ import { createRule } from "../utils.js";
6
+ export const requireCapitalReadOnly = createRule({
7
+ name: "require-capital-read-only",
8
+ meta: {
9
+ type: "problem",
10
+ docs: {
11
+ description: "Requires maps/sets/arrays with a capital letter to be read-only",
12
+ recommended: true,
13
+ requiresTypeChecking: true,
14
+ },
15
+ schema: [],
16
+ messages: {
17
+ readOnlyMap: 'Maps with a capital letter must be explicitly annotated as "ReadOnlyMap".',
18
+ readOnlySet: 'Sets with a capital letter must be explicitly annotated as "ReadOnlySet".',
19
+ readOnlyArray: 'Arrays with a capital letter must be explicitly annotated as "readonly" or "ReadonlyArray".',
20
+ readOnlyObject: 'Objects with a capital letter must be read-only. Try using a type annotation with the "Readonly" helper type.',
21
+ },
22
+ },
23
+ defaultOptions: [],
24
+ create(context) {
25
+ const parserServices = ESLintUtils.getParserServices(context);
26
+ const checker = parserServices.program.getTypeChecker();
27
+ return {
28
+ VariableDeclaration(node) {
29
+ if (node.kind !== "const") {
30
+ return;
31
+ }
32
+ for (const declaration of node.declarations) {
33
+ const { id } = declaration;
34
+ if (id.type !== AST_NODE_TYPES.Identifier) {
35
+ continue;
36
+ }
37
+ const variableName = id.name;
38
+ if (!isFirstLetterCapitalized(variableName)) {
39
+ continue;
40
+ }
41
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(declaration);
42
+ const type = checker.getTypeAtLocation(tsNode);
43
+ const messageId = getErrorMessageId(type);
44
+ if (messageId === undefined) {
45
+ return;
46
+ }
47
+ context.report({
48
+ loc: node.loc,
49
+ messageId,
50
+ });
51
+ }
52
+ },
53
+ };
54
+ },
55
+ });
56
+ function getErrorMessageId(type) {
57
+ const typeName = getTypeName(type);
58
+ if (typeName === undefined) {
59
+ return undefined;
60
+ }
61
+ if (typeName === "__object" && !isReadOnlyObject(type)) {
62
+ return "readOnlyObject";
63
+ }
64
+ // This would be "ReadonlyMap" if it was the read-only version.
65
+ if (typeName === "Map") {
66
+ return "readOnlyMap";
67
+ }
68
+ // This would be "ReadonlySet" if it was the read-only version.
69
+ if (typeName === "Set") {
70
+ return "readOnlySet";
71
+ }
72
+ // This would be "ReadonlyArray" if it was the read-only version.
73
+ if (typeName === "Array") {
74
+ return "readOnlyArray";
75
+ }
76
+ return undefined;
77
+ }
78
+ function isReadOnlyObject(type) {
79
+ const symbols = type.getProperties();
80
+ return symbols.every((symbol) => isReadonlySymbol(symbol));
81
+ }
82
+ /* eslint-disable */
83
+ /**
84
+ * This is copied from TypeScript internally; it is not exported or exposed:
85
+ * https://github.com/microsoft/TypeScript/issues/31296
86
+ */
87
+ function isReadonlySymbol(symbol) {
88
+ // The following symbols are considered read-only:
89
+ // - Properties with a 'readonly' modifier
90
+ // - Variables declared with 'const'
91
+ // - Get accessors without matching set accessors
92
+ // - Enum members
93
+ // - Object.defineProperty assignments with writable false or no setter
94
+ // - Unions and intersections of the above (unions and intersections eagerly set isReadonly on
95
+ // creation)
96
+ return !!(
97
+ // prettier-ignore
98
+ // @ts-expect-error Using internal functions.
99
+ (ts.getCheckFlags(symbol) & ts.CheckFlags.Readonly ||
100
+ (symbol.flags & ts.SymbolFlags.Property &&
101
+ // @ts-expect-error Using internal functions.
102
+ ts.getDeclarationModifierFlagsFromSymbol(symbol) &
103
+ ts.ModifierFlags.Readonly) ||
104
+ (symbol.flags & ts.SymbolFlags.Variable &&
105
+ // @ts-expect-error Using internal functions.
106
+ ts.getDeclarationNodeFlagsFromSymbol(symbol) & ts.NodeFlags.Const) ||
107
+ (symbol.flags & ts.SymbolFlags.Accessor &&
108
+ !(symbol.flags & ts.SymbolFlags.SetAccessor)) ||
109
+ symbol.flags & ts.SymbolFlags.EnumMember));
110
+ }
111
+ /* eslint-enable */
@@ -0,0 +1,2 @@
1
+ export declare const requireUnannotatedConstAssertions: import("@typescript-eslint/utils/ts-eslint").RuleModule<"annotatedConstAssertion", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
2
+ //# sourceMappingURL=require-unannotated-const-assertions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-unannotated-const-assertions.d.ts","sourceRoot":"","sources":["../../src/rules/require-unannotated-const-assertions.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iCAAiC,yMA4B5C,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { createRule } from "../utils.js";
2
+ export const requireUnannotatedConstAssertions = createRule({
3
+ name: "require-unannotated-const-assertions",
4
+ meta: {
5
+ type: "problem",
6
+ docs: {
7
+ description: "Disallows explicit type annotations for variables that have a const assertion",
8
+ recommended: true,
9
+ requiresTypeChecking: false,
10
+ },
11
+ schema: [],
12
+ messages: {
13
+ annotatedConstAssertion: "Don't use `as const` with a type annotated variable, since it widens the type.",
14
+ },
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ return {
19
+ 'VariableDeclarator[id.typeAnnotation] > :matches(TSTypeAssertion, TSAsExpression) > TSTypeReference.typeAnnotation > Identifier[name="const"]': (node) => {
20
+ context.report({
21
+ node,
22
+ messageId: "annotatedConstAssertion",
23
+ });
24
+ },
25
+ };
26
+ },
27
+ });
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+ export type Options = [];
3
+ export type MessageIds = "noArgument";
4
+ export declare const requireVariadicFunctionArgument: ESLintUtils.RuleModule<"noArgument", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, ESLintUtils.RuleListener>;
5
+ //# sourceMappingURL=require-variadic-function-argument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-variadic-function-argument.d.ts","sourceRoot":"","sources":["../../src/rules/require-variadic-function-argument.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIvE,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC;AAItC,eAAO,MAAM,+BAA+B,0HA8D1C,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
2
+ import ts from "typescript";
3
+ import { createRule } from "../utils.js";
4
+ const JSDOC_EXCEPTION_TAG = "allowEmptyVariadic";
5
+ export const requireVariadicFunctionArgument = createRule({
6
+ name: "require-variadic-function-argument",
7
+ meta: {
8
+ type: "problem",
9
+ docs: {
10
+ description: "Requires that variadic functions must be supplied with at least one argument",
11
+ recommended: true,
12
+ requiresTypeChecking: true,
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ noArgument: "You must pass at least one argument to a variadic function.",
17
+ },
18
+ },
19
+ defaultOptions: [],
20
+ create(context) {
21
+ const parserServices = ESLintUtils.getParserServices(context);
22
+ const checker = parserServices.program.getTypeChecker();
23
+ return {
24
+ CallExpression(node) {
25
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
26
+ const signature = checker.getResolvedSignature(tsNode);
27
+ if (signature === undefined) {
28
+ return;
29
+ }
30
+ const declaration = signature.getDeclaration();
31
+ // The `getDeclaration` method actually returns `ts.SignatureDeclaration | undefined`, not
32
+ // `ts.SignatureDeclaration`.
33
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
34
+ if (declaration === undefined) {
35
+ return;
36
+ }
37
+ if (isHardCodedException(node) ||
38
+ hasJSDocExceptionTag(checker, declaration)) {
39
+ return;
40
+ }
41
+ for (let i = 0; i < declaration.parameters.length; i++) {
42
+ const parameter = declaration.parameters[i];
43
+ if (parameter === undefined) {
44
+ continue;
45
+ }
46
+ if (ts.isRestParameter(parameter) &&
47
+ node.arguments[i] === undefined) {
48
+ context.report({
49
+ loc: node.loc,
50
+ messageId: "noArgument",
51
+ });
52
+ }
53
+ }
54
+ },
55
+ };
56
+ },
57
+ });
58
+ function isHardCodedException(node) {
59
+ const { callee } = node;
60
+ return isConsoleOrWindowFunction(callee) || isTimeoutFunction(callee);
61
+ }
62
+ function isConsoleOrWindowFunction(callee) {
63
+ if (callee.type !== AST_NODE_TYPES.MemberExpression) {
64
+ return false;
65
+ }
66
+ const { object } = callee;
67
+ if (object.type !== AST_NODE_TYPES.Identifier) {
68
+ return false;
69
+ }
70
+ return object.name === "console" || object.name === "window";
71
+ }
72
+ function isTimeoutFunction(callee) {
73
+ if (callee.type !== AST_NODE_TYPES.Identifier) {
74
+ return false;
75
+ }
76
+ return callee.name === "setTimeout" || callee.name === "setInterval";
77
+ }
78
+ function hasJSDocExceptionTag(checker, declaration) {
79
+ const type = checker.getTypeAtLocation(declaration);
80
+ const symbol = type.getSymbol();
81
+ if (symbol === undefined) {
82
+ return false;
83
+ }
84
+ const jsDocTagInfoArray = symbol.getJsDocTags(checker);
85
+ return jsDocTagInfoArray.some((tagInfo) => tagInfo.name === JSDOC_EXCEPTION_TAG);
86
+ }
@@ -0,0 +1,3 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+ export declare const strictArrayMethods: ESLintUtils.RuleModule<"conditionError", [], import("../interfaces/MyPluginDocs.js").MyPluginDocs, ESLintUtils.RuleListener>;
3
+ //# sourceMappingURL=strict-array-methods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strict-array-methods.d.ts","sourceRoot":"","sources":["../../src/rules/strict-array-methods.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAcvE,eAAO,MAAM,kBAAkB,8HAyF7B,CAAC"}