1 | #!/usr/bin/env node
|
2 |
|
3 | "use strict";
|
4 |
|
5 | const validators = require("./validators");
|
6 | const config = require("..");
|
7 | const prettier = require("../prettier");
|
8 |
|
9 |
|
10 |
|
11 | const { ESLint } = require(require.resolve("eslint", {
|
12 | paths: [process.cwd(), ...require.resolve.paths("eslint")],
|
13 | }));
|
14 |
|
15 | const SPECIAL_RULES_URL =
|
16 | "https://github.com/prettier/eslint-config-prettier#special-rules";
|
17 |
|
18 | const PRETTIER_RULES_URL =
|
19 | "https://github.com/prettier/eslint-config-prettier#arrow-body-style-and-prefer-arrow-callback";
|
20 |
|
21 | if (module === require.main) {
|
22 | const args = process.argv.slice(2);
|
23 |
|
24 | if (args.length === 0) {
|
25 | console.error(help());
|
26 | process.exit(1);
|
27 | }
|
28 |
|
29 | const eslint = new ESLint();
|
30 |
|
31 | Promise.all(args.map((file) => eslint.calculateConfigForFile(file)))
|
32 | .then((configs) => {
|
33 | const rules = configs.flatMap(({ rules }, index) =>
|
34 | Object.entries(rules).map((entry) => [...entry, args[index]])
|
35 | );
|
36 | const result = processRules(rules);
|
37 | if (result.stderr) {
|
38 | console.error(result.stderr);
|
39 | }
|
40 | if (result.stdout) {
|
41 | console.error(result.stdout);
|
42 | }
|
43 | process.exit(result.code);
|
44 | })
|
45 | .catch((error) => {
|
46 | console.error(error.message);
|
47 | process.exit(1);
|
48 | });
|
49 | }
|
50 |
|
51 | function help() {
|
52 | return `
|
53 | Usage: npx eslint-config-prettier FILE...
|
54 |
|
55 | Resolves an ESLint configuration for every given FILE and checks if they
|
56 | contain rules that are unnecessary or conflict with Prettier. Example:
|
57 |
|
58 | npx eslint-config-prettier index.js test/index.js other/file/to/check.js
|
59 |
|
60 | Exit codes:
|
61 |
|
62 | 0: No automatically detectable problems found.
|
63 | 1: General error.
|
64 | 2: Conflicting rules found.
|
65 |
|
66 | For more information, see:
|
67 | https://github.com/prettier/eslint-config-prettier#cli-helper-tool
|
68 | `.trim();
|
69 | }
|
70 |
|
71 | function processRules(configRules) {
|
72 | const regularRules = filterRules(config.rules, (_, value) => value === "off");
|
73 | const optionsRules = filterRules(
|
74 | config.rules,
|
75 | (ruleName, value) => value === 0 && ruleName in validators
|
76 | );
|
77 | const specialRules = filterRules(
|
78 | config.rules,
|
79 | (ruleName, value) => value === 0 && !(ruleName in validators)
|
80 | );
|
81 |
|
82 | const enabledRules = configRules
|
83 | .map(([ruleName, value, source]) => {
|
84 | const arrayValue = Array.isArray(value) ? value : [value];
|
85 | const [level, ...options] = arrayValue;
|
86 | const isOff = level === "off" || level === 0;
|
87 | return isOff ? null : { ruleName, options, source };
|
88 | })
|
89 | .filter(Boolean);
|
90 |
|
91 | const flaggedRules = enabledRules.filter(
|
92 | ({ ruleName }) => ruleName in config.rules
|
93 | );
|
94 |
|
95 | const regularFlaggedRuleNames = filterRuleNames(
|
96 | flaggedRules,
|
97 | ({ ruleName }) => ruleName in regularRules
|
98 | );
|
99 | const optionsFlaggedRuleNames = filterRuleNames(
|
100 | flaggedRules,
|
101 | ({ ruleName, ...rule }) =>
|
102 | ruleName in optionsRules && !validators[ruleName](rule)
|
103 | );
|
104 | const specialFlaggedRuleNames = filterRuleNames(
|
105 | flaggedRules,
|
106 | ({ ruleName }) => ruleName in specialRules
|
107 | );
|
108 | const prettierFlaggedRuleNames = filterRuleNames(
|
109 | enabledRules,
|
110 | ({ ruleName, source }) =>
|
111 | ruleName in prettier.rules &&
|
112 | enabledRules.some(
|
113 | (rule) =>
|
114 | rule.ruleName === "prettier/prettier" && rule.source === source
|
115 | )
|
116 | );
|
117 |
|
118 | const regularMessage = [
|
119 | "The following rules are unnecessary or might conflict with Prettier:",
|
120 | "",
|
121 | printRuleNames(regularFlaggedRuleNames),
|
122 | ].join("\n");
|
123 |
|
124 | const optionsMessage = [
|
125 | "The following rules are enabled with config that might conflict with Prettier. See:",
|
126 | SPECIAL_RULES_URL,
|
127 | "",
|
128 | printRuleNames(optionsFlaggedRuleNames),
|
129 | ].join("\n");
|
130 |
|
131 | const specialMessage = [
|
132 | "The following rules are enabled but cannot be automatically checked. See:",
|
133 | SPECIAL_RULES_URL,
|
134 | "",
|
135 | printRuleNames(specialFlaggedRuleNames),
|
136 | ].join("\n");
|
137 |
|
138 | const prettierMessage = [
|
139 | "The following rules can cause issues when using eslint-plugin-prettier at the same time.",
|
140 | "Only enable them if you know what you are doing! See:",
|
141 | PRETTIER_RULES_URL,
|
142 | "",
|
143 | printRuleNames(prettierFlaggedRuleNames),
|
144 | ].join("\n");
|
145 |
|
146 | if (
|
147 | regularFlaggedRuleNames.length === 0 &&
|
148 | optionsFlaggedRuleNames.length === 0
|
149 | ) {
|
150 | const message =
|
151 | specialFlaggedRuleNames.length === 0 &&
|
152 | prettierFlaggedRuleNames.length === 0
|
153 | ? "No rules that are unnecessary or conflict with Prettier were found."
|
154 | : [
|
155 | specialFlaggedRuleNames.length === 0 ? null : specialMessage,
|
156 | prettierFlaggedRuleNames.length === 0 ? null : prettierMessage,
|
157 | "Other than that, no rules that are unnecessary or conflict with Prettier were found.",
|
158 | ]
|
159 | .filter(Boolean)
|
160 | .join("\n\n");
|
161 |
|
162 | return {
|
163 | stdout: message,
|
164 | code: 0,
|
165 | };
|
166 | }
|
167 |
|
168 | const message = [
|
169 | regularFlaggedRuleNames.length === 0 ? null : regularMessage,
|
170 | optionsFlaggedRuleNames.length === 0 ? null : optionsMessage,
|
171 | specialFlaggedRuleNames.length === 0 ? null : specialMessage,
|
172 | prettierFlaggedRuleNames.length === 0 ? null : prettierMessage,
|
173 | ]
|
174 | .filter(Boolean)
|
175 | .join("\n\n");
|
176 |
|
177 | return {
|
178 | stdout: message,
|
179 | code: 2,
|
180 | };
|
181 | }
|
182 |
|
183 | function filterRules(rules, fn) {
|
184 | return Object.fromEntries(
|
185 | Object.entries(rules)
|
186 | .filter(([ruleName, value]) => fn(ruleName, value))
|
187 | .map(([ruleName]) => [ruleName, true])
|
188 | );
|
189 | }
|
190 |
|
191 | function filterRuleNames(rules, fn) {
|
192 | return [
|
193 | ...new Set(rules.filter((rule) => fn(rule)).map((rule) => rule.ruleName)),
|
194 | ];
|
195 | }
|
196 |
|
197 | function printRuleNames(ruleNames) {
|
198 | return ruleNames
|
199 | .slice()
|
200 | .sort()
|
201 | .map((ruleName) => `- ${ruleName}`)
|
202 | .join("\n");
|
203 | }
|
204 |
|
205 | exports.processRules = processRules;
|