UNPKG

prettier-eslint

Version:

Formats your JavaScript using prettier followed by eslint --fix

374 lines 13 kB
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) { if (typeof path === "string" && /^\.\.?\//.test(path)) { return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) { return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js"); }); } return path; }; import { createRequire } from 'node:module'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; import { inspect } from 'node:util'; import { oneLine } from 'common-tags'; import { builtinRules } from 'eslint/use-at-your-own-risk'; import getLogger from 'loglevel-colored-level-prefix'; import {} from 'prettier'; const logger = getLogger({ prefix: 'prettier-eslint' }); const require = createRequire(import.meta.url); export function formatForLog(value) { return inspect(value, { depth: null }); } export function mergeConfigs(...configs) { const merged = {}; for (const config of configs) { mergeConfig(merged, config); } return merged; } function mergeConfig(target, source) { if (!isPlainObject(source)) { return; } for (const [key, value] of Object.entries(source)) { if (value === undefined) { continue; } const existing = target[key]; target[key] = isPlainObject(existing) && isPlainObject(value) ? mergeConfig({ ...existing }, value) : value; } return target; } function isPlainObject(value) { return Object.prototype.toString.call(value) === '[object Object]'; } export function getModulePath(filePath, moduleName) { if (!filePath) { return moduleName; } try { const modulePath = createRequire(pathToFileURL(path.resolve(filePath))).resolve(moduleName); return modulePath === require.resolve(moduleName) ? moduleName : modulePath; } catch (err) { const error = err; logger.debug(oneLine ` There was a problem finding the ${moduleName} module. Using prettier-eslint's version. `, error.message, error.stack); return moduleName; } } const RULE_DISABLED = {}; const RULE_NOT_CONFIGURED = 'RULE_NOT_CONFIGURED'; const ruleValueExists = (prettierRuleValue) => prettierRuleValue !== RULE_NOT_CONFIGURED && prettierRuleValue !== RULE_DISABLED && prettierRuleValue !== undefined; const OPTION_GETTERS = { printWidth: { ruleValue: rules => getRuleValue(rules, 'max-len', 'code'), ruleValueToPrettierOption: getPrintWidth, }, tabWidth: { ruleValue(rules) { let value = getRuleValue(rules, 'indent'); if (value === 'tab') { value = getRuleValue(rules, 'max-len', 'tabWidth'); } return value; }, ruleValueToPrettierOption: getTabWidth, }, singleQuote: { ruleValue: rules => getRuleValue(rules, 'quotes'), ruleValueToPrettierOption: getSingleQuote, }, trailingComma: { ruleValue: rules => getRuleValue(rules, 'comma-dangle', []), ruleValueToPrettierOption: getTrailingComma, }, bracketSpacing: { ruleValue: rules => getRuleValue(rules, 'object-curly-spacing'), ruleValueToPrettierOption: getBracketSpacing, }, semi: { ruleValue: rules => getRuleValue(rules, 'semi'), ruleValueToPrettierOption: getSemi, }, useTabs: { ruleValue: rules => getRuleValue(rules, 'indent'), ruleValueToPrettierOption: getUseTabs, }, bracketSameLine: { ruleValue: rules => getRuleValue(rules, 'react/jsx-closing-bracket-location', 'nonEmpty'), ruleValueToPrettierOption: getBracketSameLine, }, arrowParens: { ruleValue: rules => getRuleValue(rules, 'arrow-parens'), ruleValueToPrettierOption: getArrowParens, }, }; export function getOptionsForFormatting(eslintConfig, prettierOptions = {}, fallbackPrettierOptions = {}) { const eslint = getRelevantESLintConfig(eslintConfig); const prettier = getPrettierOptionsFromESLintRules(eslintConfig, prettierOptions, fallbackPrettierOptions); return { eslint, prettier }; } let relevantRules; function getRelevantESLintConfig(eslintConfig) { logger.debug('turning off unfixable rules'); if (!relevantRules) { relevantRules = {}; for (const [name, rule] of builtinRules) { if (!rule.meta?.fixable) { logger.trace('turning off rule:', JSON.stringify({ [name]: rule })); relevantRules[name] = ['off']; } } } return { ...eslintConfig, rules: { ...eslintConfig.rules, ...relevantRules }, fix: eslintConfig.fix ?? true, }; } function getPrettierOptionsFromESLintRules(eslintConfig, prettierOptions, fallbackPrettierOptions) { const { rules } = eslintConfig; const prettierPluginOptions = getRuleValue(rules, 'prettier/prettier', []); if (ruleValueExists(prettierPluginOptions) && typeof prettierPluginOptions === 'object') { prettierOptions = { ...prettierPluginOptions, ...prettierOptions }; } return Object.keys(OPTION_GETTERS).reduce((options, key) => configureOptions(prettierOptions, fallbackPrettierOptions, key, options, rules), prettierOptions); } function configureOptions(prettierOptions, fallbackPrettierOptions, key, options, rules) { const givenOption = prettierOptions[key]; const optionIsGiven = givenOption !== undefined; if (optionIsGiven) { options[key] = givenOption; } else { const { ruleValue, ruleValueToPrettierOption } = OPTION_GETTERS[key]; const eslintRuleValue = ruleValue(rules); const option = ruleValueToPrettierOption(eslintRuleValue, fallbackPrettierOptions, rules); if (option !== undefined) { options[key] = option; } } return options; } function getPrintWidth(eslintValue, fallbacks) { return makePrettierOption('printWidth', eslintValue, fallbacks); } function getTabWidth(eslintValue, fallbacks) { return makePrettierOption('tabWidth', eslintValue, fallbacks); } function getSingleQuote(eslintValue, fallbacks) { let prettierValue; switch (eslintValue) { case 'single': { prettierValue = true; break; } case 'double': case 'backtick': { prettierValue = false; break; } default: { prettierValue = eslintValue; } } return makePrettierOption('singleQuote', prettierValue, fallbacks); } function getTrailingComma(eslintValue, fallbacks) { let prettierValue; if (eslintValue === 'never') { prettierValue = 'none'; } else if (typeof eslintValue === 'string' && eslintValue.startsWith('always')) { prettierValue = 'es5'; } else if (typeof eslintValue === 'object') { prettierValue = getValFromTrailingCommaConfig(eslintValue); } else { prettierValue = RULE_NOT_CONFIGURED; } return makePrettierOption('trailingComma', prettierValue, fallbacks); } function getValFromTrailingCommaConfig(objectConfig) { const { arrays = '', objects = '', functions = '' } = objectConfig; const fns = isAlways(functions); const es5 = [arrays, objects].some(isAlways); if (fns) { return 'all'; } if (es5) { return 'es5'; } return 'none'; } function getBracketSpacing(eslintValue, fallbacks) { let prettierValue; if (eslintValue === 'never') { prettierValue = false; } else if (eslintValue === 'always') { prettierValue = true; } else { prettierValue = eslintValue; } return makePrettierOption('bracketSpacing', prettierValue, fallbacks); } function getSemi(eslintValue, fallbacks) { let prettierValue; if (eslintValue === 'never') { prettierValue = false; } else if (eslintValue === 'always') { prettierValue = true; } else { prettierValue = eslintValue; } return makePrettierOption('semi', prettierValue, fallbacks); } function getUseTabs(eslintValue, fallbacks) { const prettierValue = eslintValue === 'tab' ? true : RULE_NOT_CONFIGURED; return makePrettierOption('useTabs', prettierValue, fallbacks); } function getBracketSameLine(eslintValue, fallbacks) { let prettierValue; if (eslintValue === 'after-props') { prettierValue = true; } else if (eslintValue === 'tag-aligned' || eslintValue === 'line-aligned' || eslintValue === 'props-aligned') { prettierValue = false; } else { prettierValue = eslintValue; } return makePrettierOption('bracketSameLine', prettierValue, fallbacks); } function getArrowParens(eslintValue, fallbacks) { const prettierValue = eslintValue === 'as-needed' ? 'avoid' : eslintValue; return makePrettierOption('arrowParens', prettierValue, fallbacks); } function extractRuleValue(objPath, name, value) { if (objPath) { logger.trace(oneLine ` Getting the value from object configuration of ${name}. delving into ${JSON.stringify(value)} with path "${objPath}" `); return getPathValue(value, objPath, RULE_NOT_CONFIGURED); } logger.debug(oneLine ` The ${name} rule is using an object configuration of ${JSON.stringify(value)} but prettier-eslint is not currently capable of getting the prettier value based on an object configuration for ${name}. Please file an issue (and make a pull request?) `); } function getPathValue(value, objPath, fallback) { const pathParts = Array.isArray(objPath) ? objPath : objPath.split('.'); let current = value; for (const pathPart of pathParts) { if (current == null || typeof current !== 'object') { return fallback; } current = current[pathPart]; } return current === undefined ? fallback : current; } function getRuleValue(rules = {}, name, objPath) { const ruleConfig = rules[name]; if (Array.isArray(ruleConfig)) { const [ruleSetting, value] = ruleConfig; if (ruleSetting === 0 || ruleSetting === 'off') { return RULE_DISABLED; } if (value != null && typeof value === 'object') { return extractRuleValue(objPath, name, value); } logger.trace(oneLine ` The ${name} rule is configured with a non-object value of ${value}. Using that value. `); return value; } return RULE_NOT_CONFIGURED; } function isAlways(val) { return val.startsWith('always'); } function makePrettierOption(prettierRuleName, prettierRuleValue, fallbacks) { if (ruleValueExists(prettierRuleValue)) { return prettierRuleValue; } const fallback = fallbacks[prettierRuleName]; if (fallback !== undefined) { logger.debug(oneLine ` The ${prettierRuleName} rule is not configured, using provided fallback of ${fallback} `); return fallback; } logger.debug(oneLine ` The ${prettierRuleName} rule is not configured, let prettier decide `); } export function loadModule(modulePath) { return import(__rewriteRelativeImportExtension(modulePath, true)); } export async function importModule(modulePath, name = modulePath) { try { logger.trace(`importing "${name}" module via "${modulePath}"`); const imported = await loadModule(path.isAbsolute(modulePath) ? pathToFileURL(modulePath).href : modulePath); return typeof imported === 'object' && 'default' in imported ? imported.default : imported; } catch (error) { logger.error(oneLine ` There was trouble importing "${name}". Is "${modulePath}" a correct path to the "${name}" module? `); throw error; } } export async function getESLint(eslintPath, eslintOptions) { const { ESLint } = await importModule(eslintPath, 'eslint'); try { return new ESLint(eslintOptions); } catch (error) { logger.error('There was trouble creating the ESLint instance.'); throw error; } } export const extractFileExtensions = (patterns) => { const extensions = patterns.flatMap(pattern => { const matchMultiple = /\.\{([^}]+)\}/.exec(pattern); if (matchMultiple) { return matchMultiple[1].split(',').reduce((acc, ext) => { ext = ext.trim(); if (ext) { acc.push(`.${ext}`); } return acc; }, []); } return /(\.[a-z0-9]+)$/i.exec(pattern)?.[1] ?? []; }); return [...new Set(extensions)]; }; //# sourceMappingURL=utils.js.map