diff --git a/lib/erb_lint/reporters/json_reporter.rb b/lib/erb_lint/reporters/json_reporter.rb new file mode 100644 index 00000000..c3f937f2 --- /dev/null +++ b/lib/erb_lint/reporters/json_reporter.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "json" + +module ERBLint + module Reporters + class JsonReporter < Reporter + def preview; end + + def show + puts formatted_data + end + + private + + def formatted_data + { + metadata: metadata, + files: formatted_files, + summary: summary, + }.to_json + end + + def metadata + { + erb_lint_version: ERBLint::VERSION, + ruby_engine: RUBY_ENGINE, + ruby_version: RUBY_VERSION, + ruby_patchlevel: RUBY_PATCHLEVEL.to_s, + ruby_platform: RUBY_PLATFORM, + } + end + + def summary + { + offenses: stats.found, + inspected_files: stats.processed_files.size, + corrected: stats.corrected, + } + end + + def formatted_files + processed_files.map do |filename, offenses| + { + path: filename, + offenses: formatted_offenses(offenses), + } + end + end + + def formatted_offenses(offenses) + offenses.map do |offense| + format_offense(offense) + end + end + + def format_offense(offense) + { + linter: offense.linter.class.simple_name, + message: offense.message.to_s, + location: { + start_line: offense.line_number, + start_column: offense.column, + last_line: offense.source_range.last_line, + last_column: offense.source_range.last_column, + length: offense.source_range.length, + }, + } + end + end + end +end diff --git a/spec/erb_lint/cli_spec.rb b/spec/erb_lint/cli_spec.rb index b9e3404c..281f7136 100644 --- a/spec/erb_lint/cli_spec.rb +++ b/spec/erb_lint/cli_spec.rb @@ -76,7 +76,7 @@ def run(_processed_source) it 'shows format instructions' do expect { subject }.to( - output(/Report offenses in the given format: \(compact, multiline\) \(default: multiline\)/).to_stdout + output(/Report offenses in the given format: \(compact, json, multiline\) \(default: multiline\)/).to_stdout ) end @@ -319,6 +319,7 @@ def run(_processed_source) expect { subject }.to(output(Regexp.new(Regexp.escape(<<~EOF.strip))).to_stderr) nonexistentformat: is not a valid format. Available formats: - compact + - json - multiline EOF end diff --git a/spec/erb_lint/reporters/json_reporter_spec.rb b/spec/erb_lint/reporters/json_reporter_spec.rb new file mode 100644 index 00000000..e9e84a42 --- /dev/null +++ b/spec/erb_lint/reporters/json_reporter_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ERBLint::Reporters::JsonReporter do + describe '.show' do + subject { described_class.new(stats, false).show } + + let(:stats) do + ERBLint::Stats.new( + found: 2, + processed_files: { + 'app/views/subscriptions/_loader.html.erb' => offenses, + }, + corrected: 1 + ) + end + + let(:offenses) do + [ + instance_double( + ERBLint::Offense, + message: 'Extra space detected where there should be no space.', + line_number: 1, + column: 7, + source_range: instance_double( + BetterHtml::Tokenizer::Location, + last_line: 1, + last_column: 9, + length: 2, + ), + linter: ERBLint::Linters::SpaceInHtmlTag.new(nil, ERBLint::LinterConfig.new), + ), + instance_double( + ERBLint::Offense, + message: 'Remove newline before `%>` to match start of tag.', + line_number: 52, + column: 10, + source_range: instance_double( + BetterHtml::Tokenizer::Location, + last_line: 54, + last_column: 10, + length: 10, + ), + linter: ERBLint::Linters::ClosingErbTagIndent.new(nil, ERBLint::LinterConfig.new), + ), + ] + end + + let(:expected_hash) do + { + metadata: { + erb_lint_version: ERBLint::VERSION, + ruby_engine: RUBY_ENGINE, + ruby_version: RUBY_VERSION, + ruby_patchlevel: RUBY_PATCHLEVEL.to_s, + ruby_platform: RUBY_PLATFORM, + }, + files: [{ + path: 'app/views/subscriptions/_loader.html.erb', + offenses: [ + { + linter: 'SpaceInHtmlTag', + message: 'Extra space detected where there should be no space.', + location: { + start_line: 1, + start_column: 7, + last_line: 1, + last_column: 9, + length: 2, + }, + }, + { + linter: 'ClosingErbTagIndent', + message: 'Remove newline before `%>` to match start of tag.', + location: { + start_line: 52, + start_column: 10, + last_line: 54, + last_column: 10, + length: 10, + }, + }, + ], + }], + summary: { + offenses: 2, + inspected_files: 1, + corrected: 1, + }, + } + end + + it 'displays formatted offenses output' do + expect { subject }.to(output(expected_hash.to_json + "\n").to_stdout) + end + end +end