lib/scss_lint/cli.rb in scss-lint-0.30.0 vs lib/scss_lint/cli.rb in scss-lint-0.31.0

- old
+ new

@@ -1,9 +1,9 @@ require 'find' -require 'optparse' require 'rainbow' require 'rainbow/ext/string' +require 'scss_lint/options' module SCSSLint # Responsible for parsing command-line options and executing the appropriate # application logic based on the options specified. class CLI @@ -18,172 +18,126 @@ no_input: 66, # Input file did not exist or was not readable software: 70, # Internal software error config: 78, # Configuration error } - DEFAULT_REPORTER = [SCSSLint::Reporter::DefaultReporter, :stdout] - - # @param args [Array] - def initialize(args = []) - @args = args - @options = { reporters: [DEFAULT_REPORTER] } - @config = Config.default + def run(args) + options = SCSSLint::Options.new.parse(args) + act_on_options(options) + rescue => ex + handle_runtime_exception(ex) end - def parse_arguments - begin - options_parser.parse!(@args) + private - # Take the rest of the arguments as files/directories + def act_on_options(options) + load_required_paths(options) - if @args.empty? - @options[:files] = @config.scss_files - else - @options[:files] = @args - end - - rescue OptionParser::InvalidOption => ex - print_help options_parser.help, ex + if options[:help] + print_help(options) + elsif options[:version] + print_version + elsif options[:show_linters] + print_linters + elsif options[:show_formatters] + print_formatters + else + config = setup_configuration(options) + scan_for_lints(options, config) end - - begin - setup_configuration - rescue InvalidConfiguration, NoSuchLinter => ex - puts ex.message - halt :config - end end - # @return [OptionParser] - def options_parser # rubocop:disable MethodLength - @options_parser ||= OptionParser.new do |opts| - opts.banner = "Usage: #{opts.program_name} [options] [scss-files]" + def scan_for_lints(options, config) + runner = Runner.new(config) + runner.run(files_to_lint(options, config)) + report_lints(options, runner.lints) - opts.separator '' - opts.separator 'Common options:' - - opts.on('-c', '--config file', 'Specify configuration file', String) do |file| - @options[:config_file] = file - end - - opts.on('-e', '--exclude file,...', Array, - 'List of file names to exclude') do |files| - @options[:excluded_files] = files - end - - opts.on('-f', '--format Formatter', 'Specify how to display lints', String) do |format| - define_output_format(format) - end - - opts.on('-o', '--out path', 'Write output to a file instead of STDOUT', String) do |path| - define_output_path(path) - end - - opts.on('-r', '--require path', 'Require Ruby file', String) do |path| - require path - end - - opts.on_tail('--show-formatters', 'Shows available formatters') do - print_formatters - end - - opts.on('-i', '--include-linter linter,...', Array, - 'Specify which linters you want to run') do |linters| - @options[:included_linters] = linters - end - - opts.on('-x', '--exclude-linter linter,...', Array, - "Specify which linters you don't want to run") do |linters| - @options[:excluded_linters] = linters - end - - opts.on_tail('--show-linters', 'Shows available linters') do - print_linters - end - - opts.on_tail('-h', '--help', 'Show this message') do - print_help opts.help - end - - opts.on_tail('-v', '--version', 'Show version') do - print_version opts.program_name, VERSION - end - end - end - - def run # rubocop:disable MethodLength - runner = Runner.new(@config) - runner.run(files_to_lint) - report_lints(runner.lints) - if runner.lints.any?(&:error?) halt :error elsif runner.lints.any? halt :warning + else + halt :ok end - rescue InvalidConfiguration => ex - puts ex - halt :config - rescue NoFilesError, Errno::ENOENT => ex - puts ex.message - halt :no_input - rescue NoSuchLinter => ex - puts ex.message - halt :usage - rescue => ex - puts ex.message - puts ex.backtrace - puts 'Report this bug at '.color(:yellow) + BUG_REPORT_URL.color(:cyan) - halt :software end - private - - def setup_configuration - if @options[:config_file] - @config = Config.load(@options[:config_file]) - @config.preferred = true + def handle_runtime_exception(exception) # rubocop:disable Metrics/AbcSize + case exception + when SCSSLint::Exceptions::InvalidCLIOption + puts exception.message + puts 'Run `scss-lint --help` for usage documentation' + halt :usage + when SCSSLint::Exceptions::InvalidConfiguration + puts exception.message + halt :config + when NoFilesError, Errno::ENOENT + puts exception.message + halt :no_input + when NoSuchLinter + puts exception.message + halt :usage + else + puts exception.message + puts exception.backtrace + puts 'Report this bug at '.color(:yellow) + BUG_REPORT_URL.color(:cyan) + halt :software end + end - merge_command_line_flags_with_config(@config) + def setup_configuration(options) + config = + if options[:config_file] + Config.load(options[:config_file]).tap do |conf| + conf.preferred = true + end + else + Config.default + end + + merge_options_with_config(options, config) end + # @param options [Hash] # @param config [Config] # @return [Config] - def merge_command_line_flags_with_config(config) - if @options[:excluded_files] - @options[:excluded_files].each do |file| + def merge_options_with_config(options, config) + if options[:excluded_files] + options[:excluded_files].each do |file| config.exclude_file(file) end end - if @options[:included_linters] + if options[:included_linters] config.disable_all_linters - LinterRegistry.extract_linters_from(@options[:included_linters]).each do |linter| + LinterRegistry.extract_linters_from(options[:included_linters]).each do |linter| config.enable_linter(linter) end end - if @options[:excluded_linters] - LinterRegistry.extract_linters_from(@options[:excluded_linters]).each do |linter| + if options[:excluded_linters] + LinterRegistry.extract_linters_from(options[:excluded_linters]).each do |linter| config.disable_linter(linter) end end config end - def files_to_lint - extract_files_from(@options[:files]).reject do |file| - config = - if !@config.preferred && (config_for_file = Config.for_file(file)) - merge_command_line_flags_with_config(config_for_file.dup) + def files_to_lint(options, config) + if options[:files].empty? + options[:files] = config.scss_files + end + + extract_files_from(options[:files]).reject do |file| + actual_config = + if !config.preferred && (config_for_file = Config.for_file(file)) + merge_options_with_config(options, config_for_file.dup) else - @config + config end - config.excluded_file?(file) + actual_config.excluded_file?(file) end end # @param list [Array] def extract_files_from(list) @@ -203,38 +157,27 @@ return false unless FileTest.file?(file) VALID_EXTENSIONS.include?(File.extname(file)) end + # @param options [Hash] # @param lints [Array<Lint>] - def report_lints(lints) + def report_lints(options, lints) sorted_lints = lints.sort_by { |l| [l.filename, l.location] } - @options.fetch(:reporters).each do |reporter, output| + options.fetch(:reporters).each do |reporter, output| results = reporter.new(sorted_lints).report_lints io = (output == :stdout ? $stdout : File.new(output, 'w+')) io.print results if results end end - # @param format [String] - def define_output_format(format) - unless @options[:reporters] == [DEFAULT_REPORTER] && format == 'Default' - @options[:reporters].reject! { |i| i == DEFAULT_REPORTER } - reporter = SCSSLint::Reporter.const_get(format + 'Reporter') - @options[:reporters] << [reporter, :stdout] + def load_required_paths(options) + Array(options[:required_paths]).each do |path| + require path end - rescue NameError - puts "Invalid output format specified: #{format}" - halt :config end - # @param path [String] - def define_output_path(path) - last_reporter, _output = @options[:reporters].pop - @options[:reporters] << [last_reporter, path] - end - def print_formatters puts 'Installed formatters:' reporter_names = SCSSLint::Reporter.descendants.map do |reporter| reporter.name.split('::').last.split('Reporter').first @@ -259,27 +202,24 @@ end halt end - # @param help_message [String] - # @param err [Exception] - def print_help(help_message, err = nil) - puts err, '' if err - puts help_message - halt(err ? :usage : :ok) + # @param options [Hash] + def print_help(options) + puts options[:help] + halt :ok end - # @param program_name [String] - # @param version [String] - def print_version(program_name, version) - puts "#{program_name} #{version}" - halt + # @param options [Hash] + def print_version + puts "scss-lint #{SCSSLint::VERSION}" + halt :ok end # Used for ease-of testing # @param exit_status [Symbol] def halt(exit_status = :ok) - exit(EXIT_CODES[exit_status]) + EXIT_CODES[exit_status] end end end