scss-lint 0.12.1 → 0.13.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 +4 -4
- data/config/default.yml +77 -0
- data/lib/scss_lint.rb +1 -0
- data/lib/scss_lint/cli.rb +94 -29
- data/lib/scss_lint/config.rb +223 -0
- data/lib/scss_lint/constants.rb +2 -0
- data/lib/scss_lint/linter.rb +4 -2
- data/lib/scss_lint/linter/indentation.rb +3 -6
- data/lib/scss_lint/runner.rb +12 -22
- data/lib/scss_lint/version.rb +1 -1
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 05986225943272819dc966c20d294f39f54bd7ef
         | 
| 4 | 
            +
              data.tar.gz: c5fb97b91c46be45d9a4bb72573356639e1f5afb
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b38f342599f822cb5d26617986f264e5ef2ad00ddddc10c049a57369a191706e77b38b9a97b9735daaa424703d19b93829a33f0a09b2ebac208bec52a5874eba
         | 
| 7 | 
            +
              data.tar.gz: 45ecd691b23e170805dd1e45f509fbff7891c0f32880c1eeb4ff9d4967d7b7733128d1a3dcad4f367ac44309eca8ab79884dda18fe0436654c95d985a4f639b0
         | 
    
        data/config/default.yml
    ADDED
    
    | @@ -0,0 +1,77 @@ | |
| 1 | 
            +
            # Default application configuration that all configurations inherit from.
         | 
| 2 | 
            +
            linters:
         | 
| 3 | 
            +
              BorderZero:
         | 
| 4 | 
            +
                enabled: true
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              CapitalizationInSelector:
         | 
| 7 | 
            +
                enabled: true
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              ColorKeyword:
         | 
| 10 | 
            +
                enabled: true
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              Comment:
         | 
| 13 | 
            +
                enabled: true
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              DebugStatement:
         | 
| 16 | 
            +
                enabled: true
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              DeclarationOrder:
         | 
| 19 | 
            +
                enabled: true
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              DeclaredName:
         | 
| 22 | 
            +
                enabled: true
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              DuplicateProperty:
         | 
| 25 | 
            +
                enabled: true
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              EmptyRule:
         | 
| 28 | 
            +
                enabled: true
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              HexFormat:
         | 
| 31 | 
            +
                enabled: true
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              IdWithExtraneousSelector:
         | 
| 34 | 
            +
                enabled: true
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              Indentation:
         | 
| 37 | 
            +
                enabled: true
         | 
| 38 | 
            +
                width: 2
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              LeadingZero:
         | 
| 41 | 
            +
                enabled: true
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              PlaceholderInExtend:
         | 
| 44 | 
            +
                enabled: true
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              Shorthand:
         | 
| 47 | 
            +
                enabled: true
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              SingleLinePerSelector:
         | 
| 50 | 
            +
                enabled: true
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              SortedProperties:
         | 
| 53 | 
            +
                enabled: true
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              SpaceAfterComma:
         | 
| 56 | 
            +
                enabled: true
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              SpaceAfterPropertyColon:
         | 
| 59 | 
            +
                enabled: true
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              SpaceAfterPropertyName:
         | 
| 62 | 
            +
                enabled: true
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              SpaceBeforeBrace:
         | 
| 65 | 
            +
                enabled: true
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              TrailingSemicolonAfterPropertyValue:
         | 
| 68 | 
            +
                enabled: true
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              UsageName:
         | 
| 71 | 
            +
                enabled: true
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              ZeroUnit:
         | 
| 74 | 
            +
                enabled: true
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              Compass::*:
         | 
| 77 | 
            +
                enabled: false
         | 
    
        data/lib/scss_lint.rb
    CHANGED
    
    
    
        data/lib/scss_lint/cli.rb
    CHANGED
    
    | @@ -5,33 +5,66 @@ module SCSSLint | |
| 5 5 | 
             
              # Responsible for parsing command-line options and executing the appropriate
         | 
| 6 6 | 
             
              # application logic based on the options specified.
         | 
| 7 7 | 
             
              class CLI
         | 
| 8 | 
            -
                 | 
| 8 | 
            +
                attr_reader :config, :options
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                # Subset of semantic exit codes conforming to `sysexits` documentation.
         | 
| 11 | 
            +
                EXIT_CODES = {
         | 
| 12 | 
            +
                  ok: 0,
         | 
| 13 | 
            +
                  usage: 64,      # Command line usage error
         | 
| 14 | 
            +
                  data: 65,       # User input was incorrect (i.e. contains lints)
         | 
| 15 | 
            +
                  no_input: 66,   # Input file did not exist or was not readable
         | 
| 16 | 
            +
                  software: 70,   # Internal software error
         | 
| 17 | 
            +
                  config: 78,     # Configuration error
         | 
| 18 | 
            +
                }
         | 
| 9 19 |  | 
| 10 20 | 
             
                def initialize(args = [])
         | 
| 11 21 | 
             
                  @args = args
         | 
| 12 22 | 
             
                  @options = {}
         | 
| 23 | 
            +
                  @config = Config.default
         | 
| 13 24 | 
             
                end
         | 
| 14 25 |  | 
| 15 26 | 
             
                def parse_arguments
         | 
| 16 | 
            -
                   | 
| 27 | 
            +
                  begin
         | 
| 28 | 
            +
                    options_parser.parse!(@args)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    # Take the rest of the arguments as files/directories
         | 
| 31 | 
            +
                    @options[:files] = @args
         | 
| 32 | 
            +
                  rescue OptionParser::InvalidOption => ex
         | 
| 33 | 
            +
                    print_help options_parser.help, ex
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  begin
         | 
| 37 | 
            +
                    setup_configuration
         | 
| 38 | 
            +
                  rescue NoSuchLinter => ex
         | 
| 39 | 
            +
                    puts ex.message
         | 
| 40 | 
            +
                    halt :config
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def options_parser
         | 
| 45 | 
            +
                  @options_parser ||= OptionParser.new do |opts|
         | 
| 17 46 | 
             
                    opts.banner = "Usage: #{opts.program_name} [options] [scss-files]"
         | 
| 18 47 |  | 
| 19 48 | 
             
                    opts.separator ''
         | 
| 20 49 | 
             
                    opts.separator 'Common options:'
         | 
| 21 50 |  | 
| 51 | 
            +
                    opts.on('-c', '--config file', 'Specify configuration file', String) do |file|
         | 
| 52 | 
            +
                      @options[:config_file] = file
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 22 55 | 
             
                    opts.on('-e', '--exclude file,...', Array,
         | 
| 23 56 | 
             
                            'List of file names to exclude') do |files|
         | 
| 24 | 
            -
                      options[:excluded_files] = files
         | 
| 57 | 
            +
                      @options[:excluded_files] = files
         | 
| 25 58 | 
             
                    end
         | 
| 26 59 |  | 
| 27 60 | 
             
                    opts.on('-i', '--include-linter linter,...', Array,
         | 
| 28 61 | 
             
                            'Specify which linters you want to run') do |linters|
         | 
| 29 | 
            -
                      options[:included_linters] = linters
         | 
| 62 | 
            +
                      @options[:included_linters] = linters
         | 
| 30 63 | 
             
                    end
         | 
| 31 64 |  | 
| 32 65 | 
             
                    opts.on('-x', '--exclude-linter linter,...', Array,
         | 
| 33 66 | 
             
                            "Specify which linters you don't want to run") do |linters|
         | 
| 34 | 
            -
                      options[:excluded_linters] = linters
         | 
| 67 | 
            +
                      @options[:excluded_linters] = linters
         | 
| 35 68 | 
             
                    end
         | 
| 36 69 |  | 
| 37 70 | 
             
                    opts.on_tail('--show-linters', 'Shows available linters') do
         | 
| @@ -47,42 +80,73 @@ module SCSSLint | |
| 47 80 | 
             
                    end
         | 
| 48 81 |  | 
| 49 82 | 
             
                    opts.on('--xml', 'Output the results in XML format') do
         | 
| 50 | 
            -
                      options[:reporter] = SCSSLint::Reporter::XMLReporter
         | 
| 83 | 
            +
                      @options[:reporter] = SCSSLint::Reporter::XMLReporter
         | 
| 51 84 | 
             
                    end
         | 
| 52 85 | 
             
                  end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                  begin
         | 
| 55 | 
            -
                    parser.parse!(@args)
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                    # Take the rest of the arguments as files/directories
         | 
| 58 | 
            -
                    options[:files] = @args
         | 
| 59 | 
            -
                  rescue OptionParser::InvalidOption => ex
         | 
| 60 | 
            -
                    print_help parser.help, ex
         | 
| 61 | 
            -
                  end
         | 
| 62 86 | 
             
                end
         | 
| 63 87 |  | 
| 64 88 | 
             
                def run
         | 
| 65 | 
            -
                  runner = Runner.new( | 
| 66 | 
            -
                  runner.run( | 
| 89 | 
            +
                  runner = Runner.new(@config)
         | 
| 90 | 
            +
                  runner.run(files_to_lint)
         | 
| 67 91 | 
             
                  report_lints(runner.lints)
         | 
| 68 | 
            -
                  halt | 
| 69 | 
            -
                rescue NoFilesError,  | 
| 92 | 
            +
                  halt :data if runner.lints.any?
         | 
| 93 | 
            +
                rescue NoFilesError, Errno::ENOENT => ex
         | 
| 70 94 | 
             
                  puts ex.message
         | 
| 71 | 
            -
                  halt | 
| 95 | 
            +
                  halt :no_input
         | 
| 96 | 
            +
                rescue NoSuchLinter => ex
         | 
| 97 | 
            +
                  puts ex.message
         | 
| 98 | 
            +
                  halt :usage
         | 
| 72 99 | 
             
                rescue => ex
         | 
| 73 100 | 
             
                  puts ex.message
         | 
| 74 101 | 
             
                  puts ex.backtrace
         | 
| 75 102 | 
             
                  puts 'Report this bug at '.yellow + BUG_REPORT_URL.cyan
         | 
| 76 | 
            -
                  halt | 
| 103 | 
            +
                  halt :software
         | 
| 77 104 | 
             
                end
         | 
| 78 105 |  | 
| 79 106 | 
             
              private
         | 
| 80 107 |  | 
| 81 | 
            -
                def  | 
| 82 | 
            -
                   | 
| 108 | 
            +
                def setup_configuration
         | 
| 109 | 
            +
                  if @options[:config_file]
         | 
| 110 | 
            +
                    @config = Config.load(@options[:config_file])
         | 
| 111 | 
            +
                    @config.preferred = true
         | 
| 112 | 
            +
                  end
         | 
| 83 113 |  | 
| 84 | 
            -
                   | 
| 85 | 
            -
             | 
| 114 | 
            +
                  merge_command_line_flags_with_config(@config)
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                def merge_command_line_flags_with_config(config)
         | 
| 118 | 
            +
                  if @options[:excluded_files]
         | 
| 119 | 
            +
                    @options[:excluded_files].each do |file|
         | 
| 120 | 
            +
                      config.exclude_file(file)
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  if @options[:included_linters]
         | 
| 125 | 
            +
                    config.disable_all_linters
         | 
| 126 | 
            +
                    LinterRegistry.extract_linters_from(@options[:included_linters]).each do |linter|
         | 
| 127 | 
            +
                      config.enable_linter(linter)
         | 
| 128 | 
            +
                    end
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  if @options[:excluded_linters]
         | 
| 132 | 
            +
                    LinterRegistry.extract_linters_from(@options[:excluded_linters]).each do |linter|
         | 
| 133 | 
            +
                      config.disable_linter(linter)
         | 
| 134 | 
            +
                    end
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  config
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def files_to_lint
         | 
| 141 | 
            +
                  extract_files_from(@options[:files]).reject do |file|
         | 
| 142 | 
            +
                    config =
         | 
| 143 | 
            +
                      if !@config.preferred && (config_for_file = Config.for_file(file))
         | 
| 144 | 
            +
                        merge_command_line_flags_with_config(config_for_file.dup)
         | 
| 145 | 
            +
                      else
         | 
| 146 | 
            +
                        @config
         | 
| 147 | 
            +
                      end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    config.excluded_file?(file)
         | 
| 86 150 | 
             
                  end
         | 
| 87 151 | 
             
                end
         | 
| 88 152 |  | 
| @@ -105,7 +169,8 @@ module SCSSLint | |
| 105 169 |  | 
| 106 170 | 
             
                def report_lints(lints)
         | 
| 107 171 | 
             
                  sorted_lints = lints.sort_by { |l| [l.filename, l.line] }
         | 
| 108 | 
            -
                  reporter = options.fetch(:reporter, Reporter::DefaultReporter) | 
| 172 | 
            +
                  reporter = @options.fetch(:reporter, Reporter::DefaultReporter)
         | 
| 173 | 
            +
                                     .new(sorted_lints)
         | 
| 109 174 | 
             
                  output = reporter.report_lints
         | 
| 110 175 | 
             
                  print output if output
         | 
| 111 176 | 
             
                end
         | 
| @@ -127,7 +192,7 @@ module SCSSLint | |
| 127 192 | 
             
                def print_help(help_message, err = nil)
         | 
| 128 193 | 
             
                  puts err, '' if err
         | 
| 129 194 | 
             
                  puts help_message
         | 
| 130 | 
            -
                  halt
         | 
| 195 | 
            +
                  halt(err ? :usage : :ok)
         | 
| 131 196 | 
             
                end
         | 
| 132 197 |  | 
| 133 198 | 
             
                def print_version(program_name, version)
         | 
| @@ -136,8 +201,8 @@ module SCSSLint | |
| 136 201 | 
             
                end
         | 
| 137 202 |  | 
| 138 203 | 
             
                # Used for ease-of testing
         | 
| 139 | 
            -
                def halt(exit_status =  | 
| 140 | 
            -
                  exit | 
| 204 | 
            +
                def halt(exit_status = :ok)
         | 
| 205 | 
            +
                  exit(EXIT_CODES[exit_status])
         | 
| 141 206 | 
             
                end
         | 
| 142 207 | 
             
              end
         | 
| 143 208 | 
             
            end
         | 
| @@ -0,0 +1,223 @@ | |
| 1 | 
            +
            require 'pathname'
         | 
| 2 | 
            +
            require 'yaml'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module SCSSLint
         | 
| 5 | 
            +
              # Loads and manages application configuration.
         | 
| 6 | 
            +
              class Config
         | 
| 7 | 
            +
                FILE_NAME = '.scss-lint.yml'
         | 
| 8 | 
            +
                DEFAULT_FILE = File.join(SCSS_LINT_HOME, 'config', 'default.yml')
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                attr_accessor :preferred # If this config should be preferred over others
         | 
| 11 | 
            +
                attr_reader :options, :warnings
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                class << self
         | 
| 14 | 
            +
                  def default
         | 
| 15 | 
            +
                    load(DEFAULT_FILE, merge_with_default: false)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # Loads a configuration from a file, merging it with the default
         | 
| 19 | 
            +
                  # configuration.
         | 
| 20 | 
            +
                  def load(file, options = {})
         | 
| 21 | 
            +
                    config_options = load_options_hash_from_file(file)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    if options.fetch(:merge_with_default, true)
         | 
| 24 | 
            +
                      config_options = smart_merge(default_options_hash, config_options)
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    Config.new(config_options)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Loads the configuration for a given file.
         | 
| 31 | 
            +
                  def for_file(file_path)
         | 
| 32 | 
            +
                    directory = File.dirname(File.expand_path(file_path))
         | 
| 33 | 
            +
                    @dir_to_config ||= {}
         | 
| 34 | 
            +
                    @dir_to_config[directory] ||=
         | 
| 35 | 
            +
                      begin
         | 
| 36 | 
            +
                        config_file = possible_config_files(directory).find { |path| path.file? }
         | 
| 37 | 
            +
                        Config.load(config_file.to_s) if config_file
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def linter_name(linter)
         | 
| 42 | 
            +
                    linter = linter.is_a?(Class) ? linter : linter.class
         | 
| 43 | 
            +
                    linter.name.split('::')[2..-1].join('::')
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def possible_config_files(directory)
         | 
| 49 | 
            +
                    files = Pathname.new(directory)
         | 
| 50 | 
            +
                                    .enum_for(:ascend)
         | 
| 51 | 
            +
                                    .map { |path| path + FILE_NAME }
         | 
| 52 | 
            +
                    files << Pathname.new(FILE_NAME)
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  def default_options_hash
         | 
| 56 | 
            +
                    @default_options_hash ||= load_options_hash_from_file(DEFAULT_FILE)
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  # Recursively load config files, fetching files specified by `include`
         | 
| 60 | 
            +
                  # directives and merging the file's config with the files specified.
         | 
| 61 | 
            +
                  def load_options_hash_from_file(file)
         | 
| 62 | 
            +
                    file_contents = load_file_contents(file)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    options =
         | 
| 65 | 
            +
                      if file_contents.strip.empty?
         | 
| 66 | 
            +
                        {}
         | 
| 67 | 
            +
                      else
         | 
| 68 | 
            +
                        YAML.load(file_contents).to_hash
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    if options['exclude']
         | 
| 72 | 
            +
                      # Ensure exclude is an array, since we allow user to specify a single
         | 
| 73 | 
            +
                      # string. We do this before merging with the config loaded via
         | 
| 74 | 
            +
                      # inherit_form since this allows us to merge the excludes from that,
         | 
| 75 | 
            +
                      # rather than overwriting them.
         | 
| 76 | 
            +
                      options['exclude'] = [options['exclude']].flatten
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    if options['inherit_from']
         | 
| 80 | 
            +
                      includes = [options.delete('inherit_from')].flatten.map do |include_file|
         | 
| 81 | 
            +
                        load_options_hash_from_file(path_relative_to_config(include_file, file))
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      merged_includes = includes[1..-1].inject(includes.first) do |merged, include_file|
         | 
| 85 | 
            +
                        smart_merge(merged, include_file)
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      options = smart_merge(merged_includes, options)
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    # Merge options from wildcard linters into individual linter configs
         | 
| 92 | 
            +
                    options.fetch('linters', {}).keys.each do |class_name|
         | 
| 93 | 
            +
                      next unless class_name.include?('*')
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      class_name_regex = /#{class_name.gsub('*', '[^:]+')}/
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                      wildcard_options = options['linters'].delete(class_name)
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                      LinterRegistry.linters.each do |linter_class|
         | 
| 100 | 
            +
                        name = linter_name(linter_class)
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                        if name.match(class_name_regex)
         | 
| 103 | 
            +
                          old_options = options['linters'].fetch(name, {})
         | 
| 104 | 
            +
                          options['linters'][name] = smart_merge(old_options, wildcard_options)
         | 
| 105 | 
            +
                        end
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    # Ensure all excludes are absolute paths
         | 
| 110 | 
            +
                    if options['exclude']
         | 
| 111 | 
            +
                      excludes = [options['exclude']].flatten
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                      options['exclude'] = excludes.map do |exclusion_glob|
         | 
| 114 | 
            +
                        if exclusion_glob.start_with?('/')
         | 
| 115 | 
            +
                          exclusion_glob
         | 
| 116 | 
            +
                        else
         | 
| 117 | 
            +
                          # Expand the path assuming it is relative to the config file itself
         | 
| 118 | 
            +
                          File.expand_path(exclusion_glob, File.expand_path(File.dirname(file)))
         | 
| 119 | 
            +
                        end
         | 
| 120 | 
            +
                      end
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    options
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  def path_relative_to_config(relative_include_path, base_config_path)
         | 
| 127 | 
            +
                    if relative_include_path.start_with?('/')
         | 
| 128 | 
            +
                      relative_include_path
         | 
| 129 | 
            +
                    else
         | 
| 130 | 
            +
                      File.join(File.dirname(base_config_path), relative_include_path)
         | 
| 131 | 
            +
                    end
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                  # For easy stubbing in tests
         | 
| 135 | 
            +
                  def load_file_contents(file)
         | 
| 136 | 
            +
                    File.open(file, 'r').read
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  # Merge two hashes, concatenating lists and further merging nested hashes.
         | 
| 140 | 
            +
                  def smart_merge(parent, child)
         | 
| 141 | 
            +
                    parent.merge(child) do |key, old, new|
         | 
| 142 | 
            +
                      case old
         | 
| 143 | 
            +
                      when Array
         | 
| 144 | 
            +
                        old + new
         | 
| 145 | 
            +
                      when Hash
         | 
| 146 | 
            +
                        smart_merge(old, new)
         | 
| 147 | 
            +
                      else
         | 
| 148 | 
            +
                        new
         | 
| 149 | 
            +
                      end
         | 
| 150 | 
            +
                    end
         | 
| 151 | 
            +
                  end
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def initialize(options)
         | 
| 155 | 
            +
                  @options = options
         | 
| 156 | 
            +
                  @warnings = []
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                  validate_linters
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                def ==(other)
         | 
| 162 | 
            +
                  super || @options == other.options
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
                alias :eql? :==
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                def enabled_linters
         | 
| 167 | 
            +
                  LinterRegistry.extract_linters_from(@options['linters'].keys).select do |linter|
         | 
| 168 | 
            +
                    linter_options(linter)['enabled']
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                def linter_enabled?(linter)
         | 
| 173 | 
            +
                  linter_options(linter)['enabled']
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def enable_linter(linter)
         | 
| 177 | 
            +
                  linter_options(linter)['enabled'] = true
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                def disable_linter(linter)
         | 
| 181 | 
            +
                  linter_options(linter)['enabled'] = false
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                def disable_all_linters
         | 
| 185 | 
            +
                  @options['linters'].values.each do |linter_config|
         | 
| 186 | 
            +
                    linter_config['enabled'] = false
         | 
| 187 | 
            +
                  end
         | 
| 188 | 
            +
                end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                def linter_options(linter)
         | 
| 191 | 
            +
                  @options['linters'][self.class.linter_name(linter)]
         | 
| 192 | 
            +
                end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                def excluded_file?(file_path)
         | 
| 195 | 
            +
                  abs_path = File.expand_path(file_path)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                  @options.fetch('exclude', []).any? do |exclusion_glob|
         | 
| 198 | 
            +
                    File.fnmatch(exclusion_glob, abs_path)
         | 
| 199 | 
            +
                  end
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                def exclude_file(file_path)
         | 
| 203 | 
            +
                  abs_path = File.expand_path(file_path)
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                  @options['exclude'] ||= []
         | 
| 206 | 
            +
                  @options['exclude'] << abs_path
         | 
| 207 | 
            +
                end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
              private
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                def validate_linters
         | 
| 212 | 
            +
                  return unless linters = @options['linters']
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  linters.keys.each do |name|
         | 
| 215 | 
            +
                    begin
         | 
| 216 | 
            +
                      Linter.const_get(name)
         | 
| 217 | 
            +
                    rescue NameError
         | 
| 218 | 
            +
                      @warnings << "Linter #{name} does not exist; ignoring"
         | 
| 219 | 
            +
                    end
         | 
| 220 | 
            +
                  end
         | 
| 221 | 
            +
                end
         | 
| 222 | 
            +
              end
         | 
| 223 | 
            +
            end
         | 
    
        data/lib/scss_lint/constants.rb
    CHANGED
    
    
    
        data/lib/scss_lint/linter.rb
    CHANGED
    
    | @@ -1,15 +1,17 @@ | |
| 1 1 | 
             
            module SCSSLint
         | 
| 2 | 
            +
              # Defines common functionality available to all linters.
         | 
| 2 3 | 
             
              class Linter < Sass::Tree::Visitors::Base
         | 
| 3 4 | 
             
                include SelectorVisitor
         | 
| 4 5 | 
             
                include Utils
         | 
| 5 6 |  | 
| 6 | 
            -
                attr_reader :engine, :lints
         | 
| 7 | 
            +
                attr_reader :config, :engine, :lints
         | 
| 7 8 |  | 
| 8 9 | 
             
                def initialize
         | 
| 9 10 | 
             
                  @lints = []
         | 
| 10 11 | 
             
                end
         | 
| 11 12 |  | 
| 12 | 
            -
                def run(engine)
         | 
| 13 | 
            +
                def run(engine, config)
         | 
| 14 | 
            +
                  @config = config
         | 
| 13 15 | 
             
                  @engine = engine
         | 
| 14 16 | 
             
                  visit(engine.tree)
         | 
| 15 17 | 
             
                end
         | 
| @@ -4,6 +4,7 @@ module SCSSLint | |
| 4 4 | 
             
                include LinterRegistry
         | 
| 5 5 |  | 
| 6 6 | 
             
                def visit_root(node)
         | 
| 7 | 
            +
                  @indent_width = config['width']
         | 
| 7 8 | 
             
                  @indent = 0
         | 
| 8 9 | 
             
                  yield
         | 
| 9 10 | 
             
                end
         | 
| @@ -14,9 +15,9 @@ module SCSSLint | |
| 14 15 | 
             
                  # indentation problems as that would likely make the lint too noisy.
         | 
| 15 16 | 
             
                  return if check_indentation(node)
         | 
| 16 17 |  | 
| 17 | 
            -
                  @indent +=  | 
| 18 | 
            +
                  @indent += @indent_width
         | 
| 18 19 | 
             
                  yield
         | 
| 19 | 
            -
                  @indent -=  | 
| 20 | 
            +
                  @indent -= @indent_width
         | 
| 20 21 | 
             
                end
         | 
| 21 22 |  | 
| 22 23 | 
             
                def check_indentation(node)
         | 
| @@ -64,9 +65,5 @@ module SCSSLint | |
| 64 65 | 
             
                alias :visit_return     :check_indentation
         | 
| 65 66 | 
             
                alias :visit_variable   :check_indentation
         | 
| 66 67 | 
             
                alias :visit_warn       :check_indentation
         | 
| 67 | 
            -
             | 
| 68 | 
            -
              private
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                INDENT_WIDTH = 2
         | 
| 71 68 | 
             
              end
         | 
| 72 69 | 
             
            end
         | 
    
        data/lib/scss_lint/runner.rb
    CHANGED
    
    | @@ -1,52 +1,42 @@ | |
| 1 1 | 
             
            module SCSSLint
         | 
| 2 2 | 
             
              class LinterError < StandardError; end
         | 
| 3 3 | 
             
              class NoFilesError < StandardError; end
         | 
| 4 | 
            -
              class NoLintersError < StandardError; end
         | 
| 5 4 |  | 
| 6 5 | 
             
              # Finds and aggregates all lints found by running the registered linters
         | 
| 7 6 | 
             
              # against a set of SCSS files.
         | 
| 8 7 | 
             
              class Runner
         | 
| 9 | 
            -
                attr_reader : | 
| 8 | 
            +
                attr_reader :lints
         | 
| 10 9 |  | 
| 11 | 
            -
                def initialize( | 
| 10 | 
            +
                def initialize(config)
         | 
| 11 | 
            +
                  @config = config
         | 
| 12 12 | 
             
                  @lints = []
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                  included_linters = LinterRegistry.
         | 
| 15 | 
            -
                    extract_linters_from(options.fetch(:included_linters, []))
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                  included_linters = LinterRegistry.linters if included_linters.empty?
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                  excluded_linters = LinterRegistry.
         | 
| 20 | 
            -
                    extract_linters_from(options.fetch(:excluded_linters, []))
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                  @linters = (included_linters - excluded_linters).map(&:new)
         | 
| 13 | 
            +
                  @linters = LinterRegistry.linters.map(&:new)
         | 
| 23 14 | 
             
                end
         | 
| 24 15 |  | 
| 25 | 
            -
                def run(files | 
| 16 | 
            +
                def run(files)
         | 
| 26 17 | 
             
                  raise NoFilesError, 'No SCSS files specified' if files.empty?
         | 
| 27 | 
            -
                  raise NoLintersError, 'No linters specified' if linters.empty?
         | 
| 28 18 |  | 
| 29 19 | 
             
                  files.each do |file|
         | 
| 30 20 | 
             
                    find_lints(file)
         | 
| 31 21 | 
             
                  end
         | 
| 32 22 |  | 
| 33 | 
            -
                  linters.each do |linter|
         | 
| 23 | 
            +
                  @linters.each do |linter|
         | 
| 34 24 | 
             
                    @lints += linter.lints
         | 
| 35 25 | 
             
                  end
         | 
| 36 26 | 
             
                end
         | 
| 37 27 |  | 
| 38 | 
            -
                def lints?
         | 
| 39 | 
            -
                  lints.any?
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
             | 
| 42 28 | 
             
              private
         | 
| 43 29 |  | 
| 44 30 | 
             
                def find_lints(file)
         | 
| 45 31 | 
             
                  engine = Engine.new(file)
         | 
| 32 | 
            +
                  config = @config.preferred ? @config : Config.for_file(file)
         | 
| 33 | 
            +
                  config ||= @config
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  @linters.each do |linter|
         | 
| 36 | 
            +
                    next unless config.linter_enabled?(linter)
         | 
| 46 37 |  | 
| 47 | 
            -
                  linters.each do |linter|
         | 
| 48 38 | 
             
                    begin
         | 
| 49 | 
            -
                      linter.run(engine)
         | 
| 39 | 
            +
                      linter.run(engine, config.linter_options(linter))
         | 
| 50 40 | 
             
                    rescue => error
         | 
| 51 41 | 
             
                      raise LinterError,
         | 
| 52 42 | 
             
                            "#{linter.class} raised unexpected error linting file #{file}: " <<
         | 
    
        data/lib/scss_lint/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: scss-lint
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.13.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Causes Engineering
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2013- | 
| 12 | 
            +
            date: 2013-12-03 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: colorize
         | 
| @@ -76,6 +76,7 @@ executables: | |
| 76 76 | 
             
            extensions: []
         | 
| 77 77 | 
             
            extra_rdoc_files: []
         | 
| 78 78 | 
             
            files:
         | 
| 79 | 
            +
            - config/default.yml
         | 
| 79 80 | 
             
            - lib/scss_lint/version.rb
         | 
| 80 81 | 
             
            - lib/scss_lint/constants.rb
         | 
| 81 82 | 
             
            - lib/scss_lint/utils.rb
         | 
| @@ -88,6 +89,7 @@ files: | |
| 88 89 | 
             
            - lib/scss_lint/selector_visitor.rb
         | 
| 89 90 | 
             
            - lib/scss_lint/sass/tree.rb
         | 
| 90 91 | 
             
            - lib/scss_lint/sass/script.rb
         | 
| 92 | 
            +
            - lib/scss_lint/config.rb
         | 
| 91 93 | 
             
            - lib/scss_lint/linter/compass.rb
         | 
| 92 94 | 
             
            - lib/scss_lint/linter/space_after_comma.rb
         | 
| 93 95 | 
             
            - lib/scss_lint/linter/space_before_brace.rb
         |