eslint-plugin-unicorn 57.0.0 → 58.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.
- package/index.js +5 -2
- package/package.json +20 -20
- package/readme.md +3 -3
- package/rules/escape-case.js +24 -9
- package/rules/fix/fix-space-around-keywords.js +2 -2
- package/rules/fix/remove-spaces-after.js +2 -2
- package/rules/fix/replace-reference-identifier.js +1 -0
- package/rules/fix/replace-string-raw.js +2 -0
- package/rules/fix/replace-template-element.js +1 -0
- package/rules/no-abusive-eslint-disable.js +34 -22
- package/rules/no-accessor-recursion.js +1 -1
- package/rules/no-static-only-class.js +1 -1
- package/rules/no-thenable.js +2 -9
- package/rules/no-unnecessary-polyfills.js +5 -6
- package/rules/number-literal-case.js +33 -5
- package/rules/prefer-at.js +3 -3
- package/rules/prefer-node-protocol.js +8 -7
- package/rules/prefer-switch.js +1 -0
- package/rules/shared/abbreviations.js +2 -0
- package/rules/utils/create-deprecated-rules.js +19 -12
- package/rules/utils/has-same-range.js +2 -0
- package/rules/utils/parentheses.js +2 -2
package/index.js
CHANGED
|
@@ -4,8 +4,11 @@ import rules from './rules/index.js';
|
|
|
4
4
|
import packageJson from './package.json' with {type: 'json'};
|
|
5
5
|
|
|
6
6
|
const deprecatedRules = createDeprecatedRules({
|
|
7
|
-
// {ruleId:
|
|
8
|
-
'no-instanceof-array':
|
|
7
|
+
// {ruleId: {message: string, replacedBy: string[]}}
|
|
8
|
+
'no-instanceof-array': {
|
|
9
|
+
message: 'Replaced by `unicorn/no-instanceof-builtins` which covers more cases.',
|
|
10
|
+
replacedBy: ['unicorn/no-instanceof-builtins'],
|
|
11
|
+
},
|
|
9
12
|
});
|
|
10
13
|
|
|
11
14
|
const externalRules = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-unicorn",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "58.0.0",
|
|
4
4
|
"description": "More than 100 powerful ESLint rules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/eslint-plugin-unicorn",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"sideEffects": false,
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": "
|
|
20
|
+
"node": "^18.20.0 || ^20.10.0 || >=21.0.0"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"bundle-lodash": "echo export {defaultsDeep, camelCase, kebabCase, snakeCase, upperFirst, lowerFirst} from 'lodash-es'; | npx esbuild --bundle --outfile=rules/utils/lodash.js --format=esm",
|
|
@@ -58,14 +58,15 @@
|
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@babel/helper-validator-identifier": "^7.25.9",
|
|
61
|
-
"@eslint-community/eslint-utils": "^4.
|
|
62
|
-
"
|
|
61
|
+
"@eslint-community/eslint-utils": "^4.5.1",
|
|
62
|
+
"@eslint/plugin-kit": "^0.2.7",
|
|
63
|
+
"ci-info": "^4.2.0",
|
|
63
64
|
"clean-regexp": "^1.0.0",
|
|
64
|
-
"core-js-compat": "^3.
|
|
65
|
+
"core-js-compat": "^3.41.0",
|
|
65
66
|
"esquery": "^1.6.0",
|
|
66
|
-
"globals": "^
|
|
67
|
+
"globals": "^16.0.0",
|
|
67
68
|
"indent-string": "^5.0.0",
|
|
68
|
-
"is-builtin-module": "^
|
|
69
|
+
"is-builtin-module": "^5.0.0",
|
|
69
70
|
"jsesc": "^3.1.0",
|
|
70
71
|
"pluralize": "^8.0.0",
|
|
71
72
|
"read-package-up": "^11.0.0",
|
|
@@ -76,28 +77,27 @@
|
|
|
76
77
|
},
|
|
77
78
|
"devDependencies": {
|
|
78
79
|
"@babel/code-frame": "^7.26.2",
|
|
79
|
-
"@babel/core": "^7.26.
|
|
80
|
-
"@babel/eslint-parser": "^7.26.
|
|
81
|
-
"@eslint/eslintrc": "^3.
|
|
80
|
+
"@babel/core": "^7.26.10",
|
|
81
|
+
"@babel/eslint-parser": "^7.26.10",
|
|
82
|
+
"@eslint/eslintrc": "^3.3.0",
|
|
82
83
|
"@lubien/fixture-beta-package": "^1.0.0-beta.1",
|
|
83
|
-
"@typescript-eslint/parser": "^8.
|
|
84
|
+
"@typescript-eslint/parser": "^8.26.1",
|
|
84
85
|
"ava": "^6.2.0",
|
|
85
86
|
"c8": "^10.1.3",
|
|
86
87
|
"enquirer": "^2.4.1",
|
|
87
|
-
"eslint": "^9.
|
|
88
|
+
"eslint": "^9.22.0",
|
|
88
89
|
"eslint-ava-rule-tester": "^5.0.1",
|
|
89
90
|
"eslint-config-xo": "^0.46.0",
|
|
90
|
-
"eslint-doc-generator": "^2.
|
|
91
|
+
"eslint-doc-generator": "^2.1.2",
|
|
91
92
|
"eslint-plugin-eslint-plugin": "^6.4.0",
|
|
92
|
-
"eslint-plugin-
|
|
93
|
-
"eslint-plugin-jsdoc": "^50.6.3",
|
|
93
|
+
"eslint-plugin-jsdoc": "^50.6.8",
|
|
94
94
|
"eslint-remote-tester": "^4.0.1",
|
|
95
|
-
"eslint-remote-tester-repositories": "^2.0.
|
|
95
|
+
"eslint-remote-tester-repositories": "^2.0.1",
|
|
96
96
|
"espree": "^10.3.0",
|
|
97
97
|
"listr2": "^8.2.5",
|
|
98
98
|
"lodash-es": "^4.17.21",
|
|
99
99
|
"markdownlint-cli": "^0.44.0",
|
|
100
|
-
"memoize": "^10.
|
|
100
|
+
"memoize": "^10.1.0",
|
|
101
101
|
"nano-spawn": "^0.2.0",
|
|
102
102
|
"node-style-text": "^0.0.7",
|
|
103
103
|
"npm-package-json-lint": "^8.0.0",
|
|
@@ -105,12 +105,12 @@
|
|
|
105
105
|
"open-editor": "^5.1.0",
|
|
106
106
|
"outdent": "^0.8.0",
|
|
107
107
|
"pretty-ms": "^9.2.0",
|
|
108
|
-
"typescript": "^5.
|
|
109
|
-
"vue-eslint-parser": "^
|
|
108
|
+
"typescript": "^5.8.2",
|
|
109
|
+
"vue-eslint-parser": "^10.1.1",
|
|
110
110
|
"yaml": "^2.7.0"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
|
-
"eslint": ">=9.
|
|
113
|
+
"eslint": ">=9.22.0"
|
|
114
114
|
},
|
|
115
115
|
"ava": {
|
|
116
116
|
"files": [
|
package/readme.md
CHANGED
|
@@ -49,8 +49,8 @@ export default [
|
|
|
49
49
|
<!-- Do not manually modify this list. Run: `npm run fix:eslint-docs` -->
|
|
50
50
|
<!-- begin auto-generated rules list -->
|
|
51
51
|
|
|
52
|
-
💼 [Configurations](https://github.com/sindresorhus/eslint-plugin-unicorn#
|
|
53
|
-
✅ Set in the `recommended` [configuration](https://github.com/sindresorhus/eslint-plugin-unicorn#
|
|
52
|
+
💼 [Configurations](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config) enabled in.\
|
|
53
|
+
✅ Set in the `recommended` [configuration](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config).\
|
|
54
54
|
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
|
|
55
55
|
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
56
56
|
|
|
@@ -67,7 +67,7 @@ export default [
|
|
|
67
67
|
| [custom-error-definition](docs/rules/custom-error-definition.md) | Enforce correct `Error` subclassing. | | 🔧 | |
|
|
68
68
|
| [empty-brace-spaces](docs/rules/empty-brace-spaces.md) | Enforce no spaces between braces. | ✅ | 🔧 | |
|
|
69
69
|
| [error-message](docs/rules/error-message.md) | Enforce passing a `message` value when creating a built-in error. | ✅ | | |
|
|
70
|
-
| [escape-case](docs/rules/escape-case.md) | Require escape sequences to use uppercase values.
|
|
70
|
+
| [escape-case](docs/rules/escape-case.md) | Require escape sequences to use uppercase or lowercase values. | ✅ | 🔧 | |
|
|
71
71
|
| [expiring-todo-comments](docs/rules/expiring-todo-comments.md) | Add expiration conditions to TODO comments. | ✅ | | |
|
|
72
72
|
| [explicit-length-check](docs/rules/explicit-length-check.md) | Enforce explicitly comparing the `length` or `size` property of a value. | ✅ | 🔧 | 💡 |
|
|
73
73
|
| [filename-case](docs/rules/filename-case.md) | Enforce a case style for filenames. | ✅ | | |
|
package/rules/escape-case.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import {replaceTemplateElement} from './fix/index.js';
|
|
2
2
|
import {isRegexLiteral, isStringLiteral, isTaggedTemplateLiteral} from './ast/index.js';
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const MESSAGE_ID_UPPERCASE = 'escape-uppercase';
|
|
5
|
+
const MESSAGE_ID_LOWERCASE = 'escape-lowercase';
|
|
5
6
|
const messages = {
|
|
6
|
-
[
|
|
7
|
+
[MESSAGE_ID_UPPERCASE]: 'Use uppercase characters for the value of the escape sequence.',
|
|
8
|
+
[MESSAGE_ID_LOWERCASE]: 'Use lowercase characters for the value of the escape sequence.',
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const getProblem = ({node, original, regex =
|
|
12
|
-
const fixed = original.replace(regex, data => data
|
|
11
|
+
const escapeCase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?<data>x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+})/g;
|
|
12
|
+
const escapePatternCase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?<data>x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+}|c[A-Za-z])/g;
|
|
13
|
+
const getProblem = ({node, original, regex = escapeCase, lowercase, fix}) => {
|
|
14
|
+
const fixed = original.replace(regex, data => data[0] + data.slice(1)[lowercase ? 'toLowerCase' : 'toUpperCase']());
|
|
13
15
|
|
|
14
16
|
if (fixed !== original) {
|
|
15
17
|
return {
|
|
16
18
|
node,
|
|
17
|
-
messageId:
|
|
19
|
+
messageId: lowercase ? MESSAGE_ID_LOWERCASE : MESSAGE_ID_UPPERCASE,
|
|
18
20
|
fix: fixer => fix ? fix(fixer, fixed) : fixer.replaceText(node, fixed),
|
|
19
21
|
};
|
|
20
22
|
}
|
|
@@ -22,11 +24,14 @@ const getProblem = ({node, original, regex = escapeWithLowercase, fix}) => {
|
|
|
22
24
|
|
|
23
25
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
24
26
|
const create = context => {
|
|
27
|
+
const lowercase = context.options[0] === 'lowercase';
|
|
28
|
+
|
|
25
29
|
context.on('Literal', node => {
|
|
26
30
|
if (isStringLiteral(node)) {
|
|
27
31
|
return getProblem({
|
|
28
32
|
node,
|
|
29
33
|
original: node.raw,
|
|
34
|
+
lowercase,
|
|
30
35
|
});
|
|
31
36
|
}
|
|
32
37
|
});
|
|
@@ -36,7 +41,8 @@ const create = context => {
|
|
|
36
41
|
return getProblem({
|
|
37
42
|
node,
|
|
38
43
|
original: node.raw,
|
|
39
|
-
regex:
|
|
44
|
+
regex: escapePatternCase,
|
|
45
|
+
lowercase,
|
|
40
46
|
});
|
|
41
47
|
}
|
|
42
48
|
});
|
|
@@ -49,21 +55,30 @@ const create = context => {
|
|
|
49
55
|
return getProblem({
|
|
50
56
|
node,
|
|
51
57
|
original: node.value.raw,
|
|
58
|
+
lowercase,
|
|
52
59
|
fix: (fixer, fixed) => replaceTemplateElement(fixer, node, fixed),
|
|
53
60
|
});
|
|
54
61
|
});
|
|
55
62
|
};
|
|
56
63
|
|
|
64
|
+
const schema = [
|
|
65
|
+
{
|
|
66
|
+
enum: ['uppercase', 'lowercase'],
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
57
70
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
58
71
|
const config = {
|
|
59
72
|
create,
|
|
60
73
|
meta: {
|
|
61
74
|
type: 'suggestion',
|
|
62
75
|
docs: {
|
|
63
|
-
description: 'Require escape sequences to use uppercase values.',
|
|
76
|
+
description: 'Require escape sequences to use uppercase or lowercase values.',
|
|
64
77
|
recommended: true,
|
|
65
78
|
},
|
|
66
79
|
fixable: 'code',
|
|
80
|
+
schema,
|
|
81
|
+
defaultOptions: ['uppercase'],
|
|
67
82
|
messages,
|
|
68
83
|
},
|
|
69
84
|
};
|
|
@@ -14,7 +14,7 @@ export default function * fixSpaceAroundKeyword(fixer, node, sourceCode) {
|
|
|
14
14
|
|
|
15
15
|
if (
|
|
16
16
|
tokenBefore
|
|
17
|
-
&& range[0] === tokenBefore
|
|
17
|
+
&& range[0] === sourceCode.getRange(tokenBefore)[1]
|
|
18
18
|
&& isProblematicToken(tokenBefore)
|
|
19
19
|
) {
|
|
20
20
|
yield fixer.insertTextAfter(tokenBefore, ' ');
|
|
@@ -24,7 +24,7 @@ export default function * fixSpaceAroundKeyword(fixer, node, sourceCode) {
|
|
|
24
24
|
|
|
25
25
|
if (
|
|
26
26
|
tokenAfter
|
|
27
|
-
&& range[1] === tokenAfter
|
|
27
|
+
&& range[1] === sourceCode.getRange(tokenAfter)[0]
|
|
28
28
|
&& isProblematicToken(tokenAfter)
|
|
29
29
|
) {
|
|
30
30
|
yield fixer.insertTextBefore(tokenAfter, ' ');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default function removeSpacesAfter(indexOrNodeOrToken, sourceCode, fixer) {
|
|
2
2
|
let index = indexOrNodeOrToken;
|
|
3
|
-
if (typeof indexOrNodeOrToken === 'object'
|
|
4
|
-
index = indexOrNodeOrToken
|
|
3
|
+
if (typeof indexOrNodeOrToken === 'object') {
|
|
4
|
+
index = sourceCode.getRange(indexOrNodeOrToken)[1];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
const textAfter = sourceCode.text.slice(index);
|
|
@@ -22,6 +22,7 @@ export default function replaceReferenceIdentifier(identifier, replacement, fixe
|
|
|
22
22
|
// `typeAnnotation`
|
|
23
23
|
if (identifier.typeAnnotation) {
|
|
24
24
|
return fixer.replaceTextRange(
|
|
25
|
+
// eslint-disable-next-line internal/no-restricted-property-access
|
|
25
26
|
[identifier.range[0], identifier.typeAnnotation.range[0]],
|
|
26
27
|
`${replacement}${identifier.optional ? '?' : ''}`,
|
|
27
28
|
);
|
|
@@ -3,7 +3,9 @@ const replaceStringRaw = (fixer, node, raw) =>
|
|
|
3
3
|
fixer.replaceTextRange(
|
|
4
4
|
// Ignore quotes and backticks
|
|
5
5
|
[
|
|
6
|
+
// eslint-disable-next-line internal/no-restricted-property-access
|
|
6
7
|
node.range[0] + 1,
|
|
8
|
+
// eslint-disable-next-line internal/no-restricted-property-access
|
|
7
9
|
node.range[1] - 1,
|
|
8
10
|
],
|
|
9
11
|
raw,
|
|
@@ -1,36 +1,48 @@
|
|
|
1
|
+
import {ConfigCommentParser} from '@eslint/plugin-kit';
|
|
2
|
+
|
|
1
3
|
const MESSAGE_ID = 'no-abusive-eslint-disable';
|
|
2
4
|
const messages = {
|
|
3
5
|
[MESSAGE_ID]: 'Specify the rules you want to disable.',
|
|
4
6
|
};
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
// https://github.com/eslint/eslint/blob/ecd0ede7fd2ccbb4c0daf0e4732e97ea0f49db1b/lib/linter/linter.js#L509-L512
|
|
9
|
+
const eslintDisableDirectives = new Set([
|
|
10
|
+
'eslint-disable',
|
|
11
|
+
'eslint-disable-line',
|
|
12
|
+
'eslint-disable-next-line',
|
|
13
|
+
]);
|
|
7
14
|
|
|
15
|
+
let commentParser;
|
|
8
16
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
9
17
|
const create = context => ({
|
|
10
18
|
* Program(node) {
|
|
11
19
|
for (const comment of node.comments) {
|
|
12
|
-
|
|
13
|
-
const result =
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Can't set it at the given location as the warning
|
|
23
|
-
// will be ignored due to the disable comment
|
|
24
|
-
loc: {
|
|
25
|
-
start: {
|
|
26
|
-
...sourceCode.getLoc(comment).start,
|
|
27
|
-
column: -1,
|
|
28
|
-
},
|
|
29
|
-
end: sourceCode.getLoc(comment).end,
|
|
30
|
-
},
|
|
31
|
-
messageId: MESSAGE_ID,
|
|
32
|
-
};
|
|
20
|
+
commentParser ??= new ConfigCommentParser();
|
|
21
|
+
const result = commentParser.parseDirective(comment.value);
|
|
22
|
+
|
|
23
|
+
if (!(
|
|
24
|
+
// It's a eslint-disable comment
|
|
25
|
+
eslintDisableDirectives.has(result?.label)
|
|
26
|
+
// But it did not specify any rules
|
|
27
|
+
&& !result?.value
|
|
28
|
+
)) {
|
|
29
|
+
return;
|
|
33
30
|
}
|
|
31
|
+
|
|
32
|
+
const {sourceCode} = context;
|
|
33
|
+
|
|
34
|
+
yield {
|
|
35
|
+
// Can't set it at the given location as the warning
|
|
36
|
+
// will be ignored due to the disable comment
|
|
37
|
+
loc: {
|
|
38
|
+
start: {
|
|
39
|
+
...sourceCode.getLoc(comment).start,
|
|
40
|
+
column: -1,
|
|
41
|
+
},
|
|
42
|
+
end: sourceCode.getLoc(comment).end,
|
|
43
|
+
},
|
|
44
|
+
messageId: MESSAGE_ID,
|
|
45
|
+
};
|
|
34
46
|
}
|
|
35
47
|
},
|
|
36
48
|
});
|
|
@@ -38,7 +38,7 @@ Check if a property is a valid getter or setter.
|
|
|
38
38
|
@param {import('estree').Property | import('estree').MethodDefinition} property
|
|
39
39
|
*/
|
|
40
40
|
const isValidProperty = property =>
|
|
41
|
-
['Property', 'MethodDefinition'].includes(property
|
|
41
|
+
['Property', 'MethodDefinition'].includes(property?.type)
|
|
42
42
|
&& !property.computed
|
|
43
43
|
&& ['set', 'get'].includes(property.kind)
|
|
44
44
|
&& isIdentifier(property.key);
|
|
@@ -153,7 +153,7 @@ function switchClassToObject(node, sourceCode) {
|
|
|
153
153
|
type === 'ClassExpression'
|
|
154
154
|
&& parent.type === 'ReturnStatement'
|
|
155
155
|
&& sourceCode.getLoc(body).start.line !== sourceCode.getLoc(parent).start.line
|
|
156
|
-
&& sourceCode.text.slice(classToken
|
|
156
|
+
&& sourceCode.text.slice(sourceCode.getRange(classToken)[1], sourceCode.getRange(body)[0]).trim()
|
|
157
157
|
) {
|
|
158
158
|
yield fixer.replaceText(classToken, '{');
|
|
159
159
|
|
package/rules/no-thenable.js
CHANGED
|
@@ -13,15 +13,8 @@ const messages = {
|
|
|
13
13
|
const isStringThen = (node, context) =>
|
|
14
14
|
getStaticValue(node, context.sourceCode.getScope(node))?.value === 'then';
|
|
15
15
|
|
|
16
|
-
const isPropertyThen = (node, context) =>
|
|
17
|
-
|
|
18
|
-
// https://github.com/eslint-community/eslint-utils/pull/182
|
|
19
|
-
try {
|
|
20
|
-
return getPropertyName(node, context.sourceCode.getScope(node)) === 'then';
|
|
21
|
-
} catch {}
|
|
22
|
-
|
|
23
|
-
return false;
|
|
24
|
-
};
|
|
16
|
+
const isPropertyThen = (node, context) =>
|
|
17
|
+
getPropertyName(node, context.sourceCode.getScope(node)) === 'then';
|
|
25
18
|
|
|
26
19
|
const cases = [
|
|
27
20
|
// `{then() {}}`,
|
|
@@ -68,8 +68,8 @@ function getTargets(options, dirname) {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const {
|
|
72
|
-
return
|
|
71
|
+
const {browserslist, engines} = packageResult.packageJson;
|
|
72
|
+
return browserslist ?? engines;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function create(context) {
|
|
@@ -127,10 +127,9 @@ function create(context) {
|
|
|
127
127
|
const polyfill = polyfills.find(({pattern}) => pattern.test(importedModule));
|
|
128
128
|
if (polyfill) {
|
|
129
129
|
const [, namespace, method = ''] = polyfill.feature.split('.');
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
)
|
|
133
|
-
if (checkFeatures(features)) {
|
|
130
|
+
const features = coreJsEntries[`core-js/full/${namespace}${method && '/'}${method}`];
|
|
131
|
+
|
|
132
|
+
if (features && checkFeatures(features)) {
|
|
134
133
|
return {node, messageId: MESSAGE_ID_POLYFILL};
|
|
135
134
|
}
|
|
136
135
|
}
|
|
@@ -6,25 +6,33 @@ const messages = {
|
|
|
6
6
|
[MESSAGE_ID]: 'Invalid number literal casing.',
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
@param {string} raw
|
|
11
|
+
@param {Options} options
|
|
12
|
+
*/
|
|
13
|
+
const fix = (raw, {hexadecimalValue}) => {
|
|
10
14
|
let fixed = raw.toLowerCase();
|
|
11
15
|
if (fixed.startsWith('0x')) {
|
|
12
|
-
fixed = '0x' + fixed.slice(2)
|
|
16
|
+
fixed = '0x' + fixed.slice(2)[hexadecimalValue === 'lowercase' ? 'toLowerCase' : 'toUpperCase']();
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
return fixed;
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
19
|
-
const create =
|
|
23
|
+
const create = context => ({
|
|
20
24
|
Literal(node) {
|
|
21
25
|
const {raw} = node;
|
|
22
26
|
|
|
27
|
+
/** @type {Options} */
|
|
28
|
+
const options = context.options[0] ?? {};
|
|
29
|
+
options.hexadecimalValue ??= 'uppercase';
|
|
30
|
+
|
|
23
31
|
let fixed = raw;
|
|
24
32
|
if (isNumberLiteral(node)) {
|
|
25
|
-
fixed = fix(raw);
|
|
33
|
+
fixed = fix(raw, options);
|
|
26
34
|
} else if (isBigIntLiteral(node)) {
|
|
27
|
-
fixed = fix(raw.slice(0, -1)) + 'n';
|
|
35
|
+
fixed = fix(raw.slice(0, -1), options) + 'n';
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
if (raw !== fixed) {
|
|
@@ -37,6 +45,22 @@ const create = () => ({
|
|
|
37
45
|
},
|
|
38
46
|
});
|
|
39
47
|
|
|
48
|
+
/** @typedef {Record<keyof typeof schema[0]["properties"], typeof caseEnum["enum"][number]>} Options */
|
|
49
|
+
|
|
50
|
+
const caseEnum = /** @type {const} */ ({
|
|
51
|
+
enum: ['uppercase', 'lowercase'],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const schema = [
|
|
55
|
+
{
|
|
56
|
+
type: 'object',
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
properties: {
|
|
59
|
+
hexadecimalValue: caseEnum,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
40
64
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
41
65
|
const config = {
|
|
42
66
|
create: checkVueTemplate(create),
|
|
@@ -47,6 +71,10 @@ const config = {
|
|
|
47
71
|
recommended: true,
|
|
48
72
|
},
|
|
49
73
|
fixable: 'code',
|
|
74
|
+
schema,
|
|
75
|
+
defaultOptions: [{
|
|
76
|
+
hexadecimalValue: 'uppercase',
|
|
77
|
+
}],
|
|
50
78
|
messages,
|
|
51
79
|
},
|
|
52
80
|
};
|
package/rules/prefer-at.js
CHANGED
|
@@ -197,9 +197,9 @@ function create(context) {
|
|
|
197
197
|
if (
|
|
198
198
|
tokenBefore.type === 'Punctuator'
|
|
199
199
|
&& tokenBefore.value === '-'
|
|
200
|
-
&& /^\s+$/.test(sourceCode.text.slice(tokenBefore
|
|
200
|
+
&& /^\s+$/.test(sourceCode.text.slice(sourceCode.getRange(tokenBefore)[1], sourceCode.getRange(numberNode)[0]))
|
|
201
201
|
) {
|
|
202
|
-
yield fixer.removeRange([tokenBefore
|
|
202
|
+
yield fixer.removeRange([sourceCode.getRange(tokenBefore)[1], sourceCode.getRange(numberNode)[0]]);
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
@@ -275,7 +275,7 @@ function create(context) {
|
|
|
275
275
|
// Remove extra arguments
|
|
276
276
|
if (sliceCall.arguments.length !== 1) {
|
|
277
277
|
const [, start] = getParenthesizedRange(sliceCall.arguments[0], sourceCode);
|
|
278
|
-
const [end] = sourceCode.getLastToken(sliceCall)
|
|
278
|
+
const [end] = sourceCode.getRange(sourceCode.getLastToken(sliceCall));
|
|
279
279
|
yield fixer.removeRange([start, end]);
|
|
280
280
|
}
|
|
281
281
|
|
|
@@ -5,6 +5,7 @@ const MESSAGE_ID = 'prefer-node-protocol';
|
|
|
5
5
|
const messages = {
|
|
6
6
|
[MESSAGE_ID]: 'Prefer `node:{{moduleName}}` over `{{moduleName}}`.',
|
|
7
7
|
};
|
|
8
|
+
const NODE_PROTOCOL = 'node:';
|
|
8
9
|
|
|
9
10
|
const create = context => ({
|
|
10
11
|
Literal(node) {
|
|
@@ -27,12 +28,12 @@ const create = context => ({
|
|
|
27
28
|
|
|
28
29
|
const {value} = node;
|
|
29
30
|
|
|
30
|
-
if (
|
|
31
|
-
typeof value
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
) {
|
|
31
|
+
if (!(
|
|
32
|
+
typeof value === 'string'
|
|
33
|
+
&& !value.startsWith(NODE_PROTOCOL)
|
|
34
|
+
&& isBuiltinModule(value)
|
|
35
|
+
&& isBuiltinModule(`${NODE_PROTOCOL}${value}`)
|
|
36
|
+
)) {
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -42,7 +43,7 @@ const create = context => ({
|
|
|
42
43
|
messageId: MESSAGE_ID,
|
|
43
44
|
data: {moduleName: value},
|
|
44
45
|
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
45
|
-
fix: fixer => fixer.insertTextAfterRange([insertPosition, insertPosition],
|
|
46
|
+
fix: fixer => fixer.insertTextAfterRange([insertPosition, insertPosition], NODE_PROTOCOL),
|
|
46
47
|
};
|
|
47
48
|
},
|
|
48
49
|
});
|
package/rules/prefer-switch.js
CHANGED
|
@@ -98,6 +98,7 @@ const getBreakTarget = node => {
|
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
const isNodeInsideNode = (inner, outer) =>
|
|
101
|
+
// eslint-disable-next-line internal/no-restricted-property-access
|
|
101
102
|
inner.range[0] >= outer.range[0] && inner.range[1] <= outer.range[1];
|
|
102
103
|
function hasBreakInside(breakStatements, node) {
|
|
103
104
|
for (const breakStatement of breakStatements) {
|
|
@@ -252,6 +252,8 @@ export const defaultAllowList = {
|
|
|
252
252
|
getInitialProps: true,
|
|
253
253
|
getServerSideProps: true,
|
|
254
254
|
getStaticProps: true,
|
|
255
|
+
// The name iOS is a standard name for an OS
|
|
256
|
+
iOS: true,
|
|
255
257
|
// React PropTypes
|
|
256
258
|
// https://reactjs.org/docs/typechecking-with-proptypes.html
|
|
257
259
|
propTypes: true,
|
|
@@ -3,20 +3,27 @@ import packageJson from '../../package.json' with {type: 'json'};
|
|
|
3
3
|
const repoUrl = 'https://github.com/sindresorhus/eslint-plugin-unicorn';
|
|
4
4
|
|
|
5
5
|
/** @returns {{ [ruleName: string]: import('eslint').Rule.RuleModule }} */
|
|
6
|
-
export default function createDeprecatedRules(
|
|
6
|
+
export default function createDeprecatedRules(rules) {
|
|
7
7
|
return Object.fromEntries(
|
|
8
|
-
Object.entries(
|
|
9
|
-
ruleId
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
Object.entries(rules).map(([ruleId, deprecatedInfo]) => {
|
|
9
|
+
const url = `${repoUrl}/blob/v${packageJson.version}/docs/deprecated-rules.md#${ruleId}`;
|
|
10
|
+
return [
|
|
11
|
+
ruleId,
|
|
12
|
+
{
|
|
13
|
+
create: () => ({}),
|
|
14
|
+
meta: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: deprecatedInfo.message,
|
|
17
|
+
url,
|
|
18
|
+
},
|
|
19
|
+
deprecated: {
|
|
20
|
+
message: deprecatedInfo.message,
|
|
21
|
+
url,
|
|
22
|
+
replacedBy: deprecatedInfo.replacedBy,
|
|
23
|
+
},
|
|
15
24
|
},
|
|
16
|
-
deprecated: true,
|
|
17
|
-
replacedBy: Array.isArray(replacedBy) ? replacedBy : [replacedBy],
|
|
18
25
|
},
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
];
|
|
27
|
+
}),
|
|
21
28
|
);
|
|
22
29
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const hasSameRange = (node1, node2) =>
|
|
2
2
|
node1
|
|
3
3
|
&& node2
|
|
4
|
+
// eslint-disable-next-line internal/no-restricted-property-access
|
|
4
5
|
&& node1.range[0] === node2.range[0]
|
|
6
|
+
// eslint-disable-next-line internal/no-restricted-property-access
|
|
5
7
|
&& node1.range[1] === node2.range[1];
|
|
6
8
|
|
|
7
9
|
export default hasSameRange;
|
|
@@ -46,8 +46,8 @@ Get the parenthesized range of the node.
|
|
|
46
46
|
*/
|
|
47
47
|
export function getParenthesizedRange(node, sourceCode) {
|
|
48
48
|
const parentheses = getParentheses(node, sourceCode);
|
|
49
|
-
const [start] = (parentheses[0]
|
|
50
|
-
const [, end] = (parentheses.at(-1)
|
|
49
|
+
const [start] = sourceCode.getRange(parentheses[0] ?? node);
|
|
50
|
+
const [, end] = sourceCode.getRange(parentheses.at(-1) ?? node);
|
|
51
51
|
return [start, end];
|
|
52
52
|
}
|
|
53
53
|
|