prettier-eslint
Version:
Formats your JavaScript using prettier followed by eslint --fix
374 lines • 13 kB
JavaScript
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