eslint-interactive 8.1.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.
Files changed (214) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +147 -0
  3. package/bin/eslint-interactive.js +10 -0
  4. package/dist/action/apply-suggestions.d.ts +6 -0
  5. package/dist/action/apply-suggestions.d.ts.map +1 -0
  6. package/dist/action/apply-suggestions.js +28 -0
  7. package/dist/action/apply-suggestions.js.map +1 -0
  8. package/dist/action/disable-per-file.d.ts +6 -0
  9. package/dist/action/disable-per-file.d.ts.map +1 -0
  10. package/dist/action/disable-per-file.js +8 -0
  11. package/dist/action/disable-per-file.js.map +1 -0
  12. package/dist/action/disable-per-line.d.ts +6 -0
  13. package/dist/action/disable-per-line.d.ts.map +1 -0
  14. package/dist/action/disable-per-line.js +8 -0
  15. package/dist/action/disable-per-line.js.map +1 -0
  16. package/dist/action/fix.d.ts +6 -0
  17. package/dist/action/fix.d.ts.map +1 -0
  18. package/dist/action/fix.js +6 -0
  19. package/dist/action/fix.js.map +1 -0
  20. package/dist/action/index.d.ts +7 -0
  21. package/dist/action/index.d.ts.map +1 -0
  22. package/dist/action/index.js +7 -0
  23. package/dist/action/index.js.map +1 -0
  24. package/dist/action/make-fixable-and-fix.d.ts +6 -0
  25. package/dist/action/make-fixable-and-fix.d.ts.map +1 -0
  26. package/dist/action/make-fixable-and-fix.js +28 -0
  27. package/dist/action/make-fixable-and-fix.js.map +1 -0
  28. package/dist/action/print-result-details.d.ts +5 -0
  29. package/dist/action/print-result-details.d.ts.map +1 -0
  30. package/dist/action/print-result-details.js +12 -0
  31. package/dist/action/print-result-details.js.map +1 -0
  32. package/dist/cli/log.d.ts +6 -0
  33. package/dist/cli/log.d.ts.map +1 -0
  34. package/dist/cli/log.js +9 -0
  35. package/dist/cli/log.js.map +1 -0
  36. package/dist/cli/ora.d.ts +4 -0
  37. package/dist/cli/ora.d.ts.map +1 -0
  38. package/dist/cli/ora.js +23 -0
  39. package/dist/cli/ora.js.map +1 -0
  40. package/dist/cli/package.d.ts +2 -0
  41. package/dist/cli/package.d.ts.map +1 -0
  42. package/dist/cli/package.js +6 -0
  43. package/dist/cli/package.js.map +1 -0
  44. package/dist/cli/parse-argv.d.ts +4 -0
  45. package/dist/cli/parse-argv.d.ts.map +1 -0
  46. package/dist/cli/parse-argv.js +50 -0
  47. package/dist/cli/parse-argv.js.map +1 -0
  48. package/dist/cli/prompt.d.ts +53 -0
  49. package/dist/cli/prompt.d.ts.map +1 -0
  50. package/dist/cli/prompt.js +154 -0
  51. package/dist/cli/prompt.js.map +1 -0
  52. package/dist/cli/run.d.ts +8 -0
  53. package/dist/cli/run.d.ts.map +1 -0
  54. package/dist/cli/run.js +46 -0
  55. package/dist/cli/run.js.map +1 -0
  56. package/dist/core-worker.d.ts +21 -0
  57. package/dist/core-worker.d.ts.map +1 -0
  58. package/dist/core-worker.js +52 -0
  59. package/dist/core-worker.js.map +1 -0
  60. package/dist/core.d.ts +84 -0
  61. package/dist/core.d.ts.map +1 -0
  62. package/dist/core.js +196 -0
  63. package/dist/core.js.map +1 -0
  64. package/dist/formatter/colors.d.ts +4 -0
  65. package/dist/formatter/colors.d.ts.map +1 -0
  66. package/dist/formatter/colors.js +5 -0
  67. package/dist/formatter/colors.js.map +1 -0
  68. package/dist/formatter/format-by-files.d.ts +3 -0
  69. package/dist/formatter/format-by-files.d.ts.map +1 -0
  70. package/dist/formatter/format-by-files.js +41 -0
  71. package/dist/formatter/format-by-files.js.map +1 -0
  72. package/dist/formatter/format-by-rules.d.ts +3 -0
  73. package/dist/formatter/format-by-rules.d.ts.map +1 -0
  74. package/dist/formatter/format-by-rules.js +34 -0
  75. package/dist/formatter/format-by-rules.js.map +1 -0
  76. package/dist/formatter/index.d.ts +4 -0
  77. package/dist/formatter/index.d.ts.map +1 -0
  78. package/dist/formatter/index.js +7 -0
  79. package/dist/formatter/index.js.map +1 -0
  80. package/dist/formatter/take-rule-statistics.d.ts +18 -0
  81. package/dist/formatter/take-rule-statistics.d.ts.map +1 -0
  82. package/dist/formatter/take-rule-statistics.js +51 -0
  83. package/dist/formatter/take-rule-statistics.js.map +1 -0
  84. package/dist/index.d.ts +5 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +4 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/plugin/fix/apply-auto-fixes.d.ts +8 -0
  89. package/dist/plugin/fix/apply-auto-fixes.d.ts.map +1 -0
  90. package/dist/plugin/fix/apply-auto-fixes.js +8 -0
  91. package/dist/plugin/fix/apply-auto-fixes.js.map +1 -0
  92. package/dist/plugin/fix/apply-suggestions.d.ts +11 -0
  93. package/dist/plugin/fix/apply-suggestions.d.ts.map +1 -0
  94. package/dist/plugin/fix/apply-suggestions.js +25 -0
  95. package/dist/plugin/fix/apply-suggestions.js.map +1 -0
  96. package/dist/plugin/fix/disable-per-file.d.ts +10 -0
  97. package/dist/plugin/fix/disable-per-file.d.ts.map +1 -0
  98. package/dist/plugin/fix/disable-per-file.js +39 -0
  99. package/dist/plugin/fix/disable-per-file.js.map +1 -0
  100. package/dist/plugin/fix/disable-per-line.d.ts +10 -0
  101. package/dist/plugin/fix/disable-per-line.d.ts.map +1 -0
  102. package/dist/plugin/fix/disable-per-line.js +54 -0
  103. package/dist/plugin/fix/disable-per-line.js.map +1 -0
  104. package/dist/plugin/fix/index.d.ts +6 -0
  105. package/dist/plugin/fix/index.d.ts.map +1 -0
  106. package/dist/plugin/fix/index.js +6 -0
  107. package/dist/plugin/fix/index.js.map +1 -0
  108. package/dist/plugin/fix/make-fixable-and-fix.d.ts +12 -0
  109. package/dist/plugin/fix/make-fixable-and-fix.d.ts.map +1 -0
  110. package/dist/plugin/fix/make-fixable-and-fix.js +61 -0
  111. package/dist/plugin/fix/make-fixable-and-fix.js.map +1 -0
  112. package/dist/plugin/fix-rule.d.ts +10 -0
  113. package/dist/plugin/fix-rule.d.ts.map +1 -0
  114. package/dist/plugin/fix-rule.js +124 -0
  115. package/dist/plugin/fix-rule.js.map +1 -0
  116. package/dist/plugin/index.d.ts +49 -0
  117. package/dist/plugin/index.d.ts.map +1 -0
  118. package/dist/plugin/index.js +11 -0
  119. package/dist/plugin/index.js.map +1 -0
  120. package/dist/plugin/prefer-addition-shorthand-rule.d.ts +7 -0
  121. package/dist/plugin/prefer-addition-shorthand-rule.d.ts.map +1 -0
  122. package/dist/plugin/prefer-addition-shorthand-rule.js +54 -0
  123. package/dist/plugin/prefer-addition-shorthand-rule.js.map +1 -0
  124. package/dist/plugin/rule-fixer.d.ts +80 -0
  125. package/dist/plugin/rule-fixer.d.ts.map +1 -0
  126. package/dist/plugin/rule-fixer.js +118 -0
  127. package/dist/plugin/rule-fixer.js.map +1 -0
  128. package/dist/scene/check-results.d.ts +21 -0
  129. package/dist/scene/check-results.d.ts.map +1 -0
  130. package/dist/scene/check-results.js +22 -0
  131. package/dist/scene/check-results.js.map +1 -0
  132. package/dist/scene/index.d.ts +25 -0
  133. package/dist/scene/index.d.ts.map +1 -0
  134. package/dist/scene/index.js +6 -0
  135. package/dist/scene/index.js.map +1 -0
  136. package/dist/scene/lint.d.ts +8 -0
  137. package/dist/scene/lint.d.ts.map +1 -0
  138. package/dist/scene/lint.js +31 -0
  139. package/dist/scene/lint.js.map +1 -0
  140. package/dist/scene/select-action.d.ts +20 -0
  141. package/dist/scene/select-action.d.ts.map +1 -0
  142. package/dist/scene/select-action.js +46 -0
  143. package/dist/scene/select-action.js.map +1 -0
  144. package/dist/scene/select-rule-ids.d.ts +15 -0
  145. package/dist/scene/select-rule-ids.d.ts.map +1 -0
  146. package/dist/scene/select-rule-ids.js +10 -0
  147. package/dist/scene/select-rule-ids.js.map +1 -0
  148. package/dist/tsconfig.src.tsbuildinfo +1 -0
  149. package/dist/util/array.d.ts +3 -0
  150. package/dist/util/array.d.ts.map +1 -0
  151. package/dist/util/array.js +14 -0
  152. package/dist/util/array.js.map +1 -0
  153. package/dist/util/cache.d.ts +5 -0
  154. package/dist/util/cache.d.ts.map +1 -0
  155. package/dist/util/cache.js +13 -0
  156. package/dist/util/cache.js.map +1 -0
  157. package/dist/util/eslint.d.ts +68 -0
  158. package/dist/util/eslint.d.ts.map +1 -0
  159. package/dist/util/eslint.js +147 -0
  160. package/dist/util/eslint.js.map +1 -0
  161. package/dist/util/filter-script.d.ts +6 -0
  162. package/dist/util/filter-script.d.ts.map +1 -0
  163. package/dist/util/filter-script.js +39 -0
  164. package/dist/util/filter-script.js.map +1 -0
  165. package/dist/util/type-check.d.ts +3 -0
  166. package/dist/util/type-check.d.ts.map +1 -0
  167. package/dist/util/type-check.js +8 -0
  168. package/dist/util/type-check.js.map +1 -0
  169. package/package.json +93 -0
  170. package/src/action/apply-suggestions.ts +40 -0
  171. package/src/action/disable-per-file.ts +16 -0
  172. package/src/action/disable-per-line.ts +16 -0
  173. package/src/action/fix.ts +14 -0
  174. package/src/action/index.ts +6 -0
  175. package/src/action/make-fixable-and-fix.ts +40 -0
  176. package/src/action/print-result-details.ts +18 -0
  177. package/src/cli/log.ts +11 -0
  178. package/src/cli/ora.ts +25 -0
  179. package/src/cli/package.ts +9 -0
  180. package/src/cli/parse-argv.ts +52 -0
  181. package/src/cli/prompt.ts +205 -0
  182. package/src/cli/run.ts +50 -0
  183. package/src/core-worker.ts +66 -0
  184. package/src/core.ts +240 -0
  185. package/src/formatter/colors.ts +5 -0
  186. package/src/formatter/format-by-files.ts +48 -0
  187. package/src/formatter/format-by-rules.ts +37 -0
  188. package/src/formatter/index.ts +9 -0
  189. package/src/formatter/take-rule-statistics.ts +66 -0
  190. package/src/index.ts +4 -0
  191. package/src/plugin/fix/apply-auto-fixes.ts +13 -0
  192. package/src/plugin/fix/apply-suggestions.ts +44 -0
  193. package/src/plugin/fix/disable-per-file.ts +53 -0
  194. package/src/plugin/fix/disable-per-line.ts +65 -0
  195. package/src/plugin/fix/index.ts +13 -0
  196. package/src/plugin/fix/make-fixable-and-fix.ts +77 -0
  197. package/src/plugin/fix-rule.ts +142 -0
  198. package/src/plugin/index.ts +66 -0
  199. package/src/plugin/prefer-addition-shorthand-rule.ts +56 -0
  200. package/src/plugin/rule-fixer.ts +147 -0
  201. package/src/scene/check-results.ts +43 -0
  202. package/src/scene/index.ts +18 -0
  203. package/src/scene/lint.ts +41 -0
  204. package/src/scene/select-action.ts +70 -0
  205. package/src/scene/select-rule-ids.ts +24 -0
  206. package/src/typings/cachedir.d.ts +5 -0
  207. package/src/typings/node-pager.d.ts +4 -0
  208. package/src/util/array.ts +16 -0
  209. package/src/util/cache.ts +11 -0
  210. package/src/util/eslint.ts +162 -0
  211. package/src/util/filter-script.ts +45 -0
  212. package/src/util/type-check.ts +8 -0
  213. package/static/example-filter-script.js +49 -0
  214. package/static/example-fixable-maker-script.js +47 -0
@@ -0,0 +1,162 @@
1
+ import { AST, ESLint } from 'eslint';
2
+ import type { Comment } from 'estree';
3
+ import { unique } from './array.js';
4
+ import { notEmpty } from './type-check.js';
5
+
6
+ const COMMENT_RE =
7
+ /^\s*(?<header>eslint-disable|eslint-disable-next-line)\s+(?<ruleList>[@a-z0-9\-_$/]*(?:\s*,\s*[@a-z0-9\-_$/]*)*(?:\s*,)?)(?:\s+--\s+(?<description>.*\S))?\s*$/u;
8
+
9
+ const SHEBANG_PATTERN = /^#!.+?\r?\n/u;
10
+
11
+ /** `results` 内で使われているプラグインの名前のリストを洗い出して返す */
12
+ export function scanUsedPluginsFromResults(results: ESLint.LintResult[]): string[] {
13
+ const plugins = results
14
+ .flatMap((result) => result.messages) // messages: Linter.LintMessage[]
15
+ .map((message) => message.ruleId) // ruleIds: (string | undefined)[]
16
+ .filter(notEmpty) // ruleIds: string[]
17
+ .map((ruleId) => {
18
+ const parts = ruleId.split('/');
19
+ if (parts.length === 1) return undefined; // ex: 'rule-a'
20
+ if (parts.length === 2) return parts[0]; // ex: 'plugin/rule-a'
21
+ if (parts.length === 3) return `${parts[0]}/${parts[1]}`; // ex: '@scoped/plugin/rule-a'
22
+ return undefined; // invalid ruleId
23
+ }) // plugins: string[]
24
+ .filter(notEmpty);
25
+ return unique(plugins);
26
+ }
27
+
28
+ export type DisableComment = {
29
+ type: 'Block' | 'Line';
30
+ scope: 'next-line' | 'file';
31
+ ruleIds: string[];
32
+ description?: string;
33
+ range: [number, number];
34
+ };
35
+
36
+ /**
37
+ * コメントを ESLint の disable comment としてパースする。
38
+ * disable comment としてパースできなかった場合は undefined を返す。
39
+ *
40
+ * ## 参考: disable comment の構造
41
+ * /* eslint-disable-next-line rule-a, rule-b, rule-c, rule-d -- I'm the rules.
42
+ * ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^
43
+ * | | | |
44
+ * header | | |
45
+ * ruleList | |
46
+ * descriptionHeader |
47
+ * description
48
+ */
49
+ export function parseDisableComment(comment: Comment): DisableComment | undefined {
50
+ // NOTE: コメントノードには必ず range があるはずだが、型上は optional なので、
51
+ // range がない場合はパースに失敗した扱いにする。
52
+ if (!comment.range) return undefined;
53
+
54
+ const result = COMMENT_RE.exec(comment.value);
55
+ if (!result) return undefined;
56
+ if (!result.groups) return undefined;
57
+
58
+ const { header, ruleList, description } = result.groups;
59
+ const ruleIds = ruleList
60
+ .split(',')
61
+ .map((r) => r.trim())
62
+ // 空文字は除外しておく
63
+ .filter((ruleId) => ruleId !== '');
64
+
65
+ const scope = header === 'eslint-disable-next-line' ? 'next-line' : 'file';
66
+ // file scope comment must be block-style.
67
+ if (scope === 'file' && comment.type === 'Line') return undefined;
68
+
69
+ return {
70
+ type: comment.type,
71
+ scope: header === 'eslint-disable-next-line' ? 'next-line' : 'file',
72
+ ruleIds: ruleIds,
73
+ // description is optional
74
+ ...(description === '' || description === undefined ? {} : { description }),
75
+ range: comment.range,
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Convert `DisableComment` to comment text.
81
+ */
82
+ export function toCommentText({ type, scope, ruleIds, description }: Omit<DisableComment, 'range'>): string {
83
+ const header = scope === 'next-line' ? 'eslint-disable-next-line' : 'eslint-disable';
84
+ const ruleList = unique(ruleIds).join(', ');
85
+ if (type === 'Line') {
86
+ if (description === undefined) {
87
+ return `// ${header} ${ruleList}`;
88
+ } else {
89
+ return `// ${header} ${ruleList} -- ${description}`;
90
+ }
91
+ } else {
92
+ if (description === undefined) {
93
+ return `/* ${header} ${ruleList} */`;
94
+ } else {
95
+ return `/* ${header} ${ruleList} -- ${description} */`;
96
+ }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Create the results with only messages with the specified rule ids.
102
+ * @param results The lint results.
103
+ * @param ruleIds The rule ids.
104
+ * @returns The results with only messages with the specified rule ids
105
+ */
106
+ export function filterResultsByRuleId(results: ESLint.LintResult[], ruleIds: (string | null)[]): ESLint.LintResult[] {
107
+ return results
108
+ .map((result) => {
109
+ return {
110
+ ...result,
111
+ messages: result.messages.filter((message) => ruleIds.includes(message.ruleId)),
112
+ };
113
+ })
114
+ .filter((result) => result.messages.length > 0);
115
+ }
116
+
117
+ /**
118
+ * push rule ids to the disable comment and return the new comment node.
119
+ * @param comment The comment node to be modified
120
+ * @param ruleIds The rule ids to be added
121
+ * @returns The new comment node
122
+ */
123
+ export function pushRuleIdsToDisableComment(comment: DisableComment, ruleIds: string[]): DisableComment {
124
+ return {
125
+ ...comment,
126
+ ruleIds: unique([...comment.ruleIds, ...ruleIds]),
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Merge the ruleIds and description of the disable comments.
132
+ * @param a The ruleIds and description of first disable comment
133
+ * @param b The ruleIds and description of second disable comment
134
+ * @returns The ruleIds and description of merged disable comment
135
+ */
136
+ export function mergeRuleIdsAndDescription(
137
+ a: { ruleIds: string[]; description?: string },
138
+ b: { ruleIds: string[]; description?: string },
139
+ ): { ruleIds: string[]; description?: string } {
140
+ const ruleIds = unique([...a.ruleIds, ...b.ruleIds]);
141
+ const description =
142
+ a.description !== undefined && b.description !== undefined
143
+ ? `${a.description}, ${b.description}`
144
+ : a.description !== undefined && b.description === undefined
145
+ ? a.description
146
+ : a.description === undefined && b.description !== undefined
147
+ ? b.description
148
+ : undefined;
149
+ if (description === undefined) return { ruleIds };
150
+ return { ruleIds, description };
151
+ }
152
+
153
+ /**
154
+ * Find shebang from the first line of the file.
155
+ * @param sourceCodeText The source code text of the file.
156
+ * @returns The information of shebang. If the file does not have shebang, return null.
157
+ */
158
+ export function findShebang(sourceCodeText: string): { range: AST.Range } | null {
159
+ const result = SHEBANG_PATTERN.exec(sourceCodeText);
160
+ if (!result) return null;
161
+ return { range: [0, result[0].length] };
162
+ }
@@ -0,0 +1,45 @@
1
+ import { exec as execOriginal } from 'child_process';
2
+ import { mkdir, appendFile, readFile, access } from 'fs/promises';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { promisify } from 'util';
6
+ import { getCacheDir } from './cache.js';
7
+
8
+ const exec = promisify(execOriginal);
9
+
10
+ const DEFAULT_EDITOR_COMMAND = 'vi';
11
+
12
+ function getEditorCommand(): string {
13
+ return process.env.EDITOR ?? DEFAULT_EDITOR_COMMAND;
14
+ }
15
+
16
+ export async function editFileWithEditor(filepath: string): Promise<string> {
17
+ const command = getEditorCommand();
18
+ await mkdir(dirname(filepath), { recursive: true });
19
+ await access(filepath).catch(async () => {
20
+ await appendFile(filepath, '', 'utf8');
21
+ });
22
+ await exec(`${command} ${filepath}`);
23
+ const newContent = await readFile(filepath, 'utf8');
24
+ return newContent;
25
+ }
26
+
27
+ export function generateFilterScriptFilePath(ruleIds: string[]): string {
28
+ const basename = ruleIds.join('_').replace(/[^\w-]/g, '') + '.js';
29
+ const filepath = join(getCacheDir(), 'filter-script', basename);
30
+ return filepath;
31
+ }
32
+
33
+ export function generateFixableMakerScriptFilePath(ruleIds: string[]): string {
34
+ const basename = ruleIds.join('_').replace(/[^\w-]/g, '') + '.js';
35
+ const filepath = join(getCacheDir(), 'fixable-maker-script', basename);
36
+ return filepath;
37
+ }
38
+
39
+ export function generateExampleFilterScriptFilePath(): string {
40
+ return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'static', 'example-filter-script.js');
41
+ }
42
+
43
+ export function generateExampleFixableMakerScriptFilePath(): string {
44
+ return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'static', 'example-fixable-maker-script.js');
45
+ }
@@ -0,0 +1,8 @@
1
+ /* istanbul ignore next */
2
+ export function unreachable(message?: string): never {
3
+ throw new Error(message ?? 'unreachable code');
4
+ }
5
+
6
+ export function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
7
+ return value !== null && value !== undefined;
8
+ }
@@ -0,0 +1,49 @@
1
+ // @ts-check
2
+
3
+ // Edit this file to customize how the suggestion is applied.
4
+ // Save and close this file to apply the suggestion.
5
+ //
6
+ // NOTE(For VSCode user):
7
+ // In the Restricted Mode of VSCode, the close event of the file is not notified to eslint-interactive.
8
+ // Therefore, eslint-interactive will not start applying suggestions even if the file is closed, and eslint-interactive will freeze.
9
+ // If you open a file in VSCode Restricted Mode, please follow the steps below:
10
+ // 1. Trust the workspace
11
+ // 2. Close the open file
12
+ // 3. exit the frozen eslint-interactive with Ctrl+C
13
+ // 4. Restart eslint-interactive and try to apply the suggestion again
14
+
15
+ /**
16
+ * A function that takes a list of suggestions that can be applied to a problem as arguments,
17
+ * and selects the suggestion to be applied from the list and returns it.
18
+ * @param {import('eslint').Linter.LintSuggestion[]} suggestions - The list of suggestions that can be applied to the problem
19
+ * @param {import('eslint').Linter.LintMessage} message - The `message` that contained a `suggestion`
20
+ * @param {import('eslint-interactive').FixContext} context - The context of the fix.
21
+ * @returns {import('eslint').Linter.LintSuggestion | null | undefined} Suggestion to apply. If null or undefined is returned, do not apply any suggestion.
22
+ */
23
+ function filterSuggestions(suggestions, message, context) {
24
+ // example:
25
+
26
+ console.log(context.filename);
27
+
28
+ if (message.ruleId === 'no-unsafe-negation') {
29
+ return suggestions.find((suggestion) => suggestion.desc.startsWith('Wrap negation'));
30
+ } else if (message.ruleId === 'no-useless-escape') {
31
+ if (message.severity === 2) {
32
+ // error
33
+ // ref: https://github.com/eslint/eslint/blob/99b1fca0e61902f0d69aea4b4cdbf75d37ea20c4/lib/rules/no-useless-escape.js#L125
34
+ return suggestions.find((suggestion) => suggestion.messageId === 'removeEscape');
35
+ } else {
36
+ // warning
37
+ return suggestions.find((suggestion) => suggestion.messageId === 'escapeBackslash');
38
+ }
39
+ } else {
40
+ // apply first suggestion
41
+ // NOTE: `suggestion.length` must be greater than 0
42
+ return suggestions[0];
43
+ }
44
+ }
45
+
46
+ // Here, `filterSuggestions` is passed to eslint-interactive pass.
47
+ // NOTE: The value evaluated on the last line of the file will be passed to eslint-interactive.
48
+ // This is because eslint-interactive evaluates this file with `eval`.
49
+ filterSuggestions;
@@ -0,0 +1,47 @@
1
+ // @ts-check
2
+
3
+ // Edit this file to customize how you want to convert `Linter.LintMessage` to `Rule.Fix`.
4
+ // Save and close this file to run `eslint --fix`.
5
+ //
6
+ // NOTE(For VSCode user):
7
+ // In the Restricted Mode of VSCode, the close event of the file is not notified to eslint-interactive.
8
+ // Therefore, eslint-interactive will not start applying suggestions even if the file is closed, and eslint-interactive will freeze.
9
+ // If you open a file in VSCode Restricted Mode, please follow the steps below:
10
+ // 1. Trust the workspace
11
+ // 2. Close the open file
12
+ // 3. exit the frozen eslint-interactive with Ctrl+C
13
+ // 4. Restart eslint-interactive and try to apply the suggestion again
14
+
15
+ /**
16
+ * A function to convert `Linter.LintMessage` to `Rule.Fix`.
17
+ * @param {import('eslint').Linter.LintMessage} message - The `Linter.LintMessage` to be converted.
18
+ * @param {import('estree').Node | null} node - The node corresponding to the message.
19
+ * @param {import('eslint-interactive').FixContext} context - The context of the fix.
20
+ * @returns {import('eslint').Rule.Fix | null | undefined} The `Rule.Fix` converted from `Linter.LintMessage`. If null or undefined, the message is not fixable.
21
+ */
22
+ function fixableMaker(message, node, context) {
23
+ // example:
24
+
25
+ console.log(context.filename);
26
+
27
+ // Edge case handling
28
+ if (!node) return null;
29
+ if (!node.range) return null;
30
+
31
+ if (message.ruleId === 'no-unused-vars' || message.ruleId === '@typescript-eslint/no-unused-vars') {
32
+ // Add underscores to the head of unused variable names.
33
+ // target codes: https://astexplorer.net/#/gist/e33d44d2e69a733766abbc9706fd3ed5/169a615afba7b0d5c88e87894db93fc7346250d2
34
+
35
+ if (node.type !== 'Identifier') return null;
36
+ // For more information about the fixer API, see the following:
37
+ // https://eslint.org/docs/developer-guide/working-with-rules#applying-fixes
38
+ return context.fixer.insertTextBefore(node, '_');
39
+ } else {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ // Here, `fixableMaker` is passed to eslint-interactive pass.
45
+ // NOTE: The value evaluated on the last line of the file will be passed to eslint-interactive.
46
+ // This is because eslint-interactive evaluates this file with `eval`.
47
+ fixableMaker;