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,109 @@
1
+ 'use strict';
2
+ const {isColonToken} = require('@eslint-community/eslint-utils');
3
+ const getSwitchCaseHeadLocation = require('./utils/get-switch-case-head-location.js');
4
+ const getIndentString = require('./utils/get-indent-string.js');
5
+ const {replaceNodeOrTokenAndSpacesBefore} = require('./fix/index.js');
6
+
7
+ const MESSAGE_ID_EMPTY_CLAUSE = 'switch-case-braces/empty';
8
+ const MESSAGE_ID_MISSING_BRACES = 'switch-case-braces/missing';
9
+ const MESSAGE_ID_UNNECESSARY_BRACES = 'switch-case-braces/unnecessary';
10
+ const messages = {
11
+ [MESSAGE_ID_EMPTY_CLAUSE]: 'Unexpected braces in empty case clause.',
12
+ [MESSAGE_ID_MISSING_BRACES]: 'Missing braces in case clause.',
13
+ [MESSAGE_ID_UNNECESSARY_BRACES]: 'Unnecessary braces in case clause.',
14
+ };
15
+
16
+ function * removeBraces(fixer, node, sourceCode) {
17
+ const [blockStatement] = node.consequent;
18
+ const openingBraceToken = sourceCode.getFirstToken(blockStatement);
19
+ yield * replaceNodeOrTokenAndSpacesBefore(openingBraceToken, '', fixer, sourceCode);
20
+
21
+ const closingBraceToken = sourceCode.getLastToken(blockStatement);
22
+ yield fixer.remove(closingBraceToken);
23
+ }
24
+
25
+ function * addBraces(fixer, node, sourceCode) {
26
+ const colonToken = sourceCode.getTokenAfter(
27
+ node.test || sourceCode.getFirstToken(node),
28
+ isColonToken,
29
+ );
30
+ yield fixer.insertTextAfter(colonToken, ' {');
31
+
32
+ const lastToken = sourceCode.getLastToken(node);
33
+ const indent = getIndentString(node, sourceCode);
34
+ yield fixer.insertTextAfter(lastToken, `\n${indent}}`);
35
+ }
36
+
37
+ /** @param {import('eslint').Rule.RuleContext} context */
38
+ const create = context => {
39
+ const isBracesRequired = context.options[0] !== 'avoid';
40
+ const {sourceCode} = context;
41
+
42
+ return {
43
+ SwitchCase(node) {
44
+ const {consequent} = node;
45
+ if (consequent.length === 0) {
46
+ return;
47
+ }
48
+
49
+ if (
50
+ consequent.length === 1
51
+ && consequent[0].type === 'BlockStatement'
52
+ && consequent[0].body.length === 0
53
+ ) {
54
+ return {
55
+ node,
56
+ loc: sourceCode.getFirstToken(consequent[0]).loc,
57
+ messageId: MESSAGE_ID_EMPTY_CLAUSE,
58
+ fix: fixer => removeBraces(fixer, node, sourceCode),
59
+ };
60
+ }
61
+
62
+ if (
63
+ isBracesRequired
64
+ && !(
65
+ consequent.length === 1
66
+ && consequent[0].type === 'BlockStatement'
67
+ )
68
+ ) {
69
+ return {
70
+ node,
71
+ loc: getSwitchCaseHeadLocation(node, sourceCode),
72
+ messageId: MESSAGE_ID_MISSING_BRACES,
73
+ fix: fixer => addBraces(fixer, node, sourceCode),
74
+ };
75
+ }
76
+
77
+ if (
78
+ !isBracesRequired
79
+ && consequent.length === 1
80
+ && consequent[0].type === 'BlockStatement'
81
+ && consequent[0].body.every(node =>
82
+ node.type !== 'VariableDeclaration'
83
+ && node.type !== 'FunctionDeclaration',
84
+ )
85
+ ) {
86
+ return {
87
+ node,
88
+ loc: sourceCode.getFirstToken(consequent[0]).loc,
89
+ messageId: MESSAGE_ID_UNNECESSARY_BRACES,
90
+ fix: fixer => removeBraces(fixer, node, sourceCode),
91
+ };
92
+ }
93
+ },
94
+ };
95
+ };
96
+
97
+ /** @type {import('eslint').Rule.RuleModule} */
98
+ module.exports = {
99
+ create,
100
+ meta: {
101
+ type: 'layout',
102
+ docs: {
103
+ description: 'Enforce consistent brace style for `case` clauses.',
104
+ },
105
+ fixable: 'code',
106
+ schema: [{enum: ['always', 'avoid']}],
107
+ messages,
108
+ },
109
+ };
@@ -0,0 +1,219 @@
1
+ 'use strict';
2
+ const stripIndent = require('strip-indent');
3
+ const indentString = require('indent-string');
4
+ const esquery = require('esquery');
5
+ const {replaceTemplateElement} = require('./fix/index.js');
6
+ const {isMethodCall, isCallExpression} = require('./ast/index.js');
7
+
8
+ const MESSAGE_ID_IMPROPERLY_INDENTED_TEMPLATE = 'template-indent';
9
+ const messages = {
10
+ [MESSAGE_ID_IMPROPERLY_INDENTED_TEMPLATE]: 'Templates should be properly indented.',
11
+ };
12
+
13
+ const isJestInlineSnapshot = node =>
14
+ isMethodCall(node.parent, {
15
+ method: 'toMatchInlineSnapshot',
16
+ argumentsLength: 1,
17
+ optionalCall: false,
18
+ optionalMember: false,
19
+ })
20
+ && node.parent.arguments[0] === node
21
+ && isCallExpression(node.parent.callee.object, {
22
+ name: 'expect',
23
+ argumentsLength: 1,
24
+ optionalCall: false,
25
+ optionalMember: false,
26
+ });
27
+
28
+ const parsedEsquerySelectors = new Map();
29
+ const parseEsquerySelector = selector => {
30
+ if (!parsedEsquerySelectors.has(selector)) {
31
+ parsedEsquerySelectors.set(selector, esquery.parse(selector));
32
+ }
33
+
34
+ return parsedEsquerySelectors.get(selector);
35
+ };
36
+
37
+ /** @param {import('eslint').Rule.RuleContext} context */
38
+ const create = context => {
39
+ const {sourceCode} = context;
40
+ const options = {
41
+ tags: ['outdent', 'dedent', 'gql', 'sql', 'html', 'styled'],
42
+ functions: ['dedent', 'stripIndent'],
43
+ selectors: [],
44
+ comments: ['HTML', 'indent'],
45
+ ...context.options[0],
46
+ };
47
+
48
+ options.comments = options.comments.map(comment => comment.toLowerCase());
49
+
50
+ /** @param {import('@babel/core').types.TemplateLiteral} node */
51
+ const getProblem = node => {
52
+ const delimiter = '__PLACEHOLDER__' + Math.random();
53
+ const joined = node.quasis
54
+ .map(quasi => {
55
+ const untrimmedText = sourceCode.getText(quasi);
56
+ return untrimmedText.slice(1, quasi.tail ? -1 : -2);
57
+ })
58
+ .join(delimiter);
59
+
60
+ const eolMatch = joined.match(/\r?\n/);
61
+ if (!eolMatch) {
62
+ return;
63
+ }
64
+
65
+ const eol = eolMatch[0];
66
+
67
+ const startLine = sourceCode.lines[node.loc.start.line - 1];
68
+ const marginMatch = startLine.match(/^(\s*)\S/);
69
+ const parentMargin = marginMatch ? marginMatch[1] : '';
70
+
71
+ let indent;
72
+ if (typeof options.indent === 'string') {
73
+ indent = options.indent;
74
+ } else if (typeof options.indent === 'number') {
75
+ indent = ' '.repeat(options.indent);
76
+ } else {
77
+ const tabs = parentMargin.startsWith('\t');
78
+ indent = tabs ? '\t' : ' ';
79
+ }
80
+
81
+ const dedented = stripIndent(joined);
82
+ const trimmed = dedented.replaceAll(new RegExp(`^${eol}|${eol}[ \t]*$`, 'g'), '');
83
+
84
+ const fixed
85
+ = eol
86
+ + indentString(trimmed, 1, {indent: parentMargin + indent})
87
+ + eol
88
+ + parentMargin;
89
+
90
+ if (fixed === joined) {
91
+ return;
92
+ }
93
+
94
+ return {
95
+ node,
96
+ messageId: MESSAGE_ID_IMPROPERLY_INDENTED_TEMPLATE,
97
+ fix: fixer => fixed
98
+ .split(delimiter)
99
+ .map((replacement, index) => replaceTemplateElement(fixer, node.quasis[index], replacement)),
100
+ };
101
+ };
102
+
103
+ const shouldIndent = node => {
104
+ if (options.comments.length > 0) {
105
+ const previousToken = sourceCode.getTokenBefore(node, {includeComments: true});
106
+ if (previousToken?.type === 'Block' && options.comments.includes(previousToken.value.trim().toLowerCase())) {
107
+ return true;
108
+ }
109
+ }
110
+
111
+ if (isJestInlineSnapshot(node)) {
112
+ return true;
113
+ }
114
+
115
+ if (
116
+ options.tags.length > 0
117
+ && node.parent.type === 'TaggedTemplateExpression'
118
+ && node.parent.quasi === node
119
+ && node.parent.tag.type === 'Identifier'
120
+ && options.tags.includes(node.parent.tag.name)
121
+ ) {
122
+ return true;
123
+ }
124
+
125
+ if (
126
+ options.functions.length > 0
127
+ && node.parent.type === 'CallExpression'
128
+ && node.parent.arguments.includes(node)
129
+ && node.parent.callee.type === 'Identifier'
130
+ && options.functions.includes(node.parent.callee.name)
131
+ ) {
132
+ return true;
133
+ }
134
+
135
+ if (options.selectors.length > 0) {
136
+ const ancestors = sourceCode.getAncestors(node).reverse();
137
+ if (options.selectors.some(selector => esquery.matches(node, parseEsquerySelector(selector), ancestors))) {
138
+ return true;
139
+ }
140
+ }
141
+
142
+ return false;
143
+ };
144
+
145
+ return {
146
+ /** @param {import('@babel/core').types.TemplateLiteral} node */
147
+ TemplateLiteral(node) {
148
+ if (!shouldIndent(node)) {
149
+ return;
150
+ }
151
+
152
+ return getProblem(node);
153
+ },
154
+ };
155
+ };
156
+
157
+ /** @type {import('json-schema').JSONSchema7[]} */
158
+ const schema = [
159
+ {
160
+ type: 'object',
161
+ additionalProperties: false,
162
+ properties: {
163
+ indent: {
164
+ oneOf: [
165
+ {
166
+ type: 'string',
167
+ pattern: /^\s+$/.source,
168
+ },
169
+ {
170
+ type: 'integer',
171
+ minimum: 1,
172
+ },
173
+ ],
174
+ },
175
+ tags: {
176
+ type: 'array',
177
+ uniqueItems: true,
178
+ items: {
179
+ type: 'string',
180
+ },
181
+ },
182
+ functions: {
183
+ type: 'array',
184
+ uniqueItems: true,
185
+ items: {
186
+ type: 'string',
187
+ },
188
+ },
189
+ selectors: {
190
+ type: 'array',
191
+ uniqueItems: true,
192
+ items: {
193
+ type: 'string',
194
+ },
195
+ },
196
+ comments: {
197
+ type: 'array',
198
+ uniqueItems: true,
199
+ items: {
200
+ type: 'string',
201
+ },
202
+ },
203
+ },
204
+ },
205
+ ];
206
+
207
+ /** @type {import('eslint').Rule.RuleModule} */
208
+ module.exports = {
209
+ create,
210
+ meta: {
211
+ type: 'suggestion',
212
+ docs: {
213
+ description: 'Fix whitespace-insensitive template indentation.',
214
+ },
215
+ fixable: 'code',
216
+ schema,
217
+ messages,
218
+ },
219
+ };
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+ const {replaceStringLiteral} = require('./fix/index.js');
3
+
4
+ const MESSAGE_ID_ERROR = 'text-encoding-identifier/error';
5
+ const MESSAGE_ID_SUGGESTION = 'text-encoding-identifier/suggestion';
6
+ const messages = {
7
+ [MESSAGE_ID_ERROR]: 'Prefer `{{replacement}}` over `{{value}}`.',
8
+ [MESSAGE_ID_SUGGESTION]: 'Replace `{{value}}` with `{{replacement}}`.',
9
+ };
10
+
11
+ const getReplacement = encoding => {
12
+ switch (encoding.toLowerCase()) {
13
+ // eslint-disable-next-line unicorn/text-encoding-identifier-case
14
+ case 'utf-8':
15
+ case 'utf8': {
16
+ return 'utf8';
17
+ }
18
+
19
+ case 'ascii': {
20
+ return 'ascii';
21
+ }
22
+ // No default
23
+ }
24
+ };
25
+
26
+ // `fs.{readFile,readFileSync}()`
27
+ const isFsReadFileEncoding = node =>
28
+ node.parent.type === 'CallExpression'
29
+ && !node.parent.optional
30
+ && node.parent.arguments[1] === node
31
+ && node.parent.arguments[0].type !== 'SpreadElement'
32
+ && node.parent.callee.type === 'MemberExpression'
33
+ && !node.parent.callee.optional
34
+ && !node.parent.callee.computed
35
+ && node.parent.callee.property.type === 'Identifier'
36
+ && (node.parent.callee.property.name === 'readFile' || node.parent.callee.property.name === 'readFileSync');
37
+
38
+ /** @param {import('eslint').Rule.RuleContext} context */
39
+ const create = () => ({
40
+ Literal(node) {
41
+ if (typeof node.value !== 'string') {
42
+ return;
43
+ }
44
+
45
+ if (
46
+ // eslint-disable-next-line unicorn/text-encoding-identifier-case
47
+ node.value === 'utf-8'
48
+ && node.parent.type === 'JSXAttribute'
49
+ && node.parent.value === node
50
+ && node.parent.name.type === 'JSXIdentifier'
51
+ && node.parent.name.name.toLowerCase() === 'charset'
52
+ && node.parent.parent.type === 'JSXOpeningElement'
53
+ && node.parent.parent.attributes.includes(node.parent)
54
+ && node.parent.parent.name.type === 'JSXIdentifier'
55
+ && node.parent.parent.name.name.toLowerCase() === 'meta'
56
+ ) {
57
+ return;
58
+ }
59
+
60
+ const {raw} = node;
61
+ const value = raw.slice(1, -1);
62
+
63
+ const replacement = getReplacement(value);
64
+ if (!replacement || replacement === value) {
65
+ return;
66
+ }
67
+
68
+ /** @param {import('eslint').Rule.RuleFixer} fixer */
69
+ const fix = fixer => replaceStringLiteral(fixer, node, replacement);
70
+
71
+ const problem = {
72
+ node,
73
+ messageId: MESSAGE_ID_ERROR,
74
+ data: {
75
+ value,
76
+ replacement,
77
+ },
78
+ };
79
+
80
+ if (isFsReadFileEncoding(node)) {
81
+ problem.fix = fix;
82
+ return problem;
83
+ }
84
+
85
+ problem.suggest = [
86
+ {
87
+ messageId: MESSAGE_ID_SUGGESTION,
88
+ fix: fixer => replaceStringLiteral(fixer, node, replacement),
89
+ },
90
+ ];
91
+
92
+ return problem;
93
+ },
94
+ });
95
+
96
+ /** @type {import('eslint').Rule.RuleModule} */
97
+ module.exports = {
98
+ create,
99
+ meta: {
100
+ type: 'suggestion',
101
+ docs: {
102
+ description: 'Enforce consistent case for text encoding identifiers.',
103
+ },
104
+ fixable: 'code',
105
+ hasSuggestions: true,
106
+ messages,
107
+ },
108
+ };
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+ const {switchCallExpressionToNewExpression} = require('./fix/index.js');
3
+
4
+ const messageId = 'throw-new-error';
5
+ const messages = {
6
+ [messageId]: 'Use `new` when throwing an error.',
7
+ };
8
+
9
+ const customError = /^(?:[A-Z][\da-z]*)*Error$/;
10
+
11
+ /** @param {import('eslint').Rule.RuleContext} context */
12
+ const create = context => ({
13
+ CallExpression(node) {
14
+ if (!(
15
+ node.parent.type === 'ThrowStatement'
16
+ && node.parent.argument === node
17
+ )) {
18
+ return;
19
+ }
20
+
21
+ const {callee} = node;
22
+ if (!(
23
+ (callee.type === 'Identifier' && customError.test(callee.name))
24
+ || (
25
+ callee.type === 'MemberExpression'
26
+ && !callee.computed
27
+ && callee.property.type === 'Identifier'
28
+ && customError.test(callee.property.name)
29
+ )
30
+ )) {
31
+ return;
32
+ }
33
+
34
+ return {
35
+ node,
36
+ messageId,
37
+ fix: fixer => switchCallExpressionToNewExpression(node, context.sourceCode, fixer),
38
+ };
39
+ },
40
+ });
41
+
42
+ /** @type {import('eslint').Rule.RuleModule} */
43
+ module.exports = {
44
+ create,
45
+ meta: {
46
+ type: 'suggestion',
47
+ docs: {
48
+ description: 'Require `new` when throwing an error.',
49
+ },
50
+ fixable: 'code',
51
+ messages,
52
+ },
53
+ };
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+ const {isMemberExpression} = require('../ast/index.js');
3
+
4
+ /**
5
+ @param {
6
+ {
7
+ object?: string,
8
+ method?: string,
9
+ methods?: string[],
10
+ }
11
+ } [options]
12
+ @returns {string}
13
+ */
14
+ function isPrototypeProperty(node, options) {
15
+ const {
16
+ object,
17
+ property,
18
+ properties,
19
+ } = {
20
+ property: '',
21
+ properties: [],
22
+ ...options,
23
+ };
24
+
25
+ if (!isMemberExpression(node, {
26
+ property,
27
+ properties,
28
+ optional: false,
29
+ })) {
30
+ return;
31
+ }
32
+
33
+ const objectNode = node.object;
34
+
35
+ return (
36
+ // `Object.prototype.method` or `Array.prototype.method`
37
+ isMemberExpression(objectNode, {
38
+ object,
39
+ property: 'prototype',
40
+ optional: false,
41
+ })
42
+ // `[].method`
43
+ || (
44
+ object === 'Array'
45
+ && objectNode.type === 'ArrayExpression'
46
+ && objectNode.elements.length === 0
47
+ )
48
+ // `{}.method`
49
+ || (
50
+ object === 'Object'
51
+ && objectNode.type === 'ObjectExpression'
52
+ && objectNode.properties.length === 0
53
+ )
54
+ );
55
+ }
56
+
57
+ const isArrayPrototypeProperty = (node, options) => isPrototypeProperty(node, {...options, object: 'Array'});
58
+ const isObjectPrototypeProperty = (node, options) => isPrototypeProperty(node, {...options, object: 'Object'});
59
+
60
+ module.exports = {
61
+ isArrayPrototypeProperty,
62
+ isObjectPrototypeProperty,
63
+ };
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ const ISSUE_LINK_PREFIX = 'https://github.com/sindresorhus/eslint-plugin-unicorn/issues/new?';
4
+ function assertToken(token, {test, expected, ruleId}) {
5
+ if (test?.(token)) {
6
+ return;
7
+ }
8
+
9
+ expected = Array.isArray(expected) ? expected : [expected];
10
+ expected = expected.map(expectedToken => typeof expectedToken === 'string' ? {value: expectedToken} : expectedToken);
11
+
12
+ if (
13
+ !test
14
+ && expected.some(
15
+ expectedToken =>
16
+ Object.entries(expectedToken)
17
+ .every(([key, value]) => token[key] === value),
18
+ )
19
+ ) {
20
+ return;
21
+ }
22
+
23
+ const actual = `'${JSON.stringify({value: token.value, type: token.type})}'`;
24
+ expected = expected.map(expectedToken => `'${JSON.stringify(expectedToken)}'`).join(' or ');
25
+ const title = `\`${ruleId}\`: Unexpected token ${actual}`;
26
+ const issueLink = `${ISSUE_LINK_PREFIX}title=${encodeURIComponent(title)}`;
27
+ const message = `Expected token ${expected}, got ${actual}.\nPlease open an issue at ${issueLink}.`;
28
+
29
+ throw new Error(message);
30
+ }
31
+
32
+ module.exports = assertToken;