#!/usr/bin/env node "use strict"; const validators = require("./validators"); const config = require(".."); const prettier = require("../prettier"); // Require locally installed eslint, for `npx eslint-config-prettier` support // with no local eslint-config-prettier installation. const { ESLint } = require(require.resolve("eslint", { paths: [process.cwd(), ...require.resolve.paths("eslint")], })); const SPECIAL_RULES_URL = "https://github.com/prettier/eslint-config-prettier#special-rules"; const PRETTIER_RULES_URL = "https://github.com/prettier/eslint-config-prettier#arrow-body-style-and-prefer-arrow-callback"; if (module === require.main) { const args = process.argv.slice(2); if (args.length === 0) { console.error(help()); process.exit(1); } const eslint = new ESLint(); Promise.all(args.map((file) => eslint.calculateConfigForFile(file))) .then((configs) => { const rules = configs.flatMap(({ rules }, index) => Object.entries(rules).map((entry) => [...entry, args[index]]) ); const result = processRules(rules); if (result.stderr) { console.error(result.stderr); } if (result.stdout) { console.error(result.stdout); } process.exit(result.code); }) .catch((error) => { console.error(error.message); process.exit(1); }); } function help() { return ` Usage: npx eslint-config-prettier FILE... Resolves an ESLint configuration for every given FILE and checks if they contain rules that are unnecessary or conflict with Prettier. Example: npx eslint-config-prettier index.js test/index.js other/file/to/check.js Exit codes: 0: No automatically detectable problems found. 1: General error. 2: Conflicting rules found. For more information, see: https://github.com/prettier/eslint-config-prettier#cli-helper-tool `.trim(); } function processRules(configRules) { const regularRules = filterRules(config.rules, (_, value) => value === "off"); const optionsRules = filterRules( config.rules, (ruleName, value) => value === 0 && ruleName in validators ); const specialRules = filterRules( config.rules, (ruleName, value) => value === 0 && !(ruleName in validators) ); const enabledRules = configRules .map(([ruleName, value, source]) => { const arrayValue = Array.isArray(value) ? value : [value]; const [level, ...options] = arrayValue; const isOff = level === "off" || level === 0; return isOff ? null : { ruleName, options, source }; }) .filter(Boolean); const flaggedRules = enabledRules.filter( ({ ruleName }) => ruleName in config.rules ); const regularFlaggedRuleNames = filterRuleNames( flaggedRules, ({ ruleName }) => ruleName in regularRules ); const optionsFlaggedRuleNames = filterRuleNames( flaggedRules, ({ ruleName, ...rule }) => ruleName in optionsRules && !validators[ruleName](rule) ); const specialFlaggedRuleNames = filterRuleNames( flaggedRules, ({ ruleName }) => ruleName in specialRules ); const prettierFlaggedRuleNames = filterRuleNames( enabledRules, ({ ruleName, source }) => ruleName in prettier.rules && enabledRules.some( (rule) => rule.ruleName === "prettier/prettier" && rule.source === source ) ); const regularMessage = [ "The following rules are unnecessary or might conflict with Prettier:", "", printRuleNames(regularFlaggedRuleNames), ].join("\n"); const optionsMessage = [ "The following rules are enabled with config that might conflict with Prettier. See:", SPECIAL_RULES_URL, "", printRuleNames(optionsFlaggedRuleNames), ].join("\n"); const specialMessage = [ "The following rules are enabled but cannot be automatically checked. See:", SPECIAL_RULES_URL, "", printRuleNames(specialFlaggedRuleNames), ].join("\n"); const prettierMessage = [ "The following rules can cause issues when using eslint-plugin-prettier at the same time.", "Only enable them if you know what you are doing! See:", PRETTIER_RULES_URL, "", printRuleNames(prettierFlaggedRuleNames), ].join("\n"); if ( regularFlaggedRuleNames.length === 0 && optionsFlaggedRuleNames.length === 0 ) { const message = specialFlaggedRuleNames.length === 0 && prettierFlaggedRuleNames.length === 0 ? "No rules that are unnecessary or conflict with Prettier were found." : [ specialFlaggedRuleNames.length === 0 ? null : specialMessage, prettierFlaggedRuleNames.length === 0 ? null : prettierMessage, "Other than that, no rules that are unnecessary or conflict with Prettier were found.", ] .filter(Boolean) .join("\n\n"); return { stdout: message, code: 0, }; } const message = [ regularFlaggedRuleNames.length === 0 ? null : regularMessage, optionsFlaggedRuleNames.length === 0 ? null : optionsMessage, specialFlaggedRuleNames.length === 0 ? null : specialMessage, prettierFlaggedRuleNames.length === 0 ? null : prettierMessage, ] .filter(Boolean) .join("\n\n"); return { stdout: message, code: 2, }; } function filterRules(rules, fn) { return Object.fromEntries( Object.entries(rules) .filter(([ruleName, value]) => fn(ruleName, value)) .map(([ruleName]) => [ruleName, true]) ); } function filterRuleNames(rules, fn) { return [ ...new Set(rules.filter((rule) => fn(rule)).map((rule) => rule.ruleName)), ]; } function printRuleNames(ruleNames) { return ruleNames .slice() .sort() .map((ruleName) => `- ${ruleName}`) .join("\n"); } exports.processRules = processRules;