liquid_lint 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.md +1 -0
- data/bin/liquid-lint +7 -0
- data/config/default.yml +99 -0
- data/lib/liquid_lint/atom.rb +98 -0
- data/lib/liquid_lint/capture_map.rb +19 -0
- data/lib/liquid_lint/cli.rb +163 -0
- data/lib/liquid_lint/configuration.rb +109 -0
- data/lib/liquid_lint/configuration_loader.rb +86 -0
- data/lib/liquid_lint/constants.rb +10 -0
- data/lib/liquid_lint/document.rb +76 -0
- data/lib/liquid_lint/engine.rb +45 -0
- data/lib/liquid_lint/exceptions.rb +20 -0
- data/lib/liquid_lint/file_finder.rb +88 -0
- data/lib/liquid_lint/filters/attribute_processor.rb +31 -0
- data/lib/liquid_lint/filters/control_processor.rb +47 -0
- data/lib/liquid_lint/filters/inject_line_numbers.rb +43 -0
- data/lib/liquid_lint/filters/sexp_converter.rb +17 -0
- data/lib/liquid_lint/filters/splat_processor.rb +15 -0
- data/lib/liquid_lint/lint.rb +43 -0
- data/lib/liquid_lint/linter/comment_control_statement.rb +22 -0
- data/lib/liquid_lint/linter/consecutive_control_statements.rb +26 -0
- data/lib/liquid_lint/linter/control_statement_spacing.rb +24 -0
- data/lib/liquid_lint/linter/embedded_engines.rb +22 -0
- data/lib/liquid_lint/linter/empty_control_statement.rb +15 -0
- data/lib/liquid_lint/linter/empty_lines.rb +26 -0
- data/lib/liquid_lint/linter/file_length.rb +20 -0
- data/lib/liquid_lint/linter/line_length.rb +21 -0
- data/lib/liquid_lint/linter/redundant_div.rb +22 -0
- data/lib/liquid_lint/linter/rubocop.rb +116 -0
- data/lib/liquid_lint/linter/tab.rb +19 -0
- data/lib/liquid_lint/linter/tag_case.rb +15 -0
- data/lib/liquid_lint/linter/trailing_blank_lines.rb +21 -0
- data/lib/liquid_lint/linter/trailing_whitespace.rb +19 -0
- data/lib/liquid_lint/linter/zwsp.rb +18 -0
- data/lib/liquid_lint/linter.rb +93 -0
- data/lib/liquid_lint/linter_registry.rb +39 -0
- data/lib/liquid_lint/linter_selector.rb +79 -0
- data/lib/liquid_lint/logger.rb +103 -0
- data/lib/liquid_lint/matcher/anything.rb +11 -0
- data/lib/liquid_lint/matcher/base.rb +21 -0
- data/lib/liquid_lint/matcher/capture.rb +32 -0
- data/lib/liquid_lint/matcher/nothing.rb +13 -0
- data/lib/liquid_lint/options.rb +110 -0
- data/lib/liquid_lint/rake_task.rb +125 -0
- data/lib/liquid_lint/report.rb +25 -0
- data/lib/liquid_lint/reporter/checkstyle_reporter.rb +42 -0
- data/lib/liquid_lint/reporter/default_reporter.rb +41 -0
- data/lib/liquid_lint/reporter/emacs_reporter.rb +44 -0
- data/lib/liquid_lint/reporter/json_reporter.rb +52 -0
- data/lib/liquid_lint/reporter.rb +44 -0
- data/lib/liquid_lint/ruby_extract_engine.rb +36 -0
- data/lib/liquid_lint/ruby_extractor.rb +106 -0
- data/lib/liquid_lint/ruby_parser.rb +40 -0
- data/lib/liquid_lint/runner.rb +82 -0
- data/lib/liquid_lint/sexp.rb +106 -0
- data/lib/liquid_lint/sexp_visitor.rb +146 -0
- data/lib/liquid_lint/utils.rb +85 -0
- data/lib/liquid_lint/version.rb +6 -0
- data/lib/liquid_lint.rb +52 -0
- metadata +185 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# Searches for tab indentation
|
5
|
+
class Linter::Tab < Linter
|
6
|
+
include LinterRegistry
|
7
|
+
|
8
|
+
MSG = 'Tab detected'
|
9
|
+
|
10
|
+
on_start do |_sexp|
|
11
|
+
dummy_node = Struct.new(:line)
|
12
|
+
document.source_lines.each_with_index do |line, index|
|
13
|
+
next unless line =~ /^( *)[\t ]*\t/
|
14
|
+
|
15
|
+
report_lint(dummy_node.new(index + 1), MSG)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# Searches for tags with uppercase characters.
|
5
|
+
class Linter::TagCase < Linter
|
6
|
+
include LinterRegistry
|
7
|
+
|
8
|
+
on [:html, :tag] do |sexp|
|
9
|
+
_, _, name = sexp
|
10
|
+
next unless name[/[A-Z]/]
|
11
|
+
|
12
|
+
report_lint(sexp, "Tag `#{name}` should be written as `#{name.downcase}`")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# This linter looks for trailing blank lines and a final newline.
|
5
|
+
class Linter::TrailingBlankLines < Linter
|
6
|
+
include LinterRegistry
|
7
|
+
|
8
|
+
on_start do |_sexp|
|
9
|
+
dummy_node = Struct.new(:line)
|
10
|
+
next if document.source.empty?
|
11
|
+
|
12
|
+
if !document.source.end_with?("\n")
|
13
|
+
report_lint(dummy_node.new(document.source_lines.size),
|
14
|
+
'No blank line in the end of file')
|
15
|
+
elsif document.source.lines.last.blank?
|
16
|
+
report_lint(dummy_node.new(document.source.lines.size),
|
17
|
+
'Multiple empty lines in the end of file')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# Checks for trailing whitespace.
|
5
|
+
class Linter::TrailingWhitespace < Linter
|
6
|
+
include LinterRegistry
|
7
|
+
|
8
|
+
on_start do |_sexp|
|
9
|
+
dummy_node = Struct.new(:line)
|
10
|
+
|
11
|
+
document.source_lines.each_with_index do |line, index|
|
12
|
+
next unless line =~ /\s+$/
|
13
|
+
|
14
|
+
report_lint(dummy_node.new(index + 1),
|
15
|
+
'Line contains trailing whitespace')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
class Linter::Zwsp < Linter
|
5
|
+
include LinterRegistry
|
6
|
+
|
7
|
+
MSG = 'Remove zero-width space'
|
8
|
+
|
9
|
+
on_start do |_sexp|
|
10
|
+
dummy_node = Struct.new(:line)
|
11
|
+
document.source_lines.each_with_index do |line, index|
|
12
|
+
next unless line.include?("\u200b")
|
13
|
+
|
14
|
+
report_lint(dummy_node.new(index + 1), MSG)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# Base implementation for all lint checks.
|
5
|
+
#
|
6
|
+
# @abstract
|
7
|
+
class Linter
|
8
|
+
# Include definitions for Sexp pattern-matching helpers.
|
9
|
+
include SexpVisitor
|
10
|
+
extend SexpVisitor::DSL
|
11
|
+
|
12
|
+
# List of lints reported by this linter.
|
13
|
+
#
|
14
|
+
# @todo Remove once spec/support/shared_linter_context returns an array of
|
15
|
+
# lints for the subject instead of the linter itself.
|
16
|
+
attr_reader :lints
|
17
|
+
|
18
|
+
# Initializes a linter with the specified configuration.
|
19
|
+
#
|
20
|
+
# @param config [Hash] configuration for this linter
|
21
|
+
def initialize(config)
|
22
|
+
@config = config
|
23
|
+
@lints = []
|
24
|
+
end
|
25
|
+
|
26
|
+
# Runs the linter against the given Liquid document.
|
27
|
+
#
|
28
|
+
# @param document [LiquidLint::Document]
|
29
|
+
def run(document)
|
30
|
+
@document = document
|
31
|
+
@lints = []
|
32
|
+
@disabled_lines = nil
|
33
|
+
trigger_pattern_callbacks(document.sexp)
|
34
|
+
@lints
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the simple name for this linter.
|
38
|
+
#
|
39
|
+
# @return [String]
|
40
|
+
def name
|
41
|
+
self.class.name.split('::').last
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :config, :document
|
47
|
+
|
48
|
+
# Record a lint for reporting back to the user.
|
49
|
+
#
|
50
|
+
# @param node [#line] node to extract the line number from
|
51
|
+
# @param message [String] error/warning to display to the user
|
52
|
+
def report_lint(node, message)
|
53
|
+
return if disabled_for_line?(node.line)
|
54
|
+
|
55
|
+
@lints << LiquidLint::Lint.new(self, @document.file, node.line, message)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Parse Ruby code into an abstract syntax tree.
|
59
|
+
#
|
60
|
+
# @param source [String] Ruby code to parse
|
61
|
+
# @return [AST::Node]
|
62
|
+
def parse_ruby(source)
|
63
|
+
@ruby_parser ||= LiquidLint::RubyParser.new
|
64
|
+
@ruby_parser.parse(source)
|
65
|
+
end
|
66
|
+
|
67
|
+
def disabled_for_line?(line)
|
68
|
+
disabled_lines.include?(line)
|
69
|
+
end
|
70
|
+
|
71
|
+
def disabled_lines
|
72
|
+
@disabled_lines ||= begin
|
73
|
+
currently_disabled = false
|
74
|
+
@document.source_lines.each_with_index.each_with_object([]) do |pair, lines|
|
75
|
+
line = pair[0]
|
76
|
+
line_number = pair[1] + 1
|
77
|
+
|
78
|
+
if line =~ %r{/ liquid-lint:disable #{linter_name}}
|
79
|
+
currently_disabled = true
|
80
|
+
elsif line =~ %r{/ liquid-lint:enable #{linter_name}}
|
81
|
+
currently_disabled = false
|
82
|
+
elsif currently_disabled
|
83
|
+
lines << line_number
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def linter_name
|
90
|
+
@linter_name ||= self.class.name.split('::').last
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
class NoSuchLinter < StandardError; end
|
5
|
+
|
6
|
+
# Stores all defined linters.
|
7
|
+
module LinterRegistry
|
8
|
+
@linters = []
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# List of all registered linters.
|
12
|
+
attr_reader :linters
|
13
|
+
|
14
|
+
# Executed when a linter includes the {LinterRegistry} module.
|
15
|
+
#
|
16
|
+
# This results in the linter being registered with the registry.
|
17
|
+
#
|
18
|
+
# @param subclass [Class]
|
19
|
+
def included(subclass)
|
20
|
+
@linters << subclass
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return a list of {LiquidLint::Linter} {Class}es corresponding to the
|
24
|
+
# specified list of names.
|
25
|
+
#
|
26
|
+
# @param linter_names [Array<String>]
|
27
|
+
# @return [Array<Class>]
|
28
|
+
def extract_linters_from(linter_names)
|
29
|
+
linter_names.map do |linter_name|
|
30
|
+
begin
|
31
|
+
LiquidLint::Linter.const_get(linter_name)
|
32
|
+
rescue NameError
|
33
|
+
raise NoSuchLinter, "Linter #{linter_name} does not exist"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# Chooses the appropriate linters to run given the specified configuration.
|
5
|
+
class LinterSelector
|
6
|
+
# Creates a selector using the given configuration and additional options.
|
7
|
+
#
|
8
|
+
# @param config [LiquidLint::Configuration]
|
9
|
+
# @param options [Hash]
|
10
|
+
def initialize(config, options)
|
11
|
+
@config = config
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the set of linters to run against the given file.
|
16
|
+
#
|
17
|
+
# @param file [String]
|
18
|
+
# @raise [LiquidLint::Exceptions::NoLintersError] when no linters are enabled
|
19
|
+
# @return [Array<LiquidLint::Linter>]
|
20
|
+
def linters_for_file(file)
|
21
|
+
@linters ||= extract_enabled_linters(@config, @options)
|
22
|
+
@linters.select { |linter| run_linter_on_file?(@config, linter, file) }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Returns a list of linters that are enabled given the specified
|
28
|
+
# configuration and additional options.
|
29
|
+
#
|
30
|
+
# @param config [LiquidLint::Configuration]
|
31
|
+
# @param options [Hash]
|
32
|
+
# @return [Array<LiquidLint::Linter>]
|
33
|
+
def extract_enabled_linters(config, options)
|
34
|
+
included_linters =
|
35
|
+
LinterRegistry.extract_linters_from(options.fetch(:included_linters, []))
|
36
|
+
|
37
|
+
included_linters = LinterRegistry.linters if included_linters.empty?
|
38
|
+
|
39
|
+
excluded_linters =
|
40
|
+
LinterRegistry.extract_linters_from(options.fetch(:excluded_linters, []))
|
41
|
+
|
42
|
+
# After filtering out explicitly included/excluded linters, only include
|
43
|
+
# linters which are enabled in the configuration
|
44
|
+
linters = (included_linters - excluded_linters).map do |linter_class|
|
45
|
+
linter_config = config.for_linter(linter_class)
|
46
|
+
linter_class.new(linter_config) if linter_config['enabled']
|
47
|
+
end.compact
|
48
|
+
|
49
|
+
# Highlight condition where all linters were filtered out, as this was
|
50
|
+
# likely a mistake on the user's part
|
51
|
+
if linters.empty?
|
52
|
+
raise LiquidLint::Exceptions::NoLintersError, 'No linters specified'
|
53
|
+
end
|
54
|
+
|
55
|
+
linters
|
56
|
+
end
|
57
|
+
|
58
|
+
# Whether to run the given linter against the specified file.
|
59
|
+
#
|
60
|
+
# @param config [LiquidLint::Configuration]
|
61
|
+
# @param linter [LiquidLint::Linter]
|
62
|
+
# @param file [String]
|
63
|
+
# @return [Boolean]
|
64
|
+
def run_linter_on_file?(config, linter, file)
|
65
|
+
linter_config = config.for_linter(linter)
|
66
|
+
|
67
|
+
if linter_config['include'].any? &&
|
68
|
+
!LiquidLint::Utils.any_glob_matches?(linter_config['include'], file)
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
if LiquidLint::Utils.any_glob_matches?(linter_config['exclude'], file)
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint
|
4
|
+
# Encapsulates all communication to an output source.
|
5
|
+
class Logger
|
6
|
+
# Whether colored output via ANSI escape sequences is enabled.
|
7
|
+
# @return [true,false]
|
8
|
+
attr_accessor :color_enabled
|
9
|
+
|
10
|
+
# Creates a logger which outputs nothing.
|
11
|
+
# @return [LiquidLint::Logger]
|
12
|
+
def self.silent
|
13
|
+
new(File.open('/dev/null', 'w'))
|
14
|
+
end
|
15
|
+
|
16
|
+
# Creates a new {LiquidLint::Logger} instance.
|
17
|
+
#
|
18
|
+
# @param out [IO] the output destination.
|
19
|
+
def initialize(out)
|
20
|
+
@out = out
|
21
|
+
end
|
22
|
+
|
23
|
+
# Print the specified output.
|
24
|
+
#
|
25
|
+
# @param output [String] the output to send
|
26
|
+
# @param newline [true,false] whether to append a newline
|
27
|
+
def log(output, newline = true)
|
28
|
+
@out.print(output)
|
29
|
+
@out.print("\n") if newline
|
30
|
+
end
|
31
|
+
|
32
|
+
# Print the specified output in bold face.
|
33
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
34
|
+
#
|
35
|
+
# @param args [Array<String>]
|
36
|
+
def bold(*args)
|
37
|
+
color('1', *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Print the specified output in a color indicative of error.
|
41
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
42
|
+
#
|
43
|
+
# @param args [Array<String>]
|
44
|
+
def error(*args)
|
45
|
+
color(31, *args)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Print the specified output in a bold face and color indicative of error.
|
49
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
50
|
+
#
|
51
|
+
# @param args [Array<String>]
|
52
|
+
def bold_error(*args)
|
53
|
+
color('1;31', *args)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Print the specified output in a color indicative of success.
|
57
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
58
|
+
#
|
59
|
+
# @param args [Array<String>]
|
60
|
+
def success(*args)
|
61
|
+
color(32, *args)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Print the specified output in a color indicative of a warning.
|
65
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
66
|
+
#
|
67
|
+
# @param args [Array<String>]
|
68
|
+
def warning(*args)
|
69
|
+
color(33, *args)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Print the specified output in a color indicating information.
|
73
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
74
|
+
#
|
75
|
+
# @param args [Array<String>]
|
76
|
+
def info(*args)
|
77
|
+
color(36, *args)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Print a blank line.
|
81
|
+
def newline
|
82
|
+
log('')
|
83
|
+
end
|
84
|
+
|
85
|
+
# Whether this logger is outputting to a TTY.
|
86
|
+
#
|
87
|
+
# @return [true,false]
|
88
|
+
def tty?
|
89
|
+
@out.respond_to?(:tty?) && @out.tty?
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# Print output in the specified color.
|
95
|
+
#
|
96
|
+
# @param code [Integer,String] ANSI color code
|
97
|
+
# @param output [String] output to print
|
98
|
+
# @param newline [Boolean] whether to append a newline
|
99
|
+
def color(code, output, newline = true)
|
100
|
+
log(color_enabled ? "\033[#{code}m#{output}\033[0m" : output, newline)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint::Matcher
|
4
|
+
# Represents a Sexp pattern implementing complex matching logic.
|
5
|
+
#
|
6
|
+
# Subclasses can implement custom logic to create complex matches that can be
|
7
|
+
# reused across linters, DRYing up matching code.
|
8
|
+
#
|
9
|
+
# @abstract
|
10
|
+
class Base
|
11
|
+
# Whether this matcher matches the specified object.
|
12
|
+
#
|
13
|
+
# This must be implemented by subclasses.
|
14
|
+
#
|
15
|
+
# @param other [Object]
|
16
|
+
# @return [Boolean]
|
17
|
+
def match?(*)
|
18
|
+
raise NotImplementedError, 'Matcher must implement `match?`'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LiquidLint::Matcher
|
4
|
+
# Wraps a matcher, taking on the behavior of the wrapped matcher but storing
|
5
|
+
# the value that matched so it can be referred to later.
|
6
|
+
class Capture < Base
|
7
|
+
# @return [LiquidLint::Matcher::Base] matcher that this capture wraps
|
8
|
+
attr_accessor :matcher
|
9
|
+
|
10
|
+
# @return [Object] value that was captured
|
11
|
+
attr_accessor :value
|
12
|
+
|
13
|
+
# Creates a capture that wraps that given matcher.
|
14
|
+
#
|
15
|
+
# @param matcher [LiquidLint::Matcher::Base]
|
16
|
+
# @return [LiquidLint::Matcher::Capture]
|
17
|
+
def self.from_matcher(matcher)
|
18
|
+
new.tap do |cap_matcher|
|
19
|
+
cap_matcher.matcher = matcher
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see {LiquidLint::Matcher::Base#match?}
|
24
|
+
def match?(object)
|
25
|
+
if result = @matcher.match?(object)
|
26
|
+
@value = object
|
27
|
+
end
|
28
|
+
|
29
|
+
result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module LiquidLint
|
6
|
+
# Handles option parsing for the command line application.
|
7
|
+
class Options
|
8
|
+
# Parses command line options into an options hash.
|
9
|
+
#
|
10
|
+
# @param args [Array<String>] arguments passed via the command line
|
11
|
+
# @return [Hash] parsed options
|
12
|
+
def parse(args)
|
13
|
+
@options = {}
|
14
|
+
|
15
|
+
OptionParser.new do |parser|
|
16
|
+
parser.banner = "Usage: #{APP_NAME} [options] [file1, file2, ...]"
|
17
|
+
|
18
|
+
add_linter_options parser
|
19
|
+
add_file_options parser
|
20
|
+
add_info_options parser
|
21
|
+
end.parse!(args)
|
22
|
+
|
23
|
+
# Any remaining arguments are assumed to be files
|
24
|
+
@options[:files] = args
|
25
|
+
|
26
|
+
@options
|
27
|
+
rescue OptionParser::InvalidOption => e
|
28
|
+
raise Exceptions::InvalidCLIOption,
|
29
|
+
e.message,
|
30
|
+
e.backtrace
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Register linter-related flags.
|
36
|
+
def add_linter_options(parser)
|
37
|
+
parser.on('-i', '--include-linter linter,...', Array,
|
38
|
+
'Specify which linters you want to run') do |linters|
|
39
|
+
@options[:included_linters] = linters
|
40
|
+
end
|
41
|
+
|
42
|
+
parser.on('-x', '--exclude-linter linter,...', Array,
|
43
|
+
"Specify which linters you don't want to run") do |linters|
|
44
|
+
@options[:excluded_linters] = linters
|
45
|
+
end
|
46
|
+
|
47
|
+
parser.on('-r', '--reporter reporter', String,
|
48
|
+
'Specify which reporter you want to use to generate the output') do |reporter|
|
49
|
+
@options[:reporter] = load_reporter_class(reporter.capitalize)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the class of the specified Reporter.
|
54
|
+
#
|
55
|
+
# @param reporter_name [String]
|
56
|
+
# @raise [LiquidLint::Exceptions::InvalidCLIOption] if reporter doesn't exist
|
57
|
+
# @return [Class]
|
58
|
+
def load_reporter_class(reporter_name)
|
59
|
+
LiquidLint::Reporter.const_get("#{reporter_name}Reporter")
|
60
|
+
rescue NameError
|
61
|
+
raise LiquidLint::Exceptions::InvalidCLIOption,
|
62
|
+
"#{reporter_name}Reporter does not exist"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Register file-related flags.
|
66
|
+
def add_file_options(parser)
|
67
|
+
parser.on('-c', '--config config-file', String,
|
68
|
+
'Specify which configuration file you want to use') do |conf_file|
|
69
|
+
@options[:config_file] = conf_file
|
70
|
+
end
|
71
|
+
|
72
|
+
parser.on('-e', '--exclude file,...', Array,
|
73
|
+
'List of file names to exclude') do |files|
|
74
|
+
@options[:excluded_files] = files
|
75
|
+
end
|
76
|
+
|
77
|
+
parser.on('--stdin-file-path file', String,
|
78
|
+
'Pipe source from STDIN, using file in offense reports.') do |file|
|
79
|
+
@options[:stdin_file_path] = file
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Register informational flags.
|
84
|
+
def add_info_options(parser)
|
85
|
+
parser.on('--show-linters', 'Display available linters') do
|
86
|
+
@options[:show_linters] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
parser.on('--show-reporters', 'Display available reporters') do
|
90
|
+
@options[:show_reporters] = true
|
91
|
+
end
|
92
|
+
|
93
|
+
parser.on('--[no-]color', 'Force output to be colorized') do |color|
|
94
|
+
@options[:color] = color
|
95
|
+
end
|
96
|
+
|
97
|
+
parser.on_tail('-h', '--help', 'Display help documentation') do
|
98
|
+
@options[:help] = parser.help
|
99
|
+
end
|
100
|
+
|
101
|
+
parser.on_tail('-v', '--version', 'Display version') do
|
102
|
+
@options[:version] = true
|
103
|
+
end
|
104
|
+
|
105
|
+
parser.on_tail('-V', '--verbose-version', 'Display verbose version information') do
|
106
|
+
@options[:verbose_version] = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|