eslint-plugin-unicorn-ts 0.0.1-security → 50.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of eslint-plugin-unicorn-ts might be problematic. Click here for more details.

Files changed (214) hide show
  1. package/configs/all.js +6 -0
  2. package/configs/flat-config-base.js +10 -0
  3. package/configs/legacy-config-base.js +10 -0
  4. package/configs/recommended.js +117 -0
  5. package/index.js +91 -0
  6. package/license +9 -0
  7. package/package.json +186 -4
  8. package/readme.md +356 -0
  9. package/rules/ast/call-or-new-expression.js +127 -0
  10. package/rules/ast/function-types.js +5 -0
  11. package/rules/ast/index.js +39 -0
  12. package/rules/ast/is-arrow-function-body.js +7 -0
  13. package/rules/ast/is-empty-node.js +20 -0
  14. package/rules/ast/is-expression-statement.js +11 -0
  15. package/rules/ast/is-function.js +8 -0
  16. package/rules/ast/is-member-expression.js +101 -0
  17. package/rules/ast/is-method-call.js +65 -0
  18. package/rules/ast/is-reference-identifier.js +156 -0
  19. package/rules/ast/is-static-require.js +14 -0
  20. package/rules/ast/is-undefined.js +7 -0
  21. package/rules/ast/literal.js +29 -0
  22. package/rules/better-regex.js +144 -0
  23. package/rules/catch-error-name.js +136 -0
  24. package/rules/consistent-destructuring.js +168 -0
  25. package/rules/consistent-function-scoping.js +223 -0
  26. package/rules/custom-error-definition.js +215 -0
  27. package/rules/empty-brace-spaces.js +72 -0
  28. package/rules/error-message.js +104 -0
  29. package/rules/escape-case.js +63 -0
  30. package/rules/expiring-todo-comments.js +580 -0
  31. package/rules/explicit-length-check.js +229 -0
  32. package/rules/filename-case.js +258 -0
  33. package/rules/fix/add-parenthesizes-to-return-or-throw-expression.js +21 -0
  34. package/rules/fix/append-argument.js +20 -0
  35. package/rules/fix/extend-fix-range.js +15 -0
  36. package/rules/fix/fix-space-around-keywords.js +35 -0
  37. package/rules/fix/index.js +23 -0
  38. package/rules/fix/remove-argument.js +32 -0
  39. package/rules/fix/remove-member-expression-property.js +11 -0
  40. package/rules/fix/remove-method-call.js +20 -0
  41. package/rules/fix/remove-parentheses.js +11 -0
  42. package/rules/fix/remove-spaces-after.js +14 -0
  43. package/rules/fix/rename-variable.js +9 -0
  44. package/rules/fix/replace-argument.js +8 -0
  45. package/rules/fix/replace-node-or-token-and-spaces-before.js +21 -0
  46. package/rules/fix/replace-reference-identifier.js +35 -0
  47. package/rules/fix/replace-string-literal.js +11 -0
  48. package/rules/fix/replace-string-raw.js +14 -0
  49. package/rules/fix/replace-template-element.js +11 -0
  50. package/rules/fix/switch-call-expression-to-new-expression.js +18 -0
  51. package/rules/fix/switch-new-expression-to-call-expression.js +34 -0
  52. package/rules/import-style.js +364 -0
  53. package/rules/new-for-builtins.js +85 -0
  54. package/rules/no-abusive-eslint-disable.js +48 -0
  55. package/rules/no-array-callback-reference.js +256 -0
  56. package/rules/no-array-for-each.js +473 -0
  57. package/rules/no-array-method-this-argument.js +188 -0
  58. package/rules/no-array-push-push.js +144 -0
  59. package/rules/no-array-reduce.js +126 -0
  60. package/rules/no-await-expression-member.js +90 -0
  61. package/rules/no-console-spaces.js +86 -0
  62. package/rules/no-document-cookie.js +25 -0
  63. package/rules/no-empty-file.js +57 -0
  64. package/rules/no-for-loop.js +427 -0
  65. package/rules/no-hex-escape.js +46 -0
  66. package/rules/no-instanceof-array.js +65 -0
  67. package/rules/no-invalid-remove-event-listener.js +60 -0
  68. package/rules/no-keyword-prefix.js +199 -0
  69. package/rules/no-lonely-if.js +151 -0
  70. package/rules/no-negated-condition.js +144 -0
  71. package/rules/no-nested-ternary.js +58 -0
  72. package/rules/no-new-array.js +104 -0
  73. package/rules/no-new-buffer.js +98 -0
  74. package/rules/no-null.js +153 -0
  75. package/rules/no-object-as-default-parameter.js +50 -0
  76. package/rules/no-process-exit.js +104 -0
  77. package/rules/no-static-only-class.js +224 -0
  78. package/rules/no-thenable.js +198 -0
  79. package/rules/no-this-assignment.js +38 -0
  80. package/rules/no-typeof-undefined.js +143 -0
  81. package/rules/no-unnecessary-await.js +107 -0
  82. package/rules/no-unnecessary-polyfills.js +176 -0
  83. package/rules/no-unreadable-array-destructuring.js +83 -0
  84. package/rules/no-unreadable-iife.js +45 -0
  85. package/rules/no-unused-properties.js +238 -0
  86. package/rules/no-useless-fallback-in-spread.js +68 -0
  87. package/rules/no-useless-length-check.js +152 -0
  88. package/rules/no-useless-promise-resolve-reject.js +212 -0
  89. package/rules/no-useless-spread.js +381 -0
  90. package/rules/no-useless-switch-case.js +71 -0
  91. package/rules/no-useless-undefined.js +301 -0
  92. package/rules/no-zero-fractions.js +79 -0
  93. package/rules/number-literal-case.js +52 -0
  94. package/rules/numeric-separators-style.js +181 -0
  95. package/rules/prefer-add-event-listener.js +188 -0
  96. package/rules/prefer-array-find.js +423 -0
  97. package/rules/prefer-array-flat-map.js +82 -0
  98. package/rules/prefer-array-flat.js +279 -0
  99. package/rules/prefer-array-index-of.js +32 -0
  100. package/rules/prefer-array-some.js +157 -0
  101. package/rules/prefer-at.js +374 -0
  102. package/rules/prefer-blob-reading-methods.js +45 -0
  103. package/rules/prefer-code-point.js +67 -0
  104. package/rules/prefer-date-now.js +135 -0
  105. package/rules/prefer-default-parameters.js +219 -0
  106. package/rules/prefer-dom-node-append.js +48 -0
  107. package/rules/prefer-dom-node-dataset.js +120 -0
  108. package/rules/prefer-dom-node-remove.js +122 -0
  109. package/rules/prefer-dom-node-text-content.js +75 -0
  110. package/rules/prefer-event-target.js +117 -0
  111. package/rules/prefer-export-from.js +413 -0
  112. package/rules/prefer-includes.js +98 -0
  113. package/rules/prefer-json-parse-buffer.js +159 -0
  114. package/rules/prefer-keyboard-event-key.js +186 -0
  115. package/rules/prefer-logical-operator-over-ternary.js +159 -0
  116. package/rules/prefer-math-trunc.js +109 -0
  117. package/rules/prefer-modern-dom-apis.js +141 -0
  118. package/rules/prefer-modern-math-apis.js +212 -0
  119. package/rules/prefer-module.js +349 -0
  120. package/rules/prefer-native-coercion-functions.js +185 -0
  121. package/rules/prefer-negative-index.js +213 -0
  122. package/rules/prefer-node-protocol.js +61 -0
  123. package/rules/prefer-number-properties.js +126 -0
  124. package/rules/prefer-object-from-entries.js +252 -0
  125. package/rules/prefer-optional-catch-binding.js +75 -0
  126. package/rules/prefer-prototype-methods.js +88 -0
  127. package/rules/prefer-query-selector.js +135 -0
  128. package/rules/prefer-reflect-apply.js +97 -0
  129. package/rules/prefer-regexp-test.js +156 -0
  130. package/rules/prefer-set-has.js +186 -0
  131. package/rules/prefer-set-size.js +103 -0
  132. package/rules/prefer-spread.js +529 -0
  133. package/rules/prefer-string-replace-all.js +145 -0
  134. package/rules/prefer-string-slice.js +182 -0
  135. package/rules/prefer-string-starts-ends-with.js +199 -0
  136. package/rules/prefer-string-trim-start-end.js +44 -0
  137. package/rules/prefer-switch.js +344 -0
  138. package/rules/prefer-ternary.js +282 -0
  139. package/rules/prefer-top-level-await.js +152 -0
  140. package/rules/prefer-type-error.js +151 -0
  141. package/rules/prevent-abbreviations.js +645 -0
  142. package/rules/relative-url-style.js +168 -0
  143. package/rules/require-array-join-separator.js +63 -0
  144. package/rules/require-number-to-fixed-digits-argument.js +54 -0
  145. package/rules/require-post-message-target-origin.js +71 -0
  146. package/rules/shared/abbreviations.js +262 -0
  147. package/rules/shared/dom-events.js +275 -0
  148. package/rules/shared/event-keys.js +52 -0
  149. package/rules/shared/negative-index.js +46 -0
  150. package/rules/shared/simple-array-search-rule.js +128 -0
  151. package/rules/shared/typed-array.js +16 -0
  152. package/rules/string-content.js +187 -0
  153. package/rules/switch-case-braces.js +109 -0
  154. package/rules/template-indent.js +219 -0
  155. package/rules/text-encoding-identifier-case.js +108 -0
  156. package/rules/throw-new-error.js +53 -0
  157. package/rules/utils/array-or-object-prototype-property.js +63 -0
  158. package/rules/utils/assert-token.js +32 -0
  159. package/rules/utils/avoid-capture.js +146 -0
  160. package/rules/utils/boolean.js +92 -0
  161. package/rules/utils/builtins.js +36 -0
  162. package/rules/utils/cartesian-product-samples.js +24 -0
  163. package/rules/utils/create-deprecated-rules.js +25 -0
  164. package/rules/utils/escape-string.js +26 -0
  165. package/rules/utils/escape-template-element-raw.js +6 -0
  166. package/rules/utils/get-ancestor.js +20 -0
  167. package/rules/utils/get-builtin-rule.js +7 -0
  168. package/rules/utils/get-call-expression-arguments-text.js +21 -0
  169. package/rules/utils/get-class-head-location.js +22 -0
  170. package/rules/utils/get-documentation-url.js +10 -0
  171. package/rules/utils/get-indent-string.js +11 -0
  172. package/rules/utils/get-previous-node.js +24 -0
  173. package/rules/utils/get-references.js +9 -0
  174. package/rules/utils/get-scopes.js +14 -0
  175. package/rules/utils/get-switch-case-head-location.js +21 -0
  176. package/rules/utils/get-variable-identifiers.js +7 -0
  177. package/rules/utils/global-reference-tracker.js +72 -0
  178. package/rules/utils/has-optional-chain-element.js +21 -0
  179. package/rules/utils/has-same-range.js +7 -0
  180. package/rules/utils/index.js +53 -0
  181. package/rules/utils/is-function-self-used-inside.js +43 -0
  182. package/rules/utils/is-left-hand-side.js +22 -0
  183. package/rules/utils/is-logical-expression.js +16 -0
  184. package/rules/utils/is-method-named.js +9 -0
  185. package/rules/utils/is-new-expression-with-parentheses.js +26 -0
  186. package/rules/utils/is-node-matches.js +53 -0
  187. package/rules/utils/is-node-value-not-dom-node.js +21 -0
  188. package/rules/utils/is-node-value-not-function.js +42 -0
  189. package/rules/utils/is-number.js +224 -0
  190. package/rules/utils/is-object-method.js +11 -0
  191. package/rules/utils/is-on-same-line.js +7 -0
  192. package/rules/utils/is-same-identifier.js +8 -0
  193. package/rules/utils/is-same-reference.js +173 -0
  194. package/rules/utils/is-shadowed.js +33 -0
  195. package/rules/utils/is-shorthand-export-local.js +9 -0
  196. package/rules/utils/is-shorthand-import-local.js +9 -0
  197. package/rules/utils/is-shorthand-property-assignment-pattern-left.js +10 -0
  198. package/rules/utils/is-shorthand-property-value.js +8 -0
  199. package/rules/utils/is-value-not-usable.js +5 -0
  200. package/rules/utils/lodash.js +1589 -0
  201. package/rules/utils/needs-semicolon.js +114 -0
  202. package/rules/utils/numeric.js +53 -0
  203. package/rules/utils/parentheses.js +73 -0
  204. package/rules/utils/resolve-variable-name.js +20 -0
  205. package/rules/utils/rule.js +190 -0
  206. package/rules/utils/should-add-parentheses-to-conditional-expression-child.js +17 -0
  207. package/rules/utils/should-add-parentheses-to-expression-statement-expression.js +26 -0
  208. package/rules/utils/should-add-parentheses-to-logical-expression-child.js +47 -0
  209. package/rules/utils/should-add-parentheses-to-member-expression-object.js +47 -0
  210. package/rules/utils/should-add-parentheses-to-new-expression-callee.js +32 -0
  211. package/rules/utils/should-add-parentheses-to-spread-element-argument.js +22 -0
  212. package/rules/utils/singular.js +18 -0
  213. package/rules/utils/to-location.js +21 -0
  214. package/README.md +0 -5
@@ -0,0 +1,212 @@
1
+ 'use strict';
2
+ const {
3
+ getParenthesizedText,
4
+ getParenthesizedRange,
5
+ isSameReference,
6
+ } = require('./utils/index.js');
7
+ const {isLiteral, isMethodCall} = require('./ast/index.js');
8
+ const {replaceNodeOrTokenAndSpacesBefore, removeParentheses} = require('./fix/index.js');
9
+
10
+ const MESSAGE_ID = 'prefer-modern-math-apis';
11
+ const messages = {
12
+ [MESSAGE_ID]: 'Prefer `{{replacement}}` over `{{description}}`.',
13
+ };
14
+
15
+ const isMathProperty = (node, property) =>
16
+ node.type === 'MemberExpression'
17
+ && !node.optional
18
+ && !node.computed
19
+ && node.object.type === 'Identifier'
20
+ && node.object.name === 'Math'
21
+ && node.property.type === 'Identifier'
22
+ && node.property.name === property;
23
+
24
+ const isMathMethodCall = (node, method) =>
25
+ node.type === 'CallExpression'
26
+ && !node.optional
27
+ && isMathProperty(node.callee, method)
28
+ && node.arguments.length === 1
29
+ && node.arguments[0].type !== 'SpreadElement';
30
+
31
+ // `Math.log(x) * Math.LOG10E` -> `Math.log10(x)`
32
+ // `Math.LOG10E * Math.log(x)` -> `Math.log10(x)`
33
+ // `Math.log(x) * Math.LOG2E` -> `Math.log2(x)`
34
+ // `Math.LOG2E * Math.log(x)` -> `Math.log2(x)`
35
+ function createLogCallTimesConstantCheck({constantName, replacementMethod}) {
36
+ const replacement = `Math.${replacementMethod}(…)`;
37
+
38
+ return function (node, context) {
39
+ if (!(node.type === 'BinaryExpression' && node.operator === '*')) {
40
+ return;
41
+ }
42
+
43
+ let mathLogCall;
44
+ let description;
45
+ if (isMathMethodCall(node.left, 'log') && isMathProperty(node.right, constantName)) {
46
+ mathLogCall = node.left;
47
+ description = `Math.log(…) * Math.${constantName}`;
48
+ } else if (isMathMethodCall(node.right, 'log') && isMathProperty(node.left, constantName)) {
49
+ mathLogCall = node.right;
50
+ description = `Math.${constantName} * Math.log(…)`;
51
+ }
52
+
53
+ if (!mathLogCall) {
54
+ return;
55
+ }
56
+
57
+ const [valueNode] = mathLogCall.arguments;
58
+
59
+ return {
60
+ node,
61
+ messageId: MESSAGE_ID,
62
+ data: {
63
+ replacement,
64
+ description,
65
+ },
66
+ fix: fixer => fixer.replaceText(node, `Math.${replacementMethod}(${getParenthesizedText(valueNode, context.sourceCode)})`),
67
+ };
68
+ };
69
+ }
70
+
71
+ // `Math.log(x) / Math.LN10` -> `Math.log10(x)`
72
+ // `Math.log(x) / Math.LN2` -> `Math.log2(x)`
73
+ function createLogCallDivideConstantCheck({constantName, replacementMethod}) {
74
+ const message = {
75
+ messageId: MESSAGE_ID,
76
+ data: {
77
+ replacement: `Math.${replacementMethod}(…)`,
78
+ description: `Math.log(…) / Math.${constantName}`,
79
+ },
80
+ };
81
+
82
+ return function (node, context) {
83
+ if (
84
+ !(
85
+ node.type === 'BinaryExpression'
86
+ && node.operator === '/'
87
+ && isMathMethodCall(node.left, 'log')
88
+ && isMathProperty(node.right, constantName)
89
+ )
90
+ ) {
91
+ return;
92
+ }
93
+
94
+ const [valueNode] = node.left.arguments;
95
+
96
+ return {
97
+ ...message,
98
+ node,
99
+ fix: fixer => fixer.replaceText(node, `Math.${replacementMethod}(${getParenthesizedText(valueNode, context.sourceCode)})`),
100
+ };
101
+ };
102
+ }
103
+
104
+ const checkFunctions = [
105
+ createLogCallTimesConstantCheck({constantName: 'LOG10E', replacementMethod: 'log10'}),
106
+ createLogCallTimesConstantCheck({constantName: 'LOG2E', replacementMethod: 'log2'}),
107
+ createLogCallDivideConstantCheck({constantName: 'LN10', replacementMethod: 'log10'}),
108
+ createLogCallDivideConstantCheck({constantName: 'LN2', replacementMethod: 'log2'}),
109
+ ];
110
+
111
+ const isPlusExpression = node => node.type === 'BinaryExpression' && node.operator === '+';
112
+
113
+ const isPow2Expression = node =>
114
+ node.type === 'BinaryExpression'
115
+ && (
116
+ // `x * x`
117
+ (node.operator === '*' && isSameReference(node.left, node.right))
118
+ // `x ** 2`
119
+ || (node.operator === '**' && isLiteral(node.right, 2))
120
+ );
121
+
122
+ const flatPlusExpression = node =>
123
+ isPlusExpression(node)
124
+ ? [node.left, node.right].flatMap(child => flatPlusExpression(child))
125
+ : [node];
126
+
127
+ /** @param {import('eslint').Rule.RuleContext} context */
128
+ const create = context => {
129
+ const nodes = [];
130
+
131
+ return {
132
+ CallExpression(callExpression) {
133
+ if (!isMethodCall(callExpression, {
134
+ object: 'Math',
135
+ method: 'sqrt',
136
+ argumentsLength: 1,
137
+ optionalCall: false,
138
+ optionalMember: false,
139
+ })) {
140
+ return;
141
+ }
142
+
143
+ const expressions = flatPlusExpression(callExpression.arguments[0]);
144
+ if (expressions.some(expression => !isPow2Expression(expression))) {
145
+ return;
146
+ }
147
+
148
+ const replacementMethod = expressions.length === 1 ? 'abs' : 'hypot';
149
+ const plusExpressions = new Set(expressions.length === 1 ? [] : expressions.map(expression => expression.parent));
150
+
151
+ return {
152
+ node: callExpression.callee.property,
153
+ messageId: MESSAGE_ID,
154
+ data: {
155
+ replacement: `Math.${replacementMethod}(…)`,
156
+ description: 'Math.sqrt(…)',
157
+ },
158
+ * fix(fixer) {
159
+ const {sourceCode} = context;
160
+
161
+ // `Math.sqrt` -> `Math.{hypot,abs}`
162
+ yield fixer.replaceText(callExpression.callee.property, replacementMethod);
163
+
164
+ // `a ** 2 + b ** 2` -> `a, b`
165
+ for (const expression of plusExpressions) {
166
+ const plusToken = sourceCode.getTokenAfter(expression.left, token => token.type === 'Punctuator' && token.value === '+');
167
+
168
+ yield * replaceNodeOrTokenAndSpacesBefore(plusToken, ',', fixer, sourceCode);
169
+ yield * removeParentheses(expression, fixer, sourceCode);
170
+ }
171
+
172
+ // `x ** 2` => `x`
173
+ // `x * a` => `x`
174
+ for (const expression of expressions) {
175
+ yield fixer.removeRange([
176
+ getParenthesizedRange(expression.left, sourceCode)[1],
177
+ expression.range[1],
178
+ ]);
179
+ }
180
+ },
181
+ };
182
+ },
183
+
184
+ BinaryExpression(node) {
185
+ nodes.push(node);
186
+ },
187
+ * 'Program:exit'() {
188
+ for (const node of nodes) {
189
+ for (const getProblem of checkFunctions) {
190
+ const problem = getProblem(node, context);
191
+
192
+ if (problem) {
193
+ yield problem;
194
+ }
195
+ }
196
+ }
197
+ },
198
+ };
199
+ };
200
+
201
+ /** @type {import('eslint').Rule.RuleModule} */
202
+ module.exports = {
203
+ create,
204
+ meta: {
205
+ type: 'suggestion',
206
+ docs: {
207
+ description: 'Prefer modern `Math` APIs over legacy patterns.',
208
+ },
209
+ fixable: 'code',
210
+ messages,
211
+ },
212
+ };
@@ -0,0 +1,349 @@
1
+ 'use strict';
2
+ const {isOpeningParenToken} = require('@eslint-community/eslint-utils');
3
+ const isShadowed = require('./utils/is-shadowed.js');
4
+ const assertToken = require('./utils/assert-token.js');
5
+ const {isStaticRequire, isReferenceIdentifier, isFunction} = require('./ast/index.js');
6
+ const {
7
+ removeParentheses,
8
+ replaceReferenceIdentifier,
9
+ removeSpacesAfter,
10
+ } = require('./fix/index.js');
11
+
12
+ const ERROR_USE_STRICT_DIRECTIVE = 'error/use-strict-directive';
13
+ const ERROR_GLOBAL_RETURN = 'error/global-return';
14
+ const ERROR_IDENTIFIER = 'error/identifier';
15
+ const SUGGESTION_USE_STRICT_DIRECTIVE = 'suggestion/use-strict-directive';
16
+ const SUGGESTION_DIRNAME = 'suggestion/dirname';
17
+ const SUGGESTION_FILENAME = 'suggestion/filename';
18
+ const SUGGESTION_IMPORT = 'suggestion/import';
19
+ const SUGGESTION_EXPORT = 'suggestion/export';
20
+ const messages = {
21
+ [ERROR_USE_STRICT_DIRECTIVE]: 'Do not use "use strict" directive.',
22
+ [ERROR_GLOBAL_RETURN]: '"return" should be used inside a function.',
23
+ [ERROR_IDENTIFIER]: 'Do not use "{{name}}".',
24
+ [SUGGESTION_USE_STRICT_DIRECTIVE]: 'Remove "use strict" directive.',
25
+ [SUGGESTION_DIRNAME]: 'Replace "__dirname" with `"…(import.meta.url)"`.',
26
+ [SUGGESTION_FILENAME]: 'Replace "__filename" with `"…(import.meta.url)"`.',
27
+ [SUGGESTION_IMPORT]: 'Switch to `import`.',
28
+ [SUGGESTION_EXPORT]: 'Switch to `export`.',
29
+ };
30
+
31
+ function fixRequireCall(node, sourceCode) {
32
+ if (!isStaticRequire(node.parent) || node.parent.callee !== node) {
33
+ return;
34
+ }
35
+
36
+ const requireCall = node.parent;
37
+ const {
38
+ parent,
39
+ callee,
40
+ arguments: [source],
41
+ } = requireCall;
42
+
43
+ // `require("foo")`
44
+ if (parent.type === 'ExpressionStatement' && parent.parent.type === 'Program') {
45
+ return function * (fixer) {
46
+ yield fixer.replaceText(callee, 'import');
47
+ const openingParenthesisToken = sourceCode.getTokenAfter(
48
+ callee,
49
+ isOpeningParenToken,
50
+ );
51
+ yield fixer.replaceText(openingParenthesisToken, ' ');
52
+ const closingParenthesisToken = sourceCode.getLastToken(requireCall);
53
+ yield fixer.remove(closingParenthesisToken);
54
+
55
+ for (const node of [callee, requireCall, source]) {
56
+ yield * removeParentheses(node, fixer, sourceCode);
57
+ }
58
+ };
59
+ }
60
+
61
+ // `const foo = require("foo")`
62
+ // `const {foo} = require("foo")`
63
+ if (
64
+ parent.type === 'VariableDeclarator'
65
+ && parent.init === requireCall
66
+ && (
67
+ parent.id.type === 'Identifier'
68
+ || (
69
+ parent.id.type === 'ObjectPattern'
70
+ && parent.id.properties.every(
71
+ ({type, key, value, computed}) =>
72
+ type === 'Property'
73
+ && !computed
74
+ && value.type === 'Identifier'
75
+ && key.type === 'Identifier',
76
+ )
77
+ )
78
+ )
79
+ && parent.parent.type === 'VariableDeclaration'
80
+ && parent.parent.kind === 'const'
81
+ && parent.parent.declarations.length === 1
82
+ && parent.parent.declarations[0] === parent
83
+ && parent.parent.parent.type === 'Program'
84
+ ) {
85
+ const declarator = parent;
86
+ const declaration = declarator.parent;
87
+ const {id} = declarator;
88
+
89
+ return function * (fixer) {
90
+ const constToken = sourceCode.getFirstToken(declaration);
91
+ assertToken(constToken, {
92
+ expected: {type: 'Keyword', value: 'const'},
93
+ ruleId: 'prefer-module',
94
+ });
95
+ yield fixer.replaceText(constToken, 'import');
96
+
97
+ const equalToken = sourceCode.getTokenAfter(id);
98
+ assertToken(equalToken, {
99
+ expected: {type: 'Punctuator', value: '='},
100
+ ruleId: 'prefer-module',
101
+ });
102
+ yield removeSpacesAfter(id, sourceCode, fixer);
103
+ yield removeSpacesAfter(equalToken, sourceCode, fixer);
104
+ yield fixer.replaceText(equalToken, ' from ');
105
+
106
+ yield fixer.remove(callee);
107
+ const openingParenthesisToken = sourceCode.getTokenAfter(
108
+ callee,
109
+ isOpeningParenToken,
110
+ );
111
+ yield fixer.remove(openingParenthesisToken);
112
+ const closingParenthesisToken = sourceCode.getLastToken(requireCall);
113
+ yield fixer.remove(closingParenthesisToken);
114
+
115
+ for (const node of [callee, requireCall, source]) {
116
+ yield * removeParentheses(node, fixer, sourceCode);
117
+ }
118
+
119
+ if (id.type === 'Identifier') {
120
+ return;
121
+ }
122
+
123
+ const {properties} = id;
124
+
125
+ for (const property of properties) {
126
+ const {key, shorthand} = property;
127
+ if (!shorthand) {
128
+ const commaToken = sourceCode.getTokenAfter(key);
129
+ assertToken(commaToken, {
130
+ expected: {type: 'Punctuator', value: ':'},
131
+ ruleId: 'prefer-module',
132
+ });
133
+ yield removeSpacesAfter(key, sourceCode, fixer);
134
+ yield removeSpacesAfter(commaToken, sourceCode, fixer);
135
+ yield fixer.replaceText(commaToken, ' as ');
136
+ }
137
+ }
138
+ };
139
+ }
140
+ }
141
+
142
+ const isTopLevelAssignment = node =>
143
+ node.parent.type === 'AssignmentExpression'
144
+ && node.parent.operator === '='
145
+ && node.parent.left === node
146
+ && node.parent.parent.type === 'ExpressionStatement'
147
+ && node.parent.parent.parent.type === 'Program';
148
+ const isNamedExport = node =>
149
+ node.parent.type === 'MemberExpression'
150
+ && !node.parent.optional
151
+ && !node.parent.computed
152
+ && node.parent.object === node
153
+ && node.parent.property.type === 'Identifier'
154
+ && isTopLevelAssignment(node.parent)
155
+ && node.parent.parent.right.type === 'Identifier';
156
+ const isModuleExports = node =>
157
+ node.parent.type === 'MemberExpression'
158
+ && !node.parent.optional
159
+ && !node.parent.computed
160
+ && node.parent.object === node
161
+ && node.parent.property.type === 'Identifier'
162
+ && node.parent.property.name === 'exports';
163
+ const isTopLevelReturnStatement = node => {
164
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
165
+ if (isFunction(ancestor)) {
166
+ return false;
167
+ }
168
+ }
169
+
170
+ return true;
171
+ };
172
+
173
+ function fixDefaultExport(node, sourceCode) {
174
+ return function * (fixer) {
175
+ yield fixer.replaceText(node, 'export default ');
176
+ yield removeSpacesAfter(node, sourceCode, fixer);
177
+
178
+ const equalToken = sourceCode.getTokenAfter(node, token => token.type === 'Punctuator' && token.value === '=');
179
+ yield fixer.remove(equalToken);
180
+ yield removeSpacesAfter(equalToken, sourceCode, fixer);
181
+
182
+ for (const currentNode of [node.parent, node]) {
183
+ yield * removeParentheses(currentNode, fixer, sourceCode);
184
+ }
185
+ };
186
+ }
187
+
188
+ function fixNamedExport(node, sourceCode) {
189
+ return function * (fixer) {
190
+ const assignmentExpression = node.parent.parent;
191
+ const exported = node.parent.property.name;
192
+ const local = assignmentExpression.right.name;
193
+ yield fixer.replaceText(assignmentExpression, `export {${local} as ${exported}}`);
194
+
195
+ yield * removeParentheses(assignmentExpression, fixer, sourceCode);
196
+ };
197
+ }
198
+
199
+ function fixExports(node, sourceCode) {
200
+ // `exports = bar`
201
+ if (isTopLevelAssignment(node)) {
202
+ return fixDefaultExport(node, sourceCode);
203
+ }
204
+
205
+ // `exports.foo = bar`
206
+ if (isNamedExport(node)) {
207
+ return fixNamedExport(node, sourceCode);
208
+ }
209
+ }
210
+
211
+ function fixModuleExports(node, sourceCode) {
212
+ if (isModuleExports(node)) {
213
+ return fixExports(node.parent, sourceCode);
214
+ }
215
+ }
216
+
217
+ function create(context) {
218
+ const filename = context.filename.toLowerCase();
219
+
220
+ if (filename.endsWith('.cjs')) {
221
+ return;
222
+ }
223
+
224
+ const {sourceCode} = context;
225
+
226
+ context.on('ExpressionStatement', node => {
227
+ if (node.directive !== 'use strict') {
228
+ return;
229
+ }
230
+
231
+ const problem = {node, messageId: ERROR_USE_STRICT_DIRECTIVE};
232
+ const fix = function * (fixer) {
233
+ yield fixer.remove(node);
234
+ yield removeSpacesAfter(node, sourceCode, fixer);
235
+ };
236
+
237
+ if (filename.endsWith('.mjs')) {
238
+ problem.fix = fix;
239
+ } else {
240
+ problem.suggest = [{messageId: SUGGESTION_USE_STRICT_DIRECTIVE, fix}];
241
+ }
242
+
243
+ return problem;
244
+ });
245
+
246
+ context.on('ReturnStatement', node => {
247
+ if (isTopLevelReturnStatement(node)) {
248
+ return {
249
+ node: sourceCode.getFirstToken(node),
250
+ messageId: ERROR_GLOBAL_RETURN,
251
+ };
252
+ }
253
+ });
254
+
255
+ context.on('Identifier', node => {
256
+ if (
257
+ !isReferenceIdentifier(node, [
258
+ 'exports',
259
+ 'require',
260
+ 'module',
261
+ '__filename',
262
+ '__dirname',
263
+ ])
264
+ || isShadowed(sourceCode.getScope(node), node)
265
+ ) {
266
+ return;
267
+ }
268
+
269
+ const {name} = node;
270
+
271
+ const problem = {
272
+ node,
273
+ messageId: ERROR_IDENTIFIER,
274
+ data: {name},
275
+ };
276
+
277
+ switch (name) {
278
+ case '__filename':
279
+ case '__dirname': {
280
+ const messageId = node.name === '__dirname' ? SUGGESTION_DIRNAME : SUGGESTION_FILENAME;
281
+ const replacement = node.name === '__dirname'
282
+ ? 'path.dirname(url.fileURLToPath(import.meta.url))'
283
+ : 'url.fileURLToPath(import.meta.url)';
284
+ problem.suggest = [{
285
+ messageId,
286
+ fix: fixer => replaceReferenceIdentifier(node, replacement, fixer),
287
+ }];
288
+ return problem;
289
+ }
290
+
291
+ case 'require': {
292
+ const fix = fixRequireCall(node, sourceCode);
293
+ if (fix) {
294
+ problem.suggest = [{
295
+ messageId: SUGGESTION_IMPORT,
296
+ fix,
297
+ }];
298
+ return problem;
299
+ }
300
+
301
+ break;
302
+ }
303
+
304
+ case 'exports': {
305
+ const fix = fixExports(node, sourceCode);
306
+ if (fix) {
307
+ problem.suggest = [{
308
+ messageId: SUGGESTION_EXPORT,
309
+ fix,
310
+ }];
311
+ return problem;
312
+ }
313
+
314
+ break;
315
+ }
316
+
317
+ case 'module': {
318
+ const fix = fixModuleExports(node, sourceCode);
319
+ if (fix) {
320
+ problem.suggest = [{
321
+ messageId: SUGGESTION_EXPORT,
322
+ fix,
323
+ }];
324
+ return problem;
325
+ }
326
+
327
+ break;
328
+ }
329
+
330
+ default:
331
+ }
332
+
333
+ return problem;
334
+ });
335
+ }
336
+
337
+ /** @type {import('eslint').Rule.RuleModule} */
338
+ module.exports = {
339
+ create,
340
+ meta: {
341
+ type: 'suggestion',
342
+ docs: {
343
+ description: 'Prefer JavaScript modules (ESM) over CommonJS.',
344
+ },
345
+ fixable: 'code',
346
+ hasSuggestions: true,
347
+ messages,
348
+ },
349
+ };