eslint-plugin-ocavue 0.1.0 → 0.2.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/README.md CHANGED
@@ -21,6 +21,7 @@ export default [
21
21
  plugins: { ocavue },
22
22
  rules: {
23
23
  'ocavue/no-implicit-arrow-linebreak': 'error',
24
+ 'ocavue/prefer-while-true': 'error',
24
25
  },
25
26
  },
26
27
  ]
@@ -49,6 +50,24 @@ const items = Array.from({ length: count }, (_, index) => {
49
50
 
50
51
  When a comment sits between `=>` and the body, the rule reports without autofixing so the fix never drops a comment.
51
52
 
53
+ ### `prefer-while-true`
54
+
55
+ Enforce `while (true)` instead of `for (;;)` for infinite loops. Rules such as [`no-constant-condition`](https://eslint.org/docs/latest/rules/no-constant-condition) (with `checkLoops: "all"`) and [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition) report `while (true)` but not `for (;;)`, so people often rewrite the loop as `for (;;)` to silence them. That only hides the infinite loop from the linter: the code still loops forever. This rule removes that workaround. `while (true)` also reads more clearly than `for (;;)`.
56
+
57
+ ```js
58
+ // Incorrect
59
+ for (;;) {
60
+ step()
61
+ }
62
+
63
+ // Correct (what the fixer produces)
64
+ while (true) {
65
+ step()
66
+ }
67
+ ```
68
+
69
+ When a comment sits inside the loop head, the rule reports without autofixing so the fix never drops a comment.
70
+
52
71
  ## Sponsors
53
72
 
54
73
  <p align="center">
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ declare const plugin: {
6
6
  };
7
7
  rules: {
8
8
  'no-implicit-arrow-linebreak': import("eslint").Rule.RuleModule;
9
+ 'prefer-while-true': import("eslint").Rule.RuleModule;
9
10
  };
10
11
  };
11
12
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";cAMM"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";cAOM"}
package/dist/index.js CHANGED
@@ -3,41 +3,76 @@
3
3
  const plugin = {
4
4
  meta: {
5
5
  name: "eslint-plugin-ocavue",
6
- version: "0.1.0"
6
+ version: "0.2.0"
7
7
  },
8
- rules: { "no-implicit-arrow-linebreak": {
9
- meta: {
10
- type: "layout",
11
- fixable: "code",
12
- docs: {
13
- description: "disallow a linebreak between `=>` and an implicit arrow function body, fixing it to a block body with `return`",
14
- url: "https://github.com/ocavue/eslint-plugin-ocavue#no-implicit-arrow-linebreak"
8
+ rules: {
9
+ "no-implicit-arrow-linebreak": {
10
+ meta: {
11
+ type: "layout",
12
+ fixable: "code",
13
+ docs: {
14
+ description: "disallow a linebreak between `=>` and an implicit arrow function body, fixing it to a block body with `return`",
15
+ url: "https://github.com/ocavue/eslint-plugin-ocavue#no-implicit-arrow-linebreak"
16
+ },
17
+ messages: { linebreak: "Implicit arrow body must not start on a new line. Use a block body with `return` instead." },
18
+ schema: []
15
19
  },
16
- messages: { linebreak: "Implicit arrow body must not start on a new line. Use a block body with `return` instead." },
17
- schema: []
20
+ create(context) {
21
+ const sourceCode = context.sourceCode;
22
+ return { ArrowFunctionExpression(node) {
23
+ if (node.body.type === "BlockStatement") return;
24
+ const arrowToken = sourceCode.getTokenBefore(node.body, { filter: (token) => token.type === "Punctuator" && token.value === "=>" });
25
+ if (!arrowToken) return;
26
+ const firstBodyToken = sourceCode.getTokenAfter(arrowToken);
27
+ const lastToken = sourceCode.getLastToken(node);
28
+ if (!firstBodyToken || !lastToken) return;
29
+ if (arrowToken.loc.end.line === firstBodyToken.loc.start.line) return;
30
+ const canFix = !sourceCode.commentsExistBetween(arrowToken, firstBodyToken);
31
+ context.report({
32
+ node: node.body,
33
+ messageId: "linebreak",
34
+ fix: canFix ? (fixer) => {
35
+ const bodyText = sourceCode.text.slice(firstBodyToken.range[0], lastToken.range[1]);
36
+ return fixer.replaceTextRange([arrowToken.range[1], lastToken.range[1]], ` { return ${bodyText} }`);
37
+ } : void 0
38
+ });
39
+ } };
40
+ }
18
41
  },
19
- create(context) {
20
- const sourceCode = context.sourceCode;
21
- return { ArrowFunctionExpression(node) {
22
- if (node.body.type === "BlockStatement") return;
23
- const arrowToken = sourceCode.getTokenBefore(node.body, { filter: (token) => token.type === "Punctuator" && token.value === "=>" });
24
- if (!arrowToken) return;
25
- const firstBodyToken = sourceCode.getTokenAfter(arrowToken);
26
- const lastToken = sourceCode.getLastToken(node);
27
- if (!firstBodyToken || !lastToken) return;
28
- if (arrowToken.loc.end.line === firstBodyToken.loc.start.line) return;
29
- const canFix = !sourceCode.commentsExistBetween(arrowToken, firstBodyToken);
30
- context.report({
31
- node: node.body,
32
- messageId: "linebreak",
33
- fix: canFix ? (fixer) => {
34
- const bodyText = sourceCode.text.slice(firstBodyToken.range[0], lastToken.range[1]);
35
- return fixer.replaceTextRange([arrowToken.range[1], lastToken.range[1]], ` { return ${bodyText} }`);
36
- } : void 0
37
- });
38
- } };
42
+ "prefer-while-true": {
43
+ meta: {
44
+ type: "suggestion",
45
+ fixable: "code",
46
+ docs: {
47
+ description: "enforce `while (true)` instead of `for (;;)` for infinite loops",
48
+ url: "https://github.com/ocavue/eslint-plugin-ocavue#prefer-while-true"
49
+ },
50
+ messages: { preferWhileTrue: "Use `while (true)` instead of `for (;;)`." },
51
+ schema: []
52
+ },
53
+ create(context) {
54
+ const sourceCode = context.sourceCode;
55
+ return { ForStatement(node) {
56
+ if (node.init || node.test || node.update) return;
57
+ const forToken = sourceCode.getFirstToken(node);
58
+ const closingParen = sourceCode.getTokenBefore(node.body);
59
+ if (!forToken || !closingParen) return;
60
+ const canFix = !sourceCode.commentsExistBetween(forToken, closingParen);
61
+ context.report({
62
+ node,
63
+ loc: {
64
+ start: forToken.loc.start,
65
+ end: closingParen.loc.end
66
+ },
67
+ messageId: "preferWhileTrue",
68
+ fix: canFix ? (fixer) => {
69
+ return fixer.replaceTextRange([forToken.range[0], closingParen.range[1]], "while (true)");
70
+ } : void 0
71
+ });
72
+ } };
73
+ }
39
74
  }
40
- } }
75
+ }
41
76
  };
42
77
  //#endregion
43
78
  export { plugin as default };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["noImplicitArrowLinebreak"],"sources":["../package.json","../src/rules/no-implicit-arrow-linebreak.ts","../src/index.ts"],"sourcesContent":["","import type { Rule } from 'eslint'\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'layout',\n fixable: 'code',\n docs: {\n description:\n 'disallow a linebreak between `=>` and an implicit arrow function body, fixing it to a block body with `return`',\n url: 'https://github.com/ocavue/eslint-plugin-ocavue#no-implicit-arrow-linebreak',\n },\n messages: {\n linebreak:\n 'Implicit arrow body must not start on a new line. Use a block body with `return` instead.',\n },\n schema: [],\n },\n create(context) {\n const sourceCode = context.sourceCode\n return {\n ArrowFunctionExpression(node) {\n if (node.body.type === 'BlockStatement') {\n return\n }\n\n const arrowToken = sourceCode.getTokenBefore(node.body, {\n filter: (token) => token.type === 'Punctuator' && token.value === '=>',\n })\n if (!arrowToken) {\n return\n }\n const firstBodyToken = sourceCode.getTokenAfter(arrowToken)\n // The last token of the arrow function. When the body is wrapped in\n // parentheses, this is the closing `)`, so the fix keeps the parens.\n const lastToken = sourceCode.getLastToken(node)\n if (!firstBodyToken || !lastToken) {\n return\n }\n if (arrowToken.loc.end.line === firstBodyToken.loc.start.line) {\n return\n }\n\n // The fix rewrites the region between `=>` and the body, so bail out\n // of fixing (but still report) if that region contains comments.\n const canFix = !sourceCode.commentsExistBetween(arrowToken, firstBodyToken)\n\n context.report({\n node: node.body,\n messageId: 'linebreak',\n fix: canFix\n ? (fixer) => {\n const bodyText = sourceCode.text.slice(firstBodyToken.range[0], lastToken.range[1])\n return fixer.replaceTextRange(\n [arrowToken.range[1], lastToken.range[1]],\n ` { return ${bodyText} }`,\n )\n }\n : undefined,\n })\n },\n }\n },\n}\n\nexport default rule\n","import type { ESLint } from 'eslint'\n\nimport { version } from '../package.json'\n\nimport noImplicitArrowLinebreak from './rules/no-implicit-arrow-linebreak'\n\nconst plugin = {\n meta: {\n name: 'eslint-plugin-ocavue',\n version,\n },\n rules: {\n 'no-implicit-arrow-linebreak': noImplicitArrowLinebreak,\n },\n} satisfies ESLint.Plugin\n\nexport default plugin\n"],"mappings":";;AEMA,MAAM,SAAS;CACb,MAAM;EACJ,MAAM;EACN;CACF;CACA,OAAO,EACL,+BAA+BA;EDTjC,MAAM;GACJ,MAAM;GACN,SAAS;GACT,MAAM;IACJ,aACE;IACF,KAAK;GACP;GACA,UAAU,EACR,WACE,4FACJ;GACA,QAAQ,CAAC;EACX;EACA,OAAO,SAAS;GACd,MAAM,aAAa,QAAQ;GAC3B,OAAO,EACL,wBAAwB,MAAM;IAC5B,IAAI,KAAK,KAAK,SAAS,kBACrB;IAGF,MAAM,aAAa,WAAW,eAAe,KAAK,MAAM,EACtD,SAAS,UAAU,MAAM,SAAS,gBAAgB,MAAM,UAAU,KACpE,CAAC;IACD,IAAI,CAAC,YACH;IAEF,MAAM,iBAAiB,WAAW,cAAc,UAAU;IAG1D,MAAM,YAAY,WAAW,aAAa,IAAI;IAC9C,IAAI,CAAC,kBAAkB,CAAC,WACtB;IAEF,IAAI,WAAW,IAAI,IAAI,SAAS,eAAe,IAAI,MAAM,MACvD;IAKF,MAAM,SAAS,CAAC,WAAW,qBAAqB,YAAY,cAAc;IAE1E,QAAQ,OAAO;KACb,MAAM,KAAK;KACX,WAAW;KACX,KAAK,UACA,UAAU;MACT,MAAM,WAAW,WAAW,KAAK,MAAM,eAAe,MAAM,IAAI,UAAU,MAAM,EAAE;MAClF,OAAO,MAAM,iBACX,CAAC,WAAW,MAAM,IAAI,UAAU,MAAM,EAAE,GACxC,aAAa,SAAS,GACxB;KACF,IACA,KAAA;IACN,CAAC;GACH,EACF;EACF;CCjDiCA,EACjC;AACF"}
1
+ {"version":3,"file":"index.js","names":["rule","noImplicitArrowLinebreak","preferWhileTrue"],"sources":["../package.json","../src/rules/no-implicit-arrow-linebreak.ts","../src/rules/prefer-while-true.ts","../src/index.ts"],"sourcesContent":["","import type { Rule } from 'eslint'\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'layout',\n fixable: 'code',\n docs: {\n description:\n 'disallow a linebreak between `=>` and an implicit arrow function body, fixing it to a block body with `return`',\n url: 'https://github.com/ocavue/eslint-plugin-ocavue#no-implicit-arrow-linebreak',\n },\n messages: {\n linebreak:\n 'Implicit arrow body must not start on a new line. Use a block body with `return` instead.',\n },\n schema: [],\n },\n create(context) {\n const sourceCode = context.sourceCode\n return {\n ArrowFunctionExpression(node) {\n if (node.body.type === 'BlockStatement') {\n return\n }\n\n const arrowToken = sourceCode.getTokenBefore(node.body, {\n filter: (token) => token.type === 'Punctuator' && token.value === '=>',\n })\n if (!arrowToken) {\n return\n }\n const firstBodyToken = sourceCode.getTokenAfter(arrowToken)\n // The last token of the arrow function. When the body is wrapped in\n // parentheses, this is the closing `)`, so the fix keeps the parens.\n const lastToken = sourceCode.getLastToken(node)\n if (!firstBodyToken || !lastToken) {\n return\n }\n if (arrowToken.loc.end.line === firstBodyToken.loc.start.line) {\n return\n }\n\n // The fix rewrites the region between `=>` and the body, so bail out\n // of fixing (but still report) if that region contains comments.\n const canFix = !sourceCode.commentsExistBetween(arrowToken, firstBodyToken)\n\n context.report({\n node: node.body,\n messageId: 'linebreak',\n fix: canFix\n ? (fixer) => {\n const bodyText = sourceCode.text.slice(firstBodyToken.range[0], lastToken.range[1])\n return fixer.replaceTextRange(\n [arrowToken.range[1], lastToken.range[1]],\n ` { return ${bodyText} }`,\n )\n }\n : undefined,\n })\n },\n }\n },\n}\n\nexport default rule\n","import type { Rule } from 'eslint'\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n fixable: 'code',\n docs: {\n description: 'enforce `while (true)` instead of `for (;;)` for infinite loops',\n url: 'https://github.com/ocavue/eslint-plugin-ocavue#prefer-while-true',\n },\n messages: {\n preferWhileTrue: 'Use `while (true)` instead of `for (;;)`.',\n },\n schema: [],\n },\n create(context) {\n const sourceCode = context.sourceCode\n return {\n ForStatement(node) {\n if (node.init || node.test || node.update) {\n return\n }\n\n const forToken = sourceCode.getFirstToken(node)\n const closingParen = sourceCode.getTokenBefore(node.body)\n if (!forToken || !closingParen) {\n return\n }\n\n // Bail out of fixing (but still report) if the loop head contains\n // comments, so the fix never drops a comment.\n const canFix = !sourceCode.commentsExistBetween(forToken, closingParen)\n\n context.report({\n node,\n loc: { start: forToken.loc.start, end: closingParen.loc.end },\n messageId: 'preferWhileTrue',\n fix: canFix\n ? (fixer) => {\n return fixer.replaceTextRange(\n [forToken.range[0], closingParen.range[1]],\n 'while (true)',\n )\n }\n : undefined,\n })\n },\n }\n },\n}\n\nexport default rule\n","import type { ESLint } from 'eslint'\n\nimport { version } from '../package.json'\n\nimport noImplicitArrowLinebreak from './rules/no-implicit-arrow-linebreak'\nimport preferWhileTrue from './rules/prefer-while-true'\n\nconst plugin = {\n meta: {\n name: 'eslint-plugin-ocavue',\n version,\n },\n rules: {\n 'no-implicit-arrow-linebreak': noImplicitArrowLinebreak,\n 'prefer-while-true': preferWhileTrue,\n },\n} satisfies ESLint.Plugin\n\nexport default plugin\n"],"mappings":";;AGOA,MAAM,SAAS;CACb,MAAM;EACJ,MAAM;EACN;CACF;CACA,OAAO;EACL,+BAA+BC;GFVjC,MAAM;IACJ,MAAM;IACN,SAAS;IACT,MAAM;KACJ,aACE;KACF,KAAK;IACP;IACA,UAAU,EACR,WACE,4FACJ;IACA,QAAQ,CAAC;GACX;GACA,OAAO,SAAS;IACd,MAAM,aAAa,QAAQ;IAC3B,OAAO,EACL,wBAAwB,MAAM;KAC5B,IAAI,KAAK,KAAK,SAAS,kBACrB;KAGF,MAAM,aAAa,WAAW,eAAe,KAAK,MAAM,EACtD,SAAS,UAAU,MAAM,SAAS,gBAAgB,MAAM,UAAU,KACpE,CAAC;KACD,IAAI,CAAC,YACH;KAEF,MAAM,iBAAiB,WAAW,cAAc,UAAU;KAG1D,MAAM,YAAY,WAAW,aAAa,IAAI;KAC9C,IAAI,CAAC,kBAAkB,CAAC,WACtB;KAEF,IAAI,WAAW,IAAI,IAAI,SAAS,eAAe,IAAI,MAAM,MACvD;KAKF,MAAM,SAAS,CAAC,WAAW,qBAAqB,YAAY,cAAc;KAE1E,QAAQ,OAAO;MACb,MAAM,KAAK;MACX,WAAW;MACX,KAAK,UACA,UAAU;OACT,MAAM,WAAW,WAAW,KAAK,MAAM,eAAe,MAAM,IAAI,UAAU,MAAM,EAAE;OAClF,OAAO,MAAM,iBACX,CAAC,WAAW,MAAM,IAAI,UAAU,MAAM,EAAE,GACxC,aAAa,SAAS,GACxB;MACF,IACA,KAAA;KACN,CAAC;IACH,EACF;GACF;EEhDiCA;EAC/B,qBAAqBC;GDXvB,MAAM;IACJ,MAAM;IACN,SAAS;IACT,MAAM;KACJ,aAAa;KACb,KAAK;IACP;IACA,UAAU,EACR,iBAAiB,4CACnB;IACA,QAAQ,CAAC;GACX;GACA,OAAO,SAAS;IACd,MAAM,aAAa,QAAQ;IAC3B,OAAO,EACL,aAAa,MAAM;KACjB,IAAI,KAAK,QAAQ,KAAK,QAAQ,KAAK,QACjC;KAGF,MAAM,WAAW,WAAW,cAAc,IAAI;KAC9C,MAAM,eAAe,WAAW,eAAe,KAAK,IAAI;KACxD,IAAI,CAAC,YAAY,CAAC,cAChB;KAKF,MAAM,SAAS,CAAC,WAAW,qBAAqB,UAAU,YAAY;KAEtE,QAAQ,OAAO;MACb;MACA,KAAK;OAAE,OAAO,SAAS,IAAI;OAAO,KAAK,aAAa,IAAI;MAAI;MAC5D,WAAW;MACX,KAAK,UACA,UAAU;OACT,OAAO,MAAM,iBACX,CAAC,SAAS,MAAM,IAAI,aAAa,MAAM,EAAE,GACzC,cACF;MACF,IACA,KAAA;KACN,CAAC;IACH,EACF;GACF;EClCuBA;CACvB;AACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-ocavue",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "description": "Custom ESLint rules from ocavue",
6
6
  "author": "ocavue <ocavue@gmail.com>",
7
7
  "license": "MIT",
@@ -35,15 +35,15 @@
35
35
  "eslint": "^10.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@ocavue/eslint-config": "^4.13.5",
38
+ "@ocavue/eslint-config": "^4.22.0",
39
39
  "@ocavue/tsconfig": "^0.7.1",
40
40
  "@types/node": "^24.0.0",
41
- "eslint": "^10.9.1",
42
- "knip": "^6.33.0",
43
- "oxfmt": "^0.65.0",
44
- "tsdown": "^0.22.14",
41
+ "eslint": "^10.14.0",
42
+ "knip": "^6.41.0",
43
+ "oxfmt": "^0.73.0",
44
+ "tsdown": "^0.23.1",
45
45
  "typescript": "^6.0.3",
46
- "vitest": "^4.1.11"
46
+ "vitest": "^5.0.5"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsdown",
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import type { ESLint } from 'eslint'
3
3
  import { version } from '../package.json'
4
4
 
5
5
  import noImplicitArrowLinebreak from './rules/no-implicit-arrow-linebreak'
6
+ import preferWhileTrue from './rules/prefer-while-true'
6
7
 
7
8
  const plugin = {
8
9
  meta: {
@@ -11,6 +12,7 @@ const plugin = {
11
12
  },
12
13
  rules: {
13
14
  'no-implicit-arrow-linebreak': noImplicitArrowLinebreak,
15
+ 'prefer-while-true': preferWhileTrue,
14
16
  },
15
17
  } satisfies ESLint.Plugin
16
18
 
@@ -0,0 +1,43 @@
1
+ import { RuleTester } from 'eslint'
2
+ import { test } from 'vitest'
3
+
4
+ import rule from './prefer-while-true'
5
+
6
+ test('prefer-while-true', () => {
7
+ const tester = new RuleTester({
8
+ languageOptions: { ecmaVersion: 'latest', sourceType: 'module' },
9
+ })
10
+
11
+ tester.run('prefer-while-true', rule, {
12
+ valid: [
13
+ 'while (true) {}',
14
+ 'for (let i = 0; i < 10; i++) {}',
15
+ 'for (; i < 10; ) {}',
16
+ 'for (let i = 0; ; i++) {}',
17
+ 'for (const x of xs) {}',
18
+ ],
19
+ invalid: [
20
+ {
21
+ code: 'for (;;) {\n fn()\n}',
22
+ output: 'while (true) {\n fn()\n}',
23
+ errors: [{ messageId: 'preferWhileTrue' }],
24
+ },
25
+ {
26
+ code: 'for ( ; ; ) fn()',
27
+ output: 'while (true) fn()',
28
+ errors: [{ messageId: 'preferWhileTrue' }],
29
+ },
30
+ {
31
+ code: 'outer: for (;;) {\n for (;;) break outer\n}',
32
+ output: 'outer: while (true) {\n while (true) break outer\n}',
33
+ errors: [{ messageId: 'preferWhileTrue' }, { messageId: 'preferWhileTrue' }],
34
+ },
35
+ {
36
+ // No autofix when a comment sits inside the loop head.
37
+ code: 'for (; /* forever */ ;) {}',
38
+ output: null,
39
+ errors: [{ messageId: 'preferWhileTrue' }],
40
+ },
41
+ ],
42
+ })
43
+ })
@@ -0,0 +1,52 @@
1
+ import type { Rule } from 'eslint'
2
+
3
+ const rule: Rule.RuleModule = {
4
+ meta: {
5
+ type: 'suggestion',
6
+ fixable: 'code',
7
+ docs: {
8
+ description: 'enforce `while (true)` instead of `for (;;)` for infinite loops',
9
+ url: 'https://github.com/ocavue/eslint-plugin-ocavue#prefer-while-true',
10
+ },
11
+ messages: {
12
+ preferWhileTrue: 'Use `while (true)` instead of `for (;;)`.',
13
+ },
14
+ schema: [],
15
+ },
16
+ create(context) {
17
+ const sourceCode = context.sourceCode
18
+ return {
19
+ ForStatement(node) {
20
+ if (node.init || node.test || node.update) {
21
+ return
22
+ }
23
+
24
+ const forToken = sourceCode.getFirstToken(node)
25
+ const closingParen = sourceCode.getTokenBefore(node.body)
26
+ if (!forToken || !closingParen) {
27
+ return
28
+ }
29
+
30
+ // Bail out of fixing (but still report) if the loop head contains
31
+ // comments, so the fix never drops a comment.
32
+ const canFix = !sourceCode.commentsExistBetween(forToken, closingParen)
33
+
34
+ context.report({
35
+ node,
36
+ loc: { start: forToken.loc.start, end: closingParen.loc.end },
37
+ messageId: 'preferWhileTrue',
38
+ fix: canFix
39
+ ? (fixer) => {
40
+ return fixer.replaceTextRange(
41
+ [forToken.range[0], closingParen.range[1]],
42
+ 'while (true)',
43
+ )
44
+ }
45
+ : undefined,
46
+ })
47
+ },
48
+ }
49
+ },
50
+ }
51
+
52
+ export default rule