diff --git a/CHANGELOG.md b/CHANGELOG.md index 65fb036f..b6d42490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## master (unreleased) * Fix non-visible line number on light-colored terminal backgrounds +* Allow files without `.haml` extension to be linted when explicitly specified ## 0.11.0 diff --git a/lib/haml_lint.rb b/lib/haml_lint.rb index 217554d2..c22f13c1 100644 --- a/lib/haml_lint.rb +++ b/lib/haml_lint.rb @@ -11,6 +11,7 @@ require 'haml_lint/logger' require 'haml_lint/reporter' require 'haml_lint/report' +require 'haml_lint/file_finder' require 'haml_lint/runner' require 'haml_lint/utils' require 'haml_lint/version' diff --git a/lib/haml_lint/file_finder.rb b/lib/haml_lint/file_finder.rb new file mode 100644 index 00000000..78607efe --- /dev/null +++ b/lib/haml_lint/file_finder.rb @@ -0,0 +1,69 @@ +require 'find' + +module HamlLint + # Finds HAML files that should be linted given a specified list of paths, glob + # patterns, and configuration. + class FileFinder + # List of extensions of files to include under a directory when a directory + # is specified instead of a file. + VALID_EXTENSIONS = %w[.haml] + + # @param config [HamlLint::Configuration] + def initialize(config) + @config = config + end + + # Return list of files to lint given the specified set of paths and glob + # patterns. + # @param patterns [Array] + # @param excluded_patterns [Array] + # @raise [HamlLint::Exceptions::InvalidFilePath] + # @return [Array] list of actual files + def find(patterns, excluded_patterns) + extract_files_from(patterns).reject do |file| + excluded_patterns.any? do |exclusion_glob| + ::File.fnmatch?(exclusion_glob, file, + ::File::FNM_PATHNAME | # Wildcards don't match path separators + ::File::FNM_DOTMATCH) # `*` wildcard matches dotfiles + end + end + end + + private + + def extract_files_from(patterns) # rubocop:disable MethodLength + files = [] + + patterns.each do |pattern| + if File.file?(pattern) + files << pattern + else + begin + ::Find.find(pattern) do |file| + files << file if haml_file?(file) + end + rescue ::Errno::ENOENT + # File didn't exist; it might be a file glob pattern + matches = ::Dir.glob(pattern) + if matches.any? + files += matches + else + # One of the paths specified does not exist; raise a more + # descriptive exception so we know which one + raise HamlLint::Exceptions::InvalidFilePath, + "File path '#{pattern}' does not exist" + end + end + end + end + + files.uniq + end + + def haml_file?(file) + return false unless ::FileTest.file?(file) + + VALID_EXTENSIONS.include?(::File.extname(file)) + end + end +end diff --git a/lib/haml_lint/runner.rb b/lib/haml_lint/runner.rb index 39a6f033..ec3401b7 100644 --- a/lib/haml_lint/runner.rb +++ b/lib/haml_lint/runner.rb @@ -9,7 +9,7 @@ class Runner # @return [HamlLint::Report] a summary of all lints found def run(options = {}) config = load_applicable_config(options) - files = extract_applicable_files(options) + files = extract_applicable_files(options, config) linters = extract_enabled_linters(config, options) raise HamlLint::Exceptions::NoLintersError, 'No linters specified' if linters.empty? @@ -63,16 +63,11 @@ def find_lints(file, linters, config) @lints << Lint.new(nil, file, ex.line, ex.to_s, :error) end - def extract_applicable_files(options) + def extract_applicable_files(options, config) + included_patterns = options[:files] excluded_files = options.fetch(:excluded_files, []) - Utils.extract_files_from(options[:files]).reject do |file| - excluded_files.any? do |exclusion_glob| - File.fnmatch?(exclusion_glob, file, - File::FNM_PATHNAME | # Wildcards don't match path separators - File::FNM_DOTMATCH) # `*` wildcard matches dotfiles - end - end + HamlLint::FileFinder.new(config).find(included_patterns, excluded_files) end end end diff --git a/lib/haml_lint/utils.rb b/lib/haml_lint/utils.rb index c740b2a6..b1016191 100644 --- a/lib/haml_lint/utils.rb +++ b/lib/haml_lint/utils.rb @@ -1,35 +1,8 @@ -require 'find' - module HamlLint # A miscellaneous set of utility functions. module Utils module_function - def extract_files_from(list) - files = [] - - list.each do |file| - begin - Find.find(file) do |f| - files << f if haml_file?(f) - end - rescue Errno::ENOENT - # File didn't exist; it might be a file glob pattern - matches = Dir.glob(file) - if matches.any? - files += matches - else - # One of the paths specified does not exist; raise a more - # descriptive exception so we know which one - raise HamlLint::Exceptions::InvalidFilePath, - "File path '#{file}' does not exist" - end - end - end - - files.uniq - end - # Yields interpolated values within a block of filter text. def extract_interpolated_values(filter_text) Haml::Util.handle_interpolation(filter_text.dump) do |scan| @@ -81,11 +54,5 @@ def count_consecutive(items, offset, satisfies) count += 1 while (offset + count < items.count) && satisfies[items[offset + count]] count end - - def haml_file?(file) - return false unless FileTest.file?(file) - - File.extname(file) == '.haml' - end end end diff --git a/spec/haml_lint/file_finder_spec.rb b/spec/haml_lint/file_finder_spec.rb new file mode 100644 index 00000000..6405fe76 --- /dev/null +++ b/spec/haml_lint/file_finder_spec.rb @@ -0,0 +1,101 @@ +require 'spec_helper' + +describe HamlLint::FileFinder do + let(:config) { double } + let(:excluded_patterns) { [] } + + subject { described_class.new(config) } + + describe '#find' do + include_context 'isolated environment' + + subject { super().find(patterns, excluded_patterns) } + + context 'when no patterns are given' do + let(:patterns) { [] } + + context 'and there are no HAML files under the current directory' do + it { should == [] } + end + + context 'and there are HAML files under the current directory' do + before do + `touch blah.haml` + `mkdir -p more` + `touch more/more.haml` + end + + it { should == [] } + end + end + + context 'when files without a valid extension are given' do + let(:patterns) { ['test.txt'] } + + context 'and those files exist' do + before do + `touch test.txt` + end + + it { should == ['test.txt'] } + end + + context 'and those files do not exist' do + it 'raises an error' do + expect { subject }.to raise_error HamlLint::Exceptions::InvalidFilePath + end + end + end + + context 'when directories are given' do + let(:patterns) { ['some-dir'] } + + context 'and those directories exist' do + before do + `mkdir -p some-dir` + end + + context 'and they contain HAML files' do + before do + `touch some-dir/test.haml` + end + + it { should == ['some-dir/test.haml'] } + end + + context 'and they contain more directories with files with recognized extensions' do + before do + `mkdir -p some-dir/more-dir` + `touch some-dir/more-dir/test.haml` + end + + it { should == ['some-dir/more-dir/test.haml'] } + end + + context 'and they contain files with some other extension' do + before do + `touch some-dir/test.txt` + end + + it { should == [] } + end + end + + context 'and those directories do not exist' do + it 'raises an error' do + expect { subject }.to raise_error HamlLint::Exceptions::InvalidFilePath + end + end + end + + context 'when the same file is specified multiple times' do + let(:patterns) { ['test.haml'] * 3 } + + before do + `touch test.haml` + end + + it { should == ['test.haml'] } + end + end +end diff --git a/spec/support/isolated_environment.rb b/spec/support/isolated_environment.rb new file mode 100644 index 00000000..934f5c01 --- /dev/null +++ b/spec/support/isolated_environment.rb @@ -0,0 +1,25 @@ +require 'fileutils' +require 'tmpdir' + +shared_context 'isolated environment' do + around do |example| + Dir.mktmpdir do |tmpdir| + original_home = ENV['HOME'] + + begin + virtual_home = File.expand_path(File.join(tmpdir, 'home')) + Dir.mkdir(virtual_home) + ENV['HOME'] = virtual_home + + working_dir = File.join(tmpdir, 'work') + Dir.mkdir(working_dir) + + Dir.chdir(working_dir) do + example.run + end + ensure + ENV['HOME'] = original_home + end + end + end +end