summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/formatters/codeframe.js
blob: 0b97a0d81803317221b9d60e66046de606ca0180 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
 * @fileoverview Codeframe reporter
 * @author Vitor Balocco
 */
"use strict";

const chalk = require("chalk");
const codeFrame = require("babel-code-frame");
const path = require("path");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Given a word and a count, append an s if count is not one.
 * @param   {string} word  A word in its singular form.
 * @param   {number} count A number controlling whether word should be pluralized.
 * @returns {string}       The original word with an s on the end if count is not one.
 */
function pluralize(word, count) {
    return (count === 1 ? word : `${word}s`);
}

/**
 * Gets a formatted relative file path from an absolute path and a line/column in the file.
 * @param   {string} filePath The absolute file path to format.
 * @param   {number} line     The line from the file to use for formatting.
 * @param   {number} column   The column from the file to use for formatting.
 * @returns {string}          The formatted file path.
 */
function formatFilePath(filePath, line, column) {
    let relPath = path.relative(process.cwd(), filePath);

    if (line && column) {
        relPath += `:${line}:${column}`;
    }

    return chalk.green(relPath);
}

/**
 * Gets the formatted output for a given message.
 * @param   {Object} message      The object that represents this message.
 * @param   {Object} parentResult The result object that this message belongs to.
 * @returns {string}              The formatted output.
 */
function formatMessage(message, parentResult) {
    const type = (message.fatal || message.severity === 2) ? chalk.red("error") : chalk.yellow("warning");
    const msg = `${chalk.bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
    const ruleId = message.fatal ? "" : chalk.dim(`(${message.ruleId})`);
    const filePath = formatFilePath(parentResult.filePath, message.line, message.column);
    const sourceCode = parentResult.output ? parentResult.output : parentResult.source;

    const firstLine = [
        `${type}:`,
        `${msg}`,
        ruleId ? `${ruleId}` : "",
        sourceCode ? `at ${filePath}:` : `at ${filePath}`
    ].filter(String).join(" ");

    const result = [firstLine];

    if (sourceCode) {
        result.push(
            codeFrame(sourceCode, message.line, message.column, { highlightCode: false })
        );
    }

    return result.join("\n");
}

/**
 * Gets the formatted output summary for a given number of errors and warnings.
 * @param   {number} errors   The number of errors.
 * @param   {number} warnings The number of warnings.
 * @param   {number} fixableErrors The number of fixable errors.
 * @param   {number} fixableWarnings The number of fixable warnings.
 * @returns {string}          The formatted output summary.
 */
function formatSummary(errors, warnings, fixableErrors, fixableWarnings) {
    const summaryColor = errors > 0 ? "red" : "yellow";
    const summary = [];
    const fixablesSummary = [];

    if (errors > 0) {
        summary.push(`${errors} ${pluralize("error", errors)}`);
    }

    if (warnings > 0) {
        summary.push(`${warnings} ${pluralize("warning", warnings)}`);
    }

    if (fixableErrors > 0) {
        fixablesSummary.push(`${fixableErrors} ${pluralize("error", fixableErrors)}`);
    }

    if (fixableWarnings > 0) {
        fixablesSummary.push(`${fixableWarnings} ${pluralize("warning", fixableWarnings)}`);
    }

    let output = chalk[summaryColor].bold(`${summary.join(" and ")} found.`);

    if (fixableErrors || fixableWarnings) {
        output += chalk[summaryColor].bold(`\n${fixablesSummary.join(" and ")} potentially fixable with the \`--fix\` option.`);
    }

    return output;
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results) {
    let errors = 0;
    let warnings = 0;
    let fixableErrors = 0;
    let fixableWarnings = 0;

    const resultsWithMessages = results.filter(result => result.messages.length > 0);

    let output = resultsWithMessages.reduce((resultsOutput, result) => {
        const messages = result.messages.map(message => `${formatMessage(message, result)}\n\n`);

        errors += result.errorCount;
        warnings += result.warningCount;
        fixableErrors += result.fixableErrorCount;
        fixableWarnings += result.fixableWarningCount;

        return resultsOutput.concat(messages);
    }, []).join("\n");

    output += "\n";
    output += formatSummary(errors, warnings, fixableErrors, fixableWarnings);

    return (errors + warnings) > 0 ? output : "";
};