Reporters

A reporter turns CoffeeLint's findings into output. The default reporter is designed for people reading a terminal; the others produce CSV, XML, or JSON for editors, CI servers, and code-quality dashboards. You can also write your own. This page shows the output of each built-in reporter for the same input and explains how to build a custom one.

Selecting a Reporter

coffeelint --reporter <name> src
Name Format Typical use
default Human-readable text Terminals
csv Comma-separated values Spreadsheets, simple scripts
jslint JSLint-style XML Tools that understand JSLint reports
checkstyle Checkstyle XML CI servers and quality dashboards
raw JSON Custom tooling

Every built-in reporter honors -q/--quiet by leaving out warnings. The exit code is computed independently of the reporter: it is 1 whenever any error-level finding exists. See Command-Line Usage.

The examples below lint this file, cart.coffee, with the default configuration:

class shoppingCart
  constructor: ->
    @items = []

  add: (item) ->
    @items.push(item);
    debugger

default

  ✗ cart.coffee
     ✗ #1: Class name should be UpperCamelCased. class name: shoppingCart.
     ✗ #6: Line contains a trailing semicolon.
     ⚡ #7: Found debugging code. found 'STATEMENT'.

✗ Lint! » 2 errors and 1 warning in 1 file
  • Each file gets a header line with an overall status symbol: ✓ (clean), ⚡ (warnings only), or ✗ (errors).
  • Each finding shows its symbol, the line number (or a range such as #12-48 for findings that span lines, like cyclomatic_complexity), the rule's message, and the optional context.
  • The summary starts with ✓ Ok!, ⚡ Warning!, or ✗ Lint!.
  • Colors are applied when the output is a terminal, or as forced by --color.
  • With -q, files without errors are omitted, and nothing at all is printed when there are no errors.

csv

path,lineNumber,lineNumberEnd,level,message
cart.coffee,1,,error,Class name should be UpperCamelCased class name: shoppingCart.
cart.coffee,6,,error,Line contains a trailing semicolon
cart.coffee,7,,warn,Found debugging code found 'STATEMENT'.
  • The header row is always printed.
  • lineNumberEnd is empty unless the finding spans several lines.
  • The context, when present, is appended to the message.

Warning: Fields are not quoted or escaped. A message or path that contains a comma will shift the columns, so use raw or one of the XML formats when you need robust parsing.

jslint

<?xml version="1.0" encoding="utf-8"?><jslint>
<file name="cart.coffee">
<issue line="1"
        lineEnd="1"
        reason="[error] Class name should be UpperCamelCased"
        evidence="class name: shoppingCart"/>
<issue line="6"
        lineEnd="6"
        reason="[error] Line contains a trailing semicolon"
        evidence="undefined"/>
<issue line="7"
        lineEnd="7"
        reason="[warn] Found debugging code"
        evidence="found &apos;STATEMENT&apos;"/>
</file>
</jslint>
  • Only files with findings get a <file> element.
  • lineEnd falls back to line when the finding does not span lines.
  • The level is embedded in reason as a [error] or [warn] prefix.
  • The context goes into evidence. When a finding has no context, evidence contains the literal text undefined.
  • XML special characters (&, ", <, >, ') are escaped.

checkstyle

<?xml version="1.0" encoding="utf-8"?>
<checkstyle version="4.3">
<file name="cart.coffee">
<error line="1"
    severity="error"
    message="Class name should be UpperCamelCased; context: class name: shoppingCart"
    source="coffeelint"/>
<error line="6"
    severity="error"
    message="Line contains a trailing semicolon; context: "
    source="coffeelint"/>
<error line="7"
    severity="warning"
    message="Found debugging code; context: found &apos;STATEMENT&apos;"
    source="coffeelint"/>
</file>
</checkstyle>
  • The format declares Checkstyle version 4.3, which most CI plugins accept.
  • warn is translated to Checkstyle's warning severity.
  • The message always has the form <message>; context: <context>, with an empty context when there is none.
  • source is always coffeelint.

raw

The raw reporter prints a JSON object mapping each path to its array of findings, indented with two spaces. Each finding is the complete error object CoffeeLint produced (see API Reference), including the rule's name, level, message, description, and options. For example (the description is shortened here):

{
  "cart.coffee": [
    {
      "name": "no_trailing_semicolons",
      "level": "error",
      "message": "Line contains a trailing semicolon",
      "description": "This rule prohibits trailing semicolons...",
      "lineNumber": 6,
      "rule": "no_trailing_semicolons"
    }
  ]
}

Findings from token-based rules also include a line property with the source line. Because raw is valid JSON and includes everything, it is the best starting point for custom tooling.

Writing a Custom Reporter

A reporter is a class (or constructor function) that CoffeeLint instantiates with new and then calls publish() on:

new Reporter(errorReport, { colorize: Boolean, quiet: Boolean })
reporter.publish()
  • errorReport is an ErrorReport instance. Its paths property maps each linted path to an array of error objects, and it offers helpers such as getSummary(), pathHasError(path), and hasError(). See the API Reference.
  • colorize reflects --color and whether the output is a terminal.
  • quiet is true when -q was given; respect it by skipping warnings.

Example: A One-Line Summary (CoffeeScript)

# summary_reporter.coffee
module.exports = class SummaryReporter

  constructor: (@errorReport, options = {}) ->
    { @quiet } = options

  publish: ->
    { errorCount, warningCount, pathCount } = @errorReport.getSummary()
    # coffeelint: disable=no_debugger
    console.log "#{pathCount} files: #{errorCount} errors, #{warningCount} warnings"
    # coffeelint: enable=no_debugger
coffeelint --reporter ./summary_reporter.coffee src

Example: CI Annotations (JavaScript)

This reporter prints one line per finding in the ::error file=...,line=...::message format that GitHub Actions turns into inline annotations:

// annotations-reporter.js
class AnnotationsReporter {
  constructor(errorReport, options) {
    this.errorReport = errorReport;
    this.quiet = Boolean(options && options.quiet);
  }

  publish() {
    const paths = this.errorReport.paths;
    for (const path of Object.keys(paths)) {
      for (const e of paths[path]) {
        if (this.quiet && e.level !== 'error') continue;
        const kind = e.level === 'error' ? 'error' : 'warning';
        const detail = e.context ? `${e.message} (${e.context})` : e.message;
        console.log(`::${kind} file=${path},line=${e.lineNumber}::[${e.rule}] ${detail}`);
      }
    }
  }
}

module.exports = AnnotationsReporter;
coffeelint --reporter ./annotations-reporter.js src

How Reporter Names Are Resolved

If the --reporter value is not a built-in name, CoffeeLint resolves it from the current working directory, trying the extensions .js, .coffee, .litcoffee, and .coffee.md. This works for both relative paths (./reporters/mine) and the names of reporter packages installed in your project. If resolution fails, the value is passed to require as-is. Since the command-line tool registers the CoffeeScript compiler, reporters written in CoffeeScript load without a build step.

Best Practices

  • Use checkstyle for CI dashboards and raw for scripts. Avoid parsing the default output, which is meant for people and may contain color codes.
  • Redirect machine-readable output to a file (> report.xml) so that logs and reports stay separate.
  • Combine -q with any reporter to focus CI output on blocking problems.