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 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: ReplacementRuleId | ReplacementRuleId[]}, if no replacement, use `{ruleId: []}`
8
- 'no-instanceof-array': 'unicorn/no-instanceof-builtins',
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": "57.0.0",
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": ">=18.18"
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.4.1",
62
- "ci-info": "^4.1.0",
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.40.0",
65
+ "core-js-compat": "^3.41.0",
65
66
  "esquery": "^1.6.0",
66
- "globals": "^15.15.0",
67
+ "globals": "^16.0.0",
67
68
  "indent-string": "^5.0.0",
68
- "is-builtin-module": "^4.0.0",
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.9",
80
- "@babel/eslint-parser": "^7.26.8",
81
- "@eslint/eslintrc": "^3.2.0",
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.24.1",
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.20.1",
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.0.2",
91
+ "eslint-doc-generator": "^2.1.2",
91
92
  "eslint-plugin-eslint-plugin": "^6.4.0",
92
- "eslint-plugin-internal-rules": "file:./scripts/internal-rules/",
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.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.0.0",
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.7.3",
109
- "vue-eslint-parser": "^9.4.3",
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.20.0"
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#preset-configs-eslintconfigjs) enabled in.\
53
- ✅ Set in the `recommended` [configuration](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs).\
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. | ✅ | | |
@@ -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 MESSAGE_ID = 'escape-case';
4
+ const MESSAGE_ID_UPPERCASE = 'escape-uppercase';
5
+ const MESSAGE_ID_LOWERCASE = 'escape-lowercase';
5
6
  const messages = {
6
- [MESSAGE_ID]: 'Use uppercase characters for the value of the escape sequence.',
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 escapeWithLowercase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?<data>x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+})/g;
10
- const escapePatternWithLowercase = /(?<=(?:^|[^\\])(?:\\\\)*\\)(?<data>x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|u{[\dA-Fa-f]+}|c[a-z])/g;
11
- const getProblem = ({node, original, regex = escapeWithLowercase, fix}) => {
12
- const fixed = original.replace(regex, data => data.slice(0, 1) + data.slice(1).toUpperCase());
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: MESSAGE_ID,
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: escapePatternWithLowercase,
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.range[1]
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.range[0]
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' && Array.isArray(indexOrNodeOrToken.range)) {
4
- index = indexOrNodeOrToken.range[1];
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,4 +1,5 @@
1
1
  const replaceTemplateElement = (fixer, node, replacement) => {
2
+ // eslint-disable-next-line internal/no-restricted-property-access
2
3
  const {range: [start, end], tail} = node;
3
4
  return fixer.replaceTextRange(
4
5
  [start + 1, end - (tail ? 1 : 2)],
@@ -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
- const disableRegex = /^eslint-disable(?:-next-line|-line)?(?<ruleId>$|(?:\s+(?:@(?:[\w-]+\/){1,2})?[\w-]+)?)/;
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
- const value = comment.value.trim();
13
- const result = disableRegex.exec(value);
14
-
15
- if (
16
- result // It's a eslint-disable comment
17
- && !result.groups.ruleId // But it did not specify any rules
18
- ) {
19
- const {sourceCode} = context;
20
-
21
- yield {
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.type)
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.range[1], body.range[0]).trim()
156
+ && sourceCode.text.slice(sourceCode.getRange(classToken)[1], sourceCode.getRange(body)[0]).trim()
157
157
  ) {
158
158
  yield fixer.replaceText(classToken, '{');
159
159
 
@@ -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
- // `getPropertyName` throws on `({[Symbol.prototype]: 0})`
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 {browserlist, engines} = packageResult.packageJson;
72
- return browserlist ?? engines;
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 [, features] = Object.entries(coreJsEntries).find(
131
- entry => entry[0] === `core-js/full/${namespace}${method && '/'}${method}`,
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
- const fix = raw => {
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).toUpperCase();
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
  };
@@ -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.range[1], numberNode.range[0]))
200
+ && /^\s+$/.test(sourceCode.text.slice(sourceCode.getRange(tokenBefore)[1], sourceCode.getRange(numberNode)[0]))
201
201
  ) {
202
- yield fixer.removeRange([tokenBefore.range[1], numberNode.range[0]]);
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).range;
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 !== 'string'
32
- || value.startsWith('node:')
33
- || /^bun(?::|$)/.test(value)
34
- || !isBuiltinModule(value)
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], 'node:'),
46
+ fix: fixer => fixer.insertTextAfterRange([insertPosition, insertPosition], NODE_PROTOCOL),
46
47
  };
47
48
  },
48
49
  });
@@ -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(data) {
6
+ export default function createDeprecatedRules(rules) {
7
7
  return Object.fromEntries(
8
- Object.entries(data).map(([ruleId, replacedBy = []]) => [
9
- ruleId,
10
- {
11
- create: () => ({}),
12
- meta: {
13
- docs: {
14
- url: `${repoUrl}/blob/v${packageJson.version}/docs/deprecated-rules.md#${ruleId}`,
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] || node).range;
50
- const [, end] = (parentheses.at(-1) || node).range;
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