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,168 @@
1
+ 'use strict';
2
+ const avoidCapture = require('./utils/avoid-capture.js');
3
+ const isLeftHandSide = require('./utils/is-left-hand-side.js');
4
+ const {isCallOrNewExpression} = require('./ast/index.js');
5
+
6
+ const MESSAGE_ID = 'consistentDestructuring';
7
+ const MESSAGE_ID_SUGGEST = 'consistentDestructuringSuggest';
8
+
9
+ const isSimpleExpression = expression => {
10
+ while (expression) {
11
+ if (expression.computed) {
12
+ return false;
13
+ }
14
+
15
+ if (expression.type !== 'MemberExpression') {
16
+ break;
17
+ }
18
+
19
+ expression = expression.object;
20
+ }
21
+
22
+ return expression.type === 'Identifier'
23
+ || expression.type === 'ThisExpression';
24
+ };
25
+
26
+ const isChildInParentScope = (child, parent) => {
27
+ while (child) {
28
+ if (child === parent) {
29
+ return true;
30
+ }
31
+
32
+ child = child.upper;
33
+ }
34
+
35
+ return false;
36
+ };
37
+
38
+ /** @param {import('eslint').Rule.RuleContext} context */
39
+ const create = context => {
40
+ const {sourceCode} = context;
41
+ const declarations = new Map();
42
+
43
+ return {
44
+ VariableDeclarator(node) {
45
+ if (!(
46
+ node.id.type === 'ObjectPattern'
47
+ && node.init
48
+ && node.init.type !== 'Literal'
49
+ // Ignore any complex expressions (e.g. arrays, functions)
50
+ && isSimpleExpression(node.init)
51
+ )) {
52
+ return;
53
+ }
54
+
55
+ declarations.set(sourceCode.getText(node.init), {
56
+ scope: sourceCode.getScope(node),
57
+ variables: sourceCode.getDeclaredVariables(node),
58
+ objectPattern: node.id,
59
+ });
60
+ },
61
+ MemberExpression(node) {
62
+ if (
63
+ node.computed
64
+ || (
65
+ isCallOrNewExpression(node.parent)
66
+ && node.parent.callee === node
67
+ )
68
+ || isLeftHandSide(node)
69
+ ) {
70
+ return;
71
+ }
72
+
73
+ const declaration = declarations.get(sourceCode.getText(node.object));
74
+
75
+ if (!declaration) {
76
+ return;
77
+ }
78
+
79
+ const {scope, objectPattern} = declaration;
80
+ const memberScope = sourceCode.getScope(node);
81
+
82
+ // Property is destructured outside the current scope
83
+ if (!isChildInParentScope(memberScope, scope)) {
84
+ return;
85
+ }
86
+
87
+ const destructurings = objectPattern.properties.filter(property =>
88
+ property.type === 'Property'
89
+ && property.key.type === 'Identifier'
90
+ && property.value.type === 'Identifier',
91
+ );
92
+ const lastProperty = objectPattern.properties.at(-1);
93
+
94
+ const hasRest = lastProperty && lastProperty.type === 'RestElement';
95
+
96
+ const expression = sourceCode.getText(node);
97
+ const member = sourceCode.getText(node.property);
98
+
99
+ // Member might already be destructured
100
+ const destructuredMember = destructurings.find(property =>
101
+ property.key.name === member,
102
+ );
103
+
104
+ if (!destructuredMember) {
105
+ // Don't destructure additional members when rest is used
106
+ if (hasRest) {
107
+ return;
108
+ }
109
+
110
+ // Destructured member collides with an existing identifier
111
+ if (avoidCapture(member, [memberScope]) !== member) {
112
+ return;
113
+ }
114
+ }
115
+
116
+ // Don't try to fix nested member expressions
117
+ if (node.parent.type === 'MemberExpression') {
118
+ return {
119
+ node,
120
+ messageId: MESSAGE_ID,
121
+ };
122
+ }
123
+
124
+ const newMember = destructuredMember ? destructuredMember.value.name : member;
125
+
126
+ return {
127
+ node,
128
+ messageId: MESSAGE_ID,
129
+ suggest: [{
130
+ messageId: MESSAGE_ID_SUGGEST,
131
+ data: {
132
+ expression,
133
+ property: newMember,
134
+ },
135
+ * fix(fixer) {
136
+ const {properties} = objectPattern;
137
+ const lastProperty = properties.at(-1);
138
+
139
+ yield fixer.replaceText(node, newMember);
140
+
141
+ if (!destructuredMember) {
142
+ yield lastProperty
143
+ ? fixer.insertTextAfter(lastProperty, `, ${newMember}`)
144
+ : fixer.replaceText(objectPattern, `{${newMember}}`);
145
+ }
146
+ },
147
+ }],
148
+ };
149
+ },
150
+ };
151
+ };
152
+
153
+ /** @type {import('eslint').Rule.RuleModule} */
154
+ module.exports = {
155
+ create,
156
+ meta: {
157
+ type: 'suggestion',
158
+ docs: {
159
+ description: 'Use destructured variables over properties.',
160
+ },
161
+ fixable: 'code',
162
+ hasSuggestions: true,
163
+ messages: {
164
+ [MESSAGE_ID]: 'Use destructured variables over properties.',
165
+ [MESSAGE_ID_SUGGEST]: 'Replace `{{expression}}` with destructured property `{{property}}`.',
166
+ },
167
+ },
168
+ };
@@ -0,0 +1,223 @@
1
+ 'use strict';
2
+ const {getFunctionHeadLocation, getFunctionNameWithKind} = require('@eslint-community/eslint-utils');
3
+ const {
4
+ getReferences,
5
+ isNodeMatches,
6
+ } = require('./utils/index.js');
7
+ const {
8
+ functionTypes,
9
+ } = require('./ast/index.js');
10
+
11
+ const MESSAGE_ID = 'consistent-function-scoping';
12
+ const messages = {
13
+ [MESSAGE_ID]: 'Move {{functionNameWithKind}} to the outer scope.',
14
+ };
15
+
16
+ const isSameScope = (scope1, scope2) =>
17
+ scope1 && scope2 && (scope1 === scope2 || scope1.block === scope2.block);
18
+
19
+ function checkReferences(scope, parent, scopeManager) {
20
+ const hitReference = references => references.some(reference => {
21
+ if (isSameScope(parent, reference.from)) {
22
+ return true;
23
+ }
24
+
25
+ const {resolved} = reference;
26
+ const [definition] = resolved.defs;
27
+
28
+ // Skip recursive function name
29
+ if (definition?.type === 'FunctionName' && resolved.name === definition.name.name) {
30
+ return false;
31
+ }
32
+
33
+ return isSameScope(parent, resolved.scope);
34
+ });
35
+
36
+ const hitDefinitions = definitions => definitions.some(definition => {
37
+ const scope = scopeManager.acquire(definition.node);
38
+ return isSameScope(parent, scope);
39
+ });
40
+
41
+ // This check looks for neighboring function definitions
42
+ const hitIdentifier = identifiers => identifiers.some(identifier => {
43
+ // Only look at identifiers that live in a FunctionDeclaration
44
+ if (
45
+ !identifier.parent
46
+ || identifier.parent.type !== 'FunctionDeclaration'
47
+ ) {
48
+ return false;
49
+ }
50
+
51
+ const identifierScope = scopeManager.acquire(identifier);
52
+
53
+ // If we have a scope, the earlier checks should have worked so ignore them here
54
+ /* c8 ignore next 3 */
55
+ if (identifierScope) {
56
+ return false;
57
+ }
58
+
59
+ const identifierParentScope = scopeManager.acquire(identifier.parent);
60
+ /* c8 ignore next 3 */
61
+ if (!identifierParentScope) {
62
+ return false;
63
+ }
64
+
65
+ // Ignore identifiers from our own scope
66
+ if (isSameScope(scope, identifierParentScope)) {
67
+ return false;
68
+ }
69
+
70
+ // Look at the scope above the function definition to see if lives
71
+ // next to the reference being checked
72
+ return isSameScope(parent, identifierParentScope.upper);
73
+ });
74
+
75
+ return getReferences(scope)
76
+ .map(({resolved}) => resolved)
77
+ .filter(Boolean)
78
+ .some(variable =>
79
+ hitReference(variable.references)
80
+ || hitDefinitions(variable.defs)
81
+ || hitIdentifier(variable.identifiers),
82
+ );
83
+ }
84
+
85
+ // https://reactjs.org/docs/hooks-reference.html
86
+ const reactHooks = [
87
+ 'useState',
88
+ 'useEffect',
89
+ 'useContext',
90
+ 'useReducer',
91
+ 'useCallback',
92
+ 'useMemo',
93
+ 'useRef',
94
+ 'useImperativeHandle',
95
+ 'useLayoutEffect',
96
+ 'useDebugValue',
97
+ ].flatMap(hookName => [hookName, `React.${hookName}`]);
98
+
99
+ const isReactHook = scope =>
100
+ scope.block?.parent?.callee
101
+ && isNodeMatches(scope.block.parent.callee, reactHooks);
102
+
103
+ const isArrowFunctionWithThis = scope =>
104
+ scope.type === 'function'
105
+ && scope.block?.type === 'ArrowFunctionExpression'
106
+ && (scope.thisFound || scope.childScopes.some(scope => isArrowFunctionWithThis(scope)));
107
+
108
+ const iifeFunctionTypes = new Set([
109
+ 'FunctionExpression',
110
+ 'ArrowFunctionExpression',
111
+ ]);
112
+ const isIife = node =>
113
+ iifeFunctionTypes.has(node.type)
114
+ && node.parent.type === 'CallExpression'
115
+ && node.parent.callee === node;
116
+
117
+ function checkNode(node, scopeManager) {
118
+ const scope = scopeManager.acquire(node);
119
+
120
+ if (!scope || isArrowFunctionWithThis(scope)) {
121
+ return true;
122
+ }
123
+
124
+ let parentNode = node.parent;
125
+
126
+ // Skip over junk like the block statement inside of a function declaration
127
+ // or the various pieces of an arrow function.
128
+
129
+ if (parentNode.type === 'VariableDeclarator') {
130
+ parentNode = parentNode.parent;
131
+ }
132
+
133
+ if (parentNode.type === 'VariableDeclaration') {
134
+ parentNode = parentNode.parent;
135
+ }
136
+
137
+ if (parentNode.type === 'BlockStatement') {
138
+ parentNode = parentNode.parent;
139
+ }
140
+
141
+ const parentScope = scopeManager.acquire(parentNode);
142
+ if (
143
+ !parentScope
144
+ || parentScope.type === 'global'
145
+ || isReactHook(parentScope)
146
+ || isIife(parentNode)
147
+ ) {
148
+ return true;
149
+ }
150
+
151
+ return checkReferences(scope, parentScope, scopeManager);
152
+ }
153
+
154
+ /** @param {import('eslint').Rule.RuleContext} context */
155
+ const create = context => {
156
+ const {checkArrowFunctions} = {checkArrowFunctions: true, ...context.options[0]};
157
+ const {sourceCode} = context;
158
+ const {scopeManager} = sourceCode;
159
+
160
+ const functions = [];
161
+
162
+ context.on(functionTypes, () => {
163
+ functions.push(false);
164
+ });
165
+
166
+ context.on('JSXElement', () => {
167
+ // Turn off this rule if we see a JSX element because scope
168
+ // references does not include JSXElement nodes.
169
+ if (functions.length > 0) {
170
+ functions[functions.length - 1] = true;
171
+ }
172
+ });
173
+
174
+ context.onExit(functionTypes, node => {
175
+ const currentFunctionHasJsx = functions.pop();
176
+ if (currentFunctionHasJsx) {
177
+ return;
178
+ }
179
+
180
+ if (node.type === 'ArrowFunctionExpression' && !checkArrowFunctions) {
181
+ return;
182
+ }
183
+
184
+ if (checkNode(node, scopeManager)) {
185
+ return;
186
+ }
187
+
188
+ return {
189
+ node,
190
+ loc: getFunctionHeadLocation(node, sourceCode),
191
+ messageId: MESSAGE_ID,
192
+ data: {
193
+ functionNameWithKind: getFunctionNameWithKind(node, sourceCode),
194
+ },
195
+ };
196
+ });
197
+ };
198
+
199
+ const schema = [
200
+ {
201
+ type: 'object',
202
+ additionalProperties: false,
203
+ properties: {
204
+ checkArrowFunctions: {
205
+ type: 'boolean',
206
+ default: true,
207
+ },
208
+ },
209
+ },
210
+ ];
211
+
212
+ /** @type {import('eslint').Rule.RuleModule} */
213
+ module.exports = {
214
+ create,
215
+ meta: {
216
+ type: 'suggestion',
217
+ docs: {
218
+ description: 'Move function definitions to the highest possible scope.',
219
+ },
220
+ schema,
221
+ messages,
222
+ },
223
+ };
@@ -0,0 +1,215 @@
1
+ 'use strict';
2
+ const {upperFirst} = require('./utils/lodash.js');
3
+
4
+ const MESSAGE_ID_INVALID_EXPORT = 'invalidExport';
5
+ const messages = {
6
+ [MESSAGE_ID_INVALID_EXPORT]: 'Exported error name should match error class',
7
+ };
8
+
9
+ const nameRegexp = /^(?:[A-Z][\da-z]*)*Error$/;
10
+
11
+ const getClassName = name => upperFirst(name).replace(/(?:error|)$/i, 'Error');
12
+
13
+ const getConstructorMethod = className => `
14
+ constructor() {
15
+ super();
16
+ this.name = '${className}';
17
+ }
18
+ `;
19
+
20
+ const hasValidSuperClass = node => {
21
+ if (!node.superClass) {
22
+ return false;
23
+ }
24
+
25
+ let {name, type, property} = node.superClass;
26
+
27
+ if (type === 'MemberExpression') {
28
+ ({name} = property);
29
+ }
30
+
31
+ return nameRegexp.test(name);
32
+ };
33
+
34
+ const isSuperExpression = node =>
35
+ node.type === 'ExpressionStatement'
36
+ && node.expression.type === 'CallExpression'
37
+ && node.expression.callee.type === 'Super';
38
+
39
+ const isAssignmentExpression = (node, name) => {
40
+ if (
41
+ node.type !== 'ExpressionStatement'
42
+ || node.expression.type !== 'AssignmentExpression'
43
+ ) {
44
+ return false;
45
+ }
46
+
47
+ const lhs = node.expression.left;
48
+
49
+ if (!lhs.object || lhs.object.type !== 'ThisExpression') {
50
+ return false;
51
+ }
52
+
53
+ return lhs.property.name === name;
54
+ };
55
+
56
+ const isPropertyDefinition = (node, name) =>
57
+ node.type === 'PropertyDefinition'
58
+ && !node.computed
59
+ && node.key.type === 'Identifier'
60
+ && node.key.name === name;
61
+
62
+ function * customErrorDefinition(context, node) {
63
+ if (!hasValidSuperClass(node)) {
64
+ return;
65
+ }
66
+
67
+ if (node.id === null) {
68
+ return;
69
+ }
70
+
71
+ const {name} = node.id;
72
+ const className = getClassName(name);
73
+
74
+ if (name !== className) {
75
+ yield {
76
+ node: node.id,
77
+ message: `Invalid class name, use \`${className}\`.`,
78
+ };
79
+ }
80
+
81
+ const {body, range} = node.body;
82
+ const constructor = body.find(x => x.kind === 'constructor');
83
+
84
+ if (!constructor) {
85
+ yield {
86
+ node,
87
+ message: 'Add a constructor to your error.',
88
+ fix: fixer => fixer.insertTextAfterRange([
89
+ range[0],
90
+ range[0] + 1,
91
+ ], getConstructorMethod(name)),
92
+ };
93
+ return;
94
+ }
95
+
96
+ const constructorBodyNode = constructor.value.body;
97
+
98
+ // Verify the constructor has a body (TypeScript)
99
+ if (!constructorBodyNode) {
100
+ return;
101
+ }
102
+
103
+ const constructorBody = constructorBodyNode.body;
104
+
105
+ const superExpression = constructorBody.find(body => isSuperExpression(body));
106
+ const messageExpressionIndex = constructorBody.findIndex(x => isAssignmentExpression(x, 'message'));
107
+
108
+ if (!superExpression) {
109
+ yield {
110
+ node: constructorBodyNode,
111
+ message: 'Missing call to `super()` in constructor.',
112
+ };
113
+ } else if (messageExpressionIndex !== -1) {
114
+ const expression = constructorBody[messageExpressionIndex];
115
+
116
+ yield {
117
+ node: superExpression,
118
+ message: 'Pass the error message to `super()` instead of setting `this.message`.',
119
+ * fix(fixer) {
120
+ if (superExpression.expression.arguments.length === 0) {
121
+ const rhs = expression.expression.right;
122
+ yield fixer.insertTextAfterRange([
123
+ superExpression.range[0],
124
+ superExpression.range[0] + 6,
125
+ ], rhs.raw || rhs.name);
126
+ }
127
+
128
+ yield fixer.removeRange([
129
+ messageExpressionIndex === 0 ? constructorBodyNode.range[0] : constructorBody[messageExpressionIndex - 1].range[1],
130
+ expression.range[1],
131
+ ]);
132
+ },
133
+ };
134
+ }
135
+
136
+ const nameExpression = constructorBody.find(x => isAssignmentExpression(x, 'name'));
137
+ if (!nameExpression) {
138
+ const nameProperty = body.find(node => isPropertyDefinition(node, 'name'));
139
+
140
+ if (!nameProperty?.value || nameProperty.value.value !== name) {
141
+ yield {
142
+ node: nameProperty?.value ?? constructorBodyNode,
143
+ message: `The \`name\` property should be set to \`${name}\`.`,
144
+ };
145
+ }
146
+ } else if (nameExpression.expression.right.value !== name) {
147
+ yield {
148
+ node: nameExpression?.expression.right ?? constructorBodyNode,
149
+ message: `The \`name\` property should be set to \`${name}\`.`,
150
+ };
151
+ }
152
+ }
153
+
154
+ const customErrorExport = (context, node) => {
155
+ const exportsName = node.left.property.name;
156
+
157
+ const maybeError = node.right;
158
+
159
+ if (maybeError.type !== 'ClassExpression') {
160
+ return;
161
+ }
162
+
163
+ if (!hasValidSuperClass(maybeError)) {
164
+ return;
165
+ }
166
+
167
+ if (!maybeError.id) {
168
+ return;
169
+ }
170
+
171
+ // Assume rule has already fixed the error name
172
+ const errorName = maybeError.id.name;
173
+
174
+ if (exportsName === errorName) {
175
+ return;
176
+ }
177
+
178
+ return {
179
+ node: node.left.property,
180
+ messageId: MESSAGE_ID_INVALID_EXPORT,
181
+ fix: fixer => fixer.replaceText(node.left.property, errorName),
182
+ };
183
+ };
184
+
185
+ /** @param {import('eslint').Rule.RuleContext} context */
186
+ const create = context => {
187
+ context.on('ClassDeclaration', node => customErrorDefinition(context, node));
188
+ context.on('AssignmentExpression', node => {
189
+ if (node.right.type === 'ClassExpression') {
190
+ return customErrorDefinition(context, node.right);
191
+ }
192
+ });
193
+ context.on('AssignmentExpression', node => {
194
+ if (
195
+ node.left.type === 'MemberExpression'
196
+ && node.left.object.type === 'Identifier'
197
+ && node.left.object.name === 'exports'
198
+ ) {
199
+ return customErrorExport(context, node);
200
+ }
201
+ });
202
+ };
203
+
204
+ /** @type {import('eslint').Rule.RuleModule} */
205
+ module.exports = {
206
+ create,
207
+ meta: {
208
+ type: 'problem',
209
+ docs: {
210
+ description: 'Enforce correct `Error` subclassing.',
211
+ },
212
+ fixable: 'code',
213
+ messages,
214
+ },
215
+ };
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+ const {isOpeningBraceToken} = require('@eslint-community/eslint-utils');
3
+
4
+ const MESSAGE_ID = 'empty-brace-spaces';
5
+ const messages = {
6
+ [MESSAGE_ID]: 'Do not add spaces between braces.',
7
+ };
8
+
9
+ const getProblem = (node, context) => {
10
+ const {sourceCode} = context;
11
+ const filter = node.type === 'RecordExpression'
12
+ ? token => token.type === 'Punctuator' && (token.value === '#{' || token.value === '{|')
13
+ : isOpeningBraceToken;
14
+ const openingBrace = sourceCode.getFirstToken(node, {filter});
15
+ const closingBrace = sourceCode.getLastToken(node);
16
+ const [, start] = openingBrace.range;
17
+ const [end] = closingBrace.range;
18
+ const textBetween = sourceCode.text.slice(start, end);
19
+
20
+ if (!/^\s+$/.test(textBetween)) {
21
+ return;
22
+ }
23
+
24
+ return {
25
+ loc: {
26
+ start: openingBrace.loc.end,
27
+ end: closingBrace.loc.start,
28
+ },
29
+ messageId: MESSAGE_ID,
30
+ fix: fixer => fixer.removeRange([start, end]),
31
+ };
32
+ };
33
+
34
+ /** @param {import('eslint').Rule.RuleContext} context */
35
+ const create = context => {
36
+ context.on([
37
+ 'BlockStatement',
38
+ 'ClassBody',
39
+ 'StaticBlock',
40
+ ], node => {
41
+ if (node.body.length > 0) {
42
+ return;
43
+ }
44
+
45
+ return getProblem(node, context);
46
+ });
47
+
48
+ context.on([
49
+ 'ObjectExpression',
50
+ // Experimental https://github.com/tc39/proposal-record-tuple
51
+ 'RecordExpression',
52
+ ], node => {
53
+ if (node.properties.length > 0) {
54
+ return;
55
+ }
56
+
57
+ return getProblem(node, context);
58
+ });
59
+ };
60
+
61
+ /** @type {import('eslint').Rule.RuleModule} */
62
+ module.exports = {
63
+ create,
64
+ meta: {
65
+ type: 'layout',
66
+ docs: {
67
+ description: 'Enforce no spaces between braces.',
68
+ },
69
+ fixable: 'whitespace',
70
+ messages,
71
+ },
72
+ };