UNPKG

5.79 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3"use strict";
4
5const validators = require("./validators");
6const config = require("..");
7const prettier = require("../prettier");
8
9// Require locally installed eslint, for `npx eslint-config-prettier` support
10// with no local eslint-config-prettier installation.
11const { ESLint } = require(require.resolve("eslint", {
12 paths: [process.cwd(), ...require.resolve.paths("eslint")],
13}));
14
15const SPECIAL_RULES_URL =
16 "https://github.com/prettier/eslint-config-prettier#special-rules";
17
18const PRETTIER_RULES_URL =
19 "https://github.com/prettier/eslint-config-prettier#arrow-body-style-and-prefer-arrow-callback";
20
21if (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
51function help() {
52 return `
53Usage: npx eslint-config-prettier FILE...
54
55Resolves an ESLint configuration for every given FILE and checks if they
56contain 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
60Exit codes:
61
620: No automatically detectable problems found.
631: General error.
642: Conflicting rules found.
65
66For more information, see:
67https://github.com/prettier/eslint-config-prettier#cli-helper-tool
68 `.trim();
69}
70
71function 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
183function 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
191function filterRuleNames(rules, fn) {
192 return [
193 ...new Set(rules.filter((rule) => fn(rule)).map((rule) => rule.ruleName)),
194 ];
195}
196
197function printRuleNames(ruleNames) {
198 return ruleNames
199 .slice()
200 .sort()
201 .map((ruleName) => `- ${ruleName}`)
202 .join("\n");
203}
204
205exports.processRules = processRules;