UNPKG

prettier-eslint

Version:

Formats your JavaScript using prettier followed by eslint --fix

266 lines (213 loc) 9.69 kB
"use strict"; var _slicedToArray2 = require("babel-runtime/helpers/slicedToArray"); var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); var _entries = require("babel-runtime/core-js/object/entries"); var _entries2 = _interopRequireDefault(_entries); var _fs = require("fs"); var _fs2 = _interopRequireDefault(_fs); var _path = require("path"); var _path2 = _interopRequireDefault(_path); var _requireRelative = require("require-relative"); var _requireRelative2 = _interopRequireDefault(_requireRelative); var _prettyFormat = require("pretty-format"); var _prettyFormat2 = _interopRequireDefault(_prettyFormat); var _commonTags = require("common-tags"); var _indentString = require("indent-string"); var _indentString2 = _interopRequireDefault(_indentString); var _loglevelColoredLevelPrefix = require("loglevel-colored-level-prefix"); var _loglevelColoredLevelPrefix2 = _interopRequireDefault(_loglevelColoredLevelPrefix); var _lodash = require("lodash.merge"); var _lodash2 = _interopRequireDefault(_lodash); var _utils = require("./utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var logger = (0, _loglevelColoredLevelPrefix2.default)({ prefix: "prettier-eslint" }); // CommonJS + ES6 modules... is it worth it? Probably not... /* eslint no-console:0, global-require:0, import/no-dynamic-require:0 */ /* eslint complexity: [1, 13] */ module.exports = format; /** * Formats the text with prettier and then eslint based on the given options * @param {String} options.filePath - the path of the file being formatted * can be used in leu of `eslintConfig` (eslint will be used to find the * relevant config for the file). Will also be used to load the `text` if * `text` is not provided. * @param {String} options.text - the text (JavaScript code) to format * @param {String} options.eslintPath - the path to the eslint module to use. * Will default to require.resolve('eslint') * @param {String} options.prettierPath - the path to the prettier module. * Will default to require.resovlve('prettier') * @param {Object} options.eslintConfig - the config to use for formatting * with ESLint. * @param {Object} options.prettierOptions - the options to pass for * formatting with `prettier`. If not provided, prettier-eslint will attempt * to create the options based on the eslintConfig * @param {Object} options.fallbackPrettierOptions - the options to pass for * formatting with `prettier` if the given option is not inferrable from the * eslintConfig. * @param {String} options.logLevel - the level for the logs * (error, warn, info, debug, trace) * @param {Boolean} options.prettierLast - Run Prettier Last * @return {String} - the formatted string */ function format(options) { var _options$logLevel = options.logLevel, logLevel = _options$logLevel === undefined ? getDefaultLogLevel() : _options$logLevel; logger.setLevel(logLevel); logger.trace("called format with options:", (0, _prettyFormat2.default)(options)); var filePath = options.filePath, _options$text = options.text, text = _options$text === undefined ? getTextFromFilePath(filePath) : _options$text, _options$eslintPath = options.eslintPath, eslintPath = _options$eslintPath === undefined ? getModulePath(filePath, "eslint") : _options$eslintPath, _options$prettierPath = options.prettierPath, prettierPath = _options$prettierPath === undefined ? getModulePath(filePath, "prettier") : _options$prettierPath, prettierLast = options.prettierLast, fallbackPrettierOptions = options.fallbackPrettierOptions; var eslintConfig = (0, _lodash2.default)({}, options.eslintConfig, getESLintConfig(filePath, eslintPath)); if (typeof eslintConfig.globals === "object") { eslintConfig.globals = (0, _entries2.default)(eslintConfig.globals).map(function (_ref) { var _ref2 = (0, _slicedToArray3.default)(_ref, 2), key = _ref2[0], value = _ref2[1]; return `${key}:${value}`; }); } var prettierOptions = (0, _lodash2.default)({}, filePath && { filepath: filePath }, getPrettierConfig(filePath, prettierPath), options.prettierOptions); var formattingOptions = (0, _utils.getOptionsForFormatting)(eslintConfig, prettierOptions, fallbackPrettierOptions, eslintPath); logger.debug("inferred options:", (0, _prettyFormat2.default)({ filePath, text, eslintPath, prettierPath, eslintConfig: formattingOptions.eslint, prettierOptions: formattingOptions.prettier, logLevel, prettierLast })); var eslintExtensions = eslintConfig.extensions || [".js", ".jsx", ".ts", ".tsx"]; var fileExtension = _path2.default.extname(filePath || ""); // If we don't get filePath run eslint on text, otherwise only run eslint // if it's a configured extension or fall back to a "supported" file type. var onlyPrettier = filePath ? !eslintExtensions.includes(fileExtension) : false; var prettify = createPrettify(formattingOptions.prettier, prettierPath); if (onlyPrettier) { return prettify(text); } if ([".ts", ".tsx"].includes(fileExtension)) { // XXX: It seems babylon is getting a TypeScript plugin. // Should that be used instead? formattingOptions.eslint.parser = "typescript-eslint-parser"; } var eslintFix = createEslintFix(formattingOptions.eslint, eslintPath); if (prettierLast) { return prettify(eslintFix(text, filePath)); } return eslintFix(prettify(text), filePath); } function createPrettify(formatOptions, prettierPath) { return function prettify(text) { logger.debug("calling prettier on text"); logger.trace(_commonTags.stripIndent` prettier input: ${(0, _indentString2.default)(text, 2)} `); var prettier = (0, _utils.requireModule)(prettierPath, "prettier"); try { logger.trace(`calling prettier.format with the text and prettierOptions`); var output = prettier.format(text, formatOptions); logger.trace("prettier: output === input", output === text); logger.trace(_commonTags.stripIndent` prettier output: ${(0, _indentString2.default)(output, 2)} `); return output; } catch (error) { logger.error("prettier formatting failed due to a prettier error"); throw error; } }; } function createEslintFix(eslintConfig, eslintPath) { return function eslintFix(text, filePath) { var cliEngine = (0, _utils.getESLintCLIEngine)(eslintPath, eslintConfig); try { logger.trace(`calling cliEngine.executeOnText with the text`); var report = cliEngine.executeOnText(text, filePath, true); logger.trace(`executeOnText returned the following report:`, (0, _prettyFormat2.default)(report)); // default the output to text because if there's nothing // to fix, eslint doesn't provide `output` var _report$results = (0, _slicedToArray3.default)(report.results, 1), _report$results$0$out = _report$results[0].output, output = _report$results$0$out === undefined ? text : _report$results$0$out; logger.trace("eslint --fix: output === input", output === text); // NOTE: We're ignoring linting errors/warnings here and // defaulting to the given text if there are any // because all we're trying to do is fix what we can. // We don't care about what we can't logger.trace(_commonTags.stripIndent` eslint --fix output: ${(0, _indentString2.default)(output, 2)} `); return output; } catch (error) { logger.error("eslint fix failed due to an eslint error"); throw error; } }; } function getTextFromFilePath(filePath) { try { logger.trace(_commonTags.oneLine` attempting fs.readFileSync to get the text for file at "${filePath}" `); return _fs2.default.readFileSync(filePath, "utf8"); } catch (error) { logger.error(_commonTags.oneLine` failed to get the text to format from the given filePath: "${filePath}" `); throw error; } } function getESLintConfig(filePath, eslintPath) { var eslintOptions = {}; if (filePath) { eslintOptions.cwd = _path2.default.dirname(filePath); } logger.trace(_commonTags.oneLine` creating ESLint CLI Engine to get the config for "${filePath || process.cwd()}" `); var cliEngine = (0, _utils.getESLintCLIEngine)(eslintPath, eslintOptions); try { logger.debug(`getting eslint config for file at "${filePath}"`); var config = cliEngine.getConfigForFile(filePath); logger.trace(`eslint config for "${filePath}" received`, (0, _prettyFormat2.default)(config)); return config; } catch (error) { // is this noisy? Try setting options.disableLog to false logger.debug("Unable to find config"); return { rules: {} }; } } function getPrettierConfig(filePath, prettierPath) { var prettier = (0, _utils.requireModule)(prettierPath, "prettier"); return prettier.resolveConfig && prettier.resolveConfig.sync && prettier.resolveConfig.sync(filePath) || {}; } function getModulePath() { var filePath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : __filename; var moduleName = arguments[1]; try { return _requireRelative2.default.resolve(moduleName, filePath); } catch (error) { logger.debug(_commonTags.oneLine` There was a problem finding the ${moduleName} module. Using prettier-eslint's version. `, error.message, error.stack); return require.resolve(moduleName); } } function getDefaultLogLevel() { return process.env.LOG_LEVEL || "warn"; }