haml_lint 0.13.0 → 0.14.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +1 -0
  3. data/lib/haml_lint/cli.rb +25 -8
  4. data/lib/haml_lint/configuration.rb +56 -34
  5. data/lib/haml_lint/configuration_loader.rb +22 -6
  6. data/lib/haml_lint/constants.rb +1 -1
  7. data/lib/haml_lint/document.rb +102 -0
  8. data/lib/haml_lint/file_finder.rb +14 -5
  9. data/lib/haml_lint/lint.rb +17 -1
  10. data/lib/haml_lint/linter/alt_text.rb +1 -1
  11. data/lib/haml_lint/linter/class_attribute_with_static_value.rb +2 -2
  12. data/lib/haml_lint/linter/classes_before_ids.rb +2 -2
  13. data/lib/haml_lint/linter/consecutive_comments.rb +3 -5
  14. data/lib/haml_lint/linter/consecutive_silent_scripts.rb +5 -5
  15. data/lib/haml_lint/linter/empty_script.rb +1 -1
  16. data/lib/haml_lint/linter/html_attributes.rb +2 -2
  17. data/lib/haml_lint/linter/implicit_div.rb +3 -3
  18. data/lib/haml_lint/linter/leading_comment_space.rb +1 -1
  19. data/lib/haml_lint/linter/line_length.rb +2 -2
  20. data/lib/haml_lint/linter/multiline_pipe.rb +3 -3
  21. data/lib/haml_lint/linter/multiline_script.rb +3 -3
  22. data/lib/haml_lint/linter/object_reference_attributes.rb +1 -1
  23. data/lib/haml_lint/linter/rubocop.rb +60 -34
  24. data/lib/haml_lint/linter/ruby_comments.rb +1 -1
  25. data/lib/haml_lint/linter/space_before_script.rb +4 -4
  26. data/lib/haml_lint/linter/space_inside_hash_attributes.rb +2 -2
  27. data/lib/haml_lint/linter/tag_name.rb +1 -1
  28. data/lib/haml_lint/linter/trailing_whitespace.rb +2 -2
  29. data/lib/haml_lint/linter/unnecessary_interpolation.rb +3 -3
  30. data/lib/haml_lint/linter/unnecessary_string_output.rb +10 -3
  31. data/lib/haml_lint/linter.rb +30 -10
  32. data/lib/haml_lint/linter_registry.rb +13 -2
  33. data/lib/haml_lint/linter_selector.rb +77 -0
  34. data/lib/haml_lint/node_transformer.rb +5 -5
  35. data/lib/haml_lint/options.rb +13 -6
  36. data/lib/haml_lint/rake_task.rb +18 -3
  37. data/lib/haml_lint/report.rb +7 -0
  38. data/lib/haml_lint/reporter/default_reporter.rb +2 -2
  39. data/lib/haml_lint/reporter/json_reporter.rb +15 -10
  40. data/lib/haml_lint/reporter.rb +17 -11
  41. data/lib/haml_lint/{script_extractor.rb → ruby_extractor.rb} +26 -17
  42. data/lib/haml_lint/runner.rb +44 -44
  43. data/lib/haml_lint/tree/node.rb +7 -8
  44. data/lib/haml_lint/utils.rb +88 -21
  45. data/lib/haml_lint/version.rb +1 -1
  46. data/lib/haml_lint.rb +2 -1
  47. metadata +5 -4
  48. data/lib/haml_lint/parser.rb +0 -87
@@ -1,29 +1,34 @@
1
1
  module HamlLint
2
2
  # Outputs report as a JSON document.
3
3
  class Reporter::JsonReporter < Reporter
4
- def report_lints
4
+ def display_report(report)
5
+ lints = report.lints
5
6
  grouped = lints.group_by(&:filename)
6
7
 
7
- report = {
8
- metadata: {
9
- hamllint_version: VERSION,
10
- ruby_engine: RUBY_ENGINE,
11
- ruby_patchlevel: RUBY_PATCHLEVEL.to_s,
12
- ruby_platform: RUBY_PLATFORM,
13
- },
8
+ report_hash = {
9
+ metadata: metadata,
14
10
  files: grouped.map { |l| map_file(l) },
15
11
  summary: {
16
12
  offense_count: lints.length,
17
13
  target_file_count: grouped.length,
18
- inspected_file_count: files.length,
14
+ inspected_file_count: report.files.length,
19
15
  },
20
16
  }
21
17
 
22
- log.log report.to_json
18
+ log.log report_hash.to_json
23
19
  end
24
20
 
25
21
  private
26
22
 
23
+ def metadata
24
+ {
25
+ haml_lint_version: HamlLint::VERSION,
26
+ ruby_engine: RUBY_ENGINE,
27
+ ruby_patchlevel: RUBY_PATCHLEVEL.to_s,
28
+ ruby_platform: RUBY_PLATFORM,
29
+ }
30
+ end
31
+
27
32
  def map_file(file)
28
33
  {
29
34
  path: file.first,
@@ -1,36 +1,42 @@
1
1
  module HamlLint
2
- # Abstract lint reporter. Subclass and override {#report_lints} to
2
+ # Abstract lint reporter. Subclass and override {#display_report} to
3
3
  # implement a custom lint reporter.
4
4
  #
5
5
  # @abstract
6
6
  class Reporter
7
- attr_reader :lints
8
- attr_reader :files
9
-
7
+ # Creates the reporter that will display the given report.
8
+ #
10
9
  # @param logger [HamlLint::Logger]
11
- # @param report [HamlLint::Report]
12
- def initialize(logger, report)
10
+ def initialize(logger)
13
11
  @log = logger
14
- @lints = report.lints
15
- @files = report.files
16
12
  end
17
13
 
18
14
  # Implemented by subclasses to display lints from a {HamlLint::Report}.
19
- def report_lints
20
- raise NotImplementedError
15
+ #
16
+ # @param report [HamlLint::Report]
17
+ def display_report(report)
18
+ raise NotImplementedError,
19
+ "Implement `display_report` to display #{report}"
21
20
  end
22
21
 
23
- # Keep tracking all the descendants of this class for the list of available reporters
22
+ # Keep tracking all the descendants of this class for the list of available
23
+ # reporters.
24
+ #
25
+ # @return [Array<Class>]
24
26
  def self.descendants
25
27
  @descendants ||= []
26
28
  end
27
29
 
30
+ # Executed when this class is subclassed.
31
+ #
32
+ # @param descendant [Class]
28
33
  def self.inherited(descendant)
29
34
  descendants << descendant
30
35
  end
31
36
 
32
37
  private
33
38
 
39
+ # @return [HamlLint::Logger] logger to send output to
34
40
  attr_reader :log
35
41
  end
36
42
  end
@@ -19,24 +19,33 @@ module HamlLint
19
19
  # link_to 'Sign In', sign_in_path
20
20
  # end
21
21
  #
22
- class ScriptExtractor
22
+ # The translation won't be perfect, and won't make any real sense, but the
23
+ # relationship between variable declarations/uses and the flow control graph
24
+ # will remain intact.
25
+ class RubyExtractor
23
26
  include HamlVisitor
24
27
 
25
- attr_reader :source, :source_map
26
-
27
- def initialize(parser)
28
- @parser = parser
29
- end
30
-
31
- def extract
32
- visit(@parser.tree)
33
- @source = @code.join("\n")
28
+ # Stores the extracted source and a map of lines of generated source to the
29
+ # original source that created them.
30
+ #
31
+ # @attr_reader source [String] generated source code
32
+ # @attr_reader source_map [Hash] map of line numbers from generated source
33
+ # to original source line number
34
+ RubySource = Struct.new(:source, :source_map)
35
+
36
+ # Extracts Ruby code from Sexp representing a Slim document.
37
+ #
38
+ # @param document [HamlLint::Document]
39
+ # @return [HamlLint::RubyExtractor::RubySource]
40
+ def extract(document)
41
+ visit(document.tree)
42
+ RubySource.new(@source_lines.join("\n"), @source_map)
34
43
  end
35
44
 
36
45
  def visit_root(_node)
37
- @code = []
38
- @total_lines = 0
46
+ @source_lines = []
39
47
  @source_map = {}
48
+ @line_count = 0
40
49
  @indent_level = 0
41
50
 
42
51
  yield # Collect lines of code from children
@@ -104,8 +113,8 @@ module HamlLint
104
113
  end
105
114
  else
106
115
  add_line('puts', node)
107
- HamlLint::Utils.extract_interpolated_values(node.text) do |interpolated_code|
108
- add_line(interpolated_code, node)
116
+ HamlLint::Utils.extract_interpolated_values(node.text) do |interpolated_code, line|
117
+ add_line(interpolated_code, node.line + line)
109
118
  end
110
119
  end
111
120
  end
@@ -140,7 +149,7 @@ module HamlLint
140
149
 
141
150
  indent = (' ' * 2 * indent_level)
142
151
 
143
- @code << indent + code
152
+ @source_lines << indent + code
144
153
 
145
154
  original_line =
146
155
  node_or_line.respond_to?(:line) ? node_or_line.line : node_or_line
@@ -149,8 +158,8 @@ module HamlLint
149
158
  # resulting code will span multiple lines, so we need to create a
150
159
  # mapping for each line.
151
160
  (code.count("\n") + 1).times do
152
- @total_lines += 1
153
- @source_map[@total_lines] = original_line
161
+ @line_count += 1
162
+ @source_map[@line_count] = original_line
154
163
  end
155
164
  end
156
165
 
@@ -1,76 +1,76 @@
1
1
  module HamlLint
2
2
  # Responsible for running the applicable linters against the desired files.
3
3
  class Runner
4
- # Make the list of applicable files available
5
- attr_reader :files
6
-
7
4
  # Runs the appropriate linters against the desired files given the specified
8
5
  # options.
9
6
  #
10
- # @param options [Hash]
11
- # @raise [HamlLint::Exceptions::NoLintersError] when no linters are enabled
7
+ # @param [Hash] options
8
+ # @option options :config_file [String] path of configuration file to load
9
+ # @option options :config [HamlLint::Configuration] configuration to use
10
+ # @option options :excluded_files [Array<String>]
11
+ # @option options :included_linters [Array<String>]
12
+ # @option options :excluded_linters [Array<String>]
12
13
  # @return [HamlLint::Report] a summary of all lints found
13
14
  def run(options = {})
14
15
  config = load_applicable_config(options)
15
- files = extract_applicable_files(options, config)
16
- linters = extract_enabled_linters(config, options)
16
+ files = extract_applicable_files(config, options)
17
17
 
18
- raise HamlLint::Exceptions::NoLintersError, 'No linters specified' if linters.empty?
18
+ linter_selector = HamlLint::LinterSelector.new(config, options)
19
19
 
20
- @lints = []
21
- files.each do |file|
22
- find_lints(file, linters, config)
23
- end
24
-
25
- linters.each do |linter|
26
- @lints += linter.lints
27
- end
20
+ lints = files.map do |file|
21
+ collect_lints(file, linter_selector, config)
22
+ end.flatten
28
23
 
29
- HamlLint::Report.new(@lints, files)
24
+ HamlLint::Report.new(lints, files)
30
25
  end
31
26
 
32
27
  private
33
28
 
29
+ # Returns the {HamlLint::Configuration} that should be used given the
30
+ # specified options.
31
+ #
32
+ # @param options [Hash]
33
+ # @return [HamlLint::Configuration]
34
34
  def load_applicable_config(options)
35
35
  if options[:config_file]
36
36
  HamlLint::ConfigurationLoader.load_file(options[:config_file])
37
+ elsif options[:config]
38
+ options[:config]
37
39
  else
38
40
  HamlLint::ConfigurationLoader.load_applicable_config
39
41
  end
40
42
  end
41
43
 
42
- def extract_enabled_linters(config, options)
43
- included_linters = LinterRegistry
44
- .extract_linters_from(options.fetch(:included_linters, []))
45
-
46
- included_linters = LinterRegistry.linters if included_linters.empty?
47
-
48
- excluded_linters = LinterRegistry
49
- .extract_linters_from(options.fetch(:excluded_linters, []))
50
-
51
- # After filtering out explicitly included/excluded linters, only include
52
- # linters which are enabled in the configuration
53
- (included_linters - excluded_linters).map do |linter_class|
54
- linter_config = config.for_linter(linter_class)
55
- linter_class.new(linter_config) if linter_config['enabled']
56
- end.compact
57
- end
58
-
59
- def find_lints(file, linters, config)
60
- parser = Parser.new(file, config.hash)
61
-
62
- linters.each do |linter|
63
- linter.run(parser)
44
+ # Runs all provided linters using the specified config against the given
45
+ # file.
46
+ #
47
+ # @param file [String] path to file to lint
48
+ # @param linter_selector [HamlLint::LinterSelector]
49
+ # @param config [HamlLint::Configuration]
50
+ def collect_lints(file, linter_selector, config)
51
+ begin
52
+ document = HamlLint::Document.new(File.read(file), file: file, config: config)
53
+ rescue Haml::Error => ex
54
+ return [HamlLint::Lint.new(nil, file, ex.line, ex.to_s, :error)]
64
55
  end
65
- rescue Haml::Error => ex
66
- @lints << Lint.new(nil, file, ex.line, ex.to_s, :error)
56
+
57
+ linter_selector.linters_for_file(file).map do |linter|
58
+ linter.run(document)
59
+ end.flatten
67
60
  end
68
61
 
69
- def extract_applicable_files(options, config)
62
+ # Returns the list of files that should be linted given the specified
63
+ # configuration and options.
64
+ #
65
+ # @param config [HamlLint::Configuration]
66
+ # @param options [Hash]
67
+ # @return [Array<String>]
68
+ def extract_applicable_files(config, options)
70
69
  included_patterns = options[:files]
71
- excluded_files = options.fetch(:excluded_files, [])
70
+ excluded_patterns = config['exclude']
71
+ excluded_patterns += options.fetch(:excluded_files, [])
72
72
 
73
- HamlLint::FileFinder.new(config).find(included_patterns, excluded_files)
73
+ HamlLint::FileFinder.new(config).find(included_patterns, excluded_patterns)
74
74
  end
75
75
  end
76
76
  end
@@ -14,12 +14,11 @@ module HamlLint::Tree
14
14
 
15
15
  # Creates a node wrapping the given {Haml::Parser::ParseNode} struct.
16
16
  #
17
- # @param parser [HamlLint::Parser] parser that created this node
17
+ # @param document [HamlLint::Document] Haml document that created this node
18
18
  # @param parse_node [Haml::Parser::ParseNode] parse node created by HAML's parser
19
- def initialize(parser, parse_node)
20
- # TODO: Change signature to take source code object, not parser
19
+ def initialize(document, parse_node)
21
20
  @line = parse_node.line
22
- @parser = parser
21
+ @document = document
23
22
  @value = parse_node.value
24
23
  @type = parse_node.type
25
24
  end
@@ -50,12 +49,12 @@ module HamlLint::Tree
50
49
  if next_node
51
50
  next_node.line - 1
52
51
  else
53
- @parser.lines.count + 1
52
+ @document.source_lines.count + 1
54
53
  end
55
54
 
56
- @parser.lines[@line - 1...next_node_line]
57
- .join("\n")
58
- .gsub(/^\s*\z/m, '') # Remove blank lines at the end
55
+ @document.source_lines[@line - 1...next_node_line]
56
+ .join("\n")
57
+ .gsub(/^\s*\z/m, '') # Remove blank lines at the end
59
58
  end
60
59
 
61
60
  def inspect
@@ -3,56 +3,123 @@ module HamlLint
3
3
  module Utils
4
4
  module_function
5
5
 
6
- # Yields interpolated values within a block of filter text.
7
- def extract_interpolated_values(filter_text)
8
- Haml::Util.handle_interpolation(filter_text.dump) do |scan|
6
+ # Returns whether a glob pattern (or any of a list of patterns) matches the
7
+ # specified file.
8
+ #
9
+ # This is defined here so our file globbing options are consistent
10
+ # everywhere we perform globbing.
11
+ #
12
+ # @param glob [String, Array]
13
+ # @param file [String]
14
+ # @return [Boolean]
15
+ def any_glob_matches?(globs_or_glob, file)
16
+ Array(globs_or_glob).any? do |glob|
17
+ ::File.fnmatch?(glob, file,
18
+ ::File::FNM_PATHNAME | # Wildcards don't match path separators
19
+ ::File::FNM_DOTMATCH) # `*` wildcard matches dotfiles
20
+ end
21
+ end
22
+
23
+ # Yields interpolated values within a block of text.
24
+ #
25
+ # @param text [String]
26
+ # @yield Passes interpolated code and line number that code appears on in
27
+ # the text.
28
+ # @yieldparam interpolated_code [String] code that was interpolated
29
+ # @yieldparam line [Integer] line number code appears on in text
30
+ def extract_interpolated_values(text) # rubocop:disable Metrics/AbcSize
31
+ dumped_text = text.dump
32
+ newline_positions = extract_substring_positions(dumped_text, '\\\n')
33
+
34
+ Haml::Util.handle_interpolation(dumped_text) do |scan|
35
+ line = (newline_positions.find_index { |marker| scan.pos <= marker } ||
36
+ newline_positions.size) + 1
37
+
9
38
  escape_count = (scan[2].size - 1) / 2
10
- return unless escape_count.even? # rubocop:disable Lint/NonLocalExitFromIterator
39
+ break unless escape_count.even?
11
40
 
12
41
  dumped_interpolated_str = Haml::Util.balance(scan, '{', '}', 1)[0][0...-1]
13
42
 
14
43
  # Hacky way to turn a dumped string back into a regular string
15
- yield eval('"' + dumped_interpolated_str + '"') # rubocop:disable Eval
44
+ yield [eval('"' + dumped_interpolated_str + '"'), line] # rubocop:disable Eval
16
45
  end
17
46
  end
18
47
 
48
+ # Returns indexes of all occurrences of a substring within a string.
49
+ #
50
+ # Note, this will not return overlaping substrings, so searching for "aa"
51
+ # in "aaa" will only find one substring, not two.
52
+ #
53
+ # @param text [String] the text to search
54
+ # @param substr [String] the substring to search for
55
+ # @return [Array<Integer>] list of indexes where the substring occurs
56
+ def extract_substring_positions(text, substr)
57
+ positions = []
58
+ scanner = StringScanner.new(text)
59
+ positions << scanner.pos while scanner.scan(/(.*?)#{substr}/)
60
+ positions
61
+ end
62
+
19
63
  # Converts a string containing underscores/hyphens/spaces into CamelCase.
20
64
  def camel_case(str)
21
65
  str.split(/_|-| /).map { |part| part.sub(/^\w/) { |c| c.upcase } }.join
22
66
  end
23
67
 
24
- # Find all consecutive nodes satisfying the given {Proc} of a minimum size
25
- # and yield each group.
68
+ # Find all consecutive items satisfying the given block of a minimum size,
69
+ # yielding each group of consecutive items to the provided block.
26
70
  #
27
71
  # @param items [Array]
28
- # @param min_size [Fixnum] minimum number of consecutive items before
29
- # yielding
30
72
  # @param satisfies [Proc] function that takes an item and returns true/false
31
- def find_consecutive(items, min_size, satisfies)
32
- current = -1
73
+ # @param min_consecutive [Fixnum] minimum number of consecutive items before
74
+ # yielding the group
75
+ # @yield Passes list of consecutive items all matching the criteria defined
76
+ # by the `satisfies` {Proc} to the provided block
77
+ # @yieldparam group [Array] List of consecutive items
78
+ # @yieldreturn [Boolean] block should return whether item matches criteria
79
+ # for inclusion
80
+ def for_consecutive_items(items, satisfies, min_consecutive = 2)
81
+ current_index = -1
33
82
 
34
- while (current += 1) < items.count
35
- next unless satisfies[items[current]]
83
+ while (current_index += 1) < items.count
84
+ next unless satisfies[items[current_index]]
36
85
 
37
- count = count_consecutive(items, current, satisfies)
38
- next unless count >= min_size
86
+ count = count_consecutive(items, current_index, &satisfies)
87
+ next unless count >= min_consecutive
39
88
 
40
89
  # Yield the chunk of consecutive items
41
- yield items[current...(current + count)]
90
+ yield items[current_index...(current_index + count)]
42
91
 
43
- current += count # Skip this patch of consecutive items to find more
92
+ current_index += count # Skip this patch of consecutive items to find more
44
93
  end
45
94
  end
46
95
 
47
96
  # Count the number of consecutive items satisfying the given {Proc}.
48
97
  #
49
98
  # @param items [Array]
50
- # @param offset [Fixnum] index to start searching
51
- # @param satisfies [Proc] function to evaluate item with
52
- def count_consecutive(items, offset, satisfies)
99
+ # @param offset [Fixnum] index to start searching from
100
+ # @yield [item] Passes item to the provided block.
101
+ # @yieldparam item [Object] Item to evaluate as matching criteria for
102
+ # inclusion
103
+ # @yieldreturn [Boolean] whether to include the item
104
+ # @return [Integer]
105
+ def count_consecutive(items, offset = 0, &block)
53
106
  count = 1
54
- count += 1 while (offset + count < items.count) && satisfies[items[offset + count]]
107
+ count += 1 while (offset + count < items.count) && block.call(items[offset + count])
55
108
  count
56
109
  end
110
+
111
+ # Calls a block of code with a modified set of environment variables,
112
+ # restoring them once the code has executed.
113
+ def with_environment(env)
114
+ old_env = {}
115
+ env.each do |var, value|
116
+ old_env[var] = ENV[var.to_s]
117
+ ENV[var.to_s] = value
118
+ end
119
+
120
+ yield
121
+ ensure
122
+ old_env.each { |var, value| ENV[var.to_s] = value }
123
+ end
57
124
  end
58
125
  end
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module HamlLint
3
- VERSION = '0.13.0'
3
+ VERSION = '0.14.0'
4
4
  end
data/lib/haml_lint.rb CHANGED
@@ -2,7 +2,7 @@ require 'haml_lint/constants'
2
2
  require 'haml_lint/exceptions'
3
3
  require 'haml_lint/configuration'
4
4
  require 'haml_lint/configuration_loader'
5
- require 'haml_lint/parser'
5
+ require 'haml_lint/document'
6
6
  require 'haml_lint/haml_visitor'
7
7
  require 'haml_lint/lint'
8
8
  require 'haml_lint/linter_registry'
@@ -11,6 +11,7 @@ require 'haml_lint/linter'
11
11
  require 'haml_lint/logger'
12
12
  require 'haml_lint/reporter'
13
13
  require 'haml_lint/report'
14
+ require 'haml_lint/linter_selector'
14
15
  require 'haml_lint/file_finder'
15
16
  require 'haml_lint/runner'
16
17
  require 'haml_lint/utils'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haml_lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brigade Engineering
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-05-03 00:00:00.000000000 Z
12
+ date: 2015-06-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: haml
@@ -97,6 +97,7 @@ files:
97
97
  - lib/haml_lint/configuration.rb
98
98
  - lib/haml_lint/configuration_loader.rb
99
99
  - lib/haml_lint/constants.rb
100
+ - lib/haml_lint/document.rb
100
101
  - lib/haml_lint/exceptions.rb
101
102
  - lib/haml_lint/file_finder.rb
102
103
  - lib/haml_lint/haml_visitor.rb
@@ -124,18 +125,18 @@ files:
124
125
  - lib/haml_lint/linter/unnecessary_interpolation.rb
125
126
  - lib/haml_lint/linter/unnecessary_string_output.rb
126
127
  - lib/haml_lint/linter_registry.rb
128
+ - lib/haml_lint/linter_selector.rb
127
129
  - lib/haml_lint/logger.rb
128
130
  - lib/haml_lint/node_transformer.rb
129
131
  - lib/haml_lint/options.rb
130
- - lib/haml_lint/parser.rb
131
132
  - lib/haml_lint/rake_task.rb
132
133
  - lib/haml_lint/report.rb
133
134
  - lib/haml_lint/reporter.rb
134
135
  - lib/haml_lint/reporter/default_reporter.rb
135
136
  - lib/haml_lint/reporter/json_reporter.rb
137
+ - lib/haml_lint/ruby_extractor.rb
136
138
  - lib/haml_lint/ruby_parser.rb
137
139
  - lib/haml_lint/runner.rb
138
- - lib/haml_lint/script_extractor.rb
139
140
  - lib/haml_lint/tree/comment_node.rb
140
141
  - lib/haml_lint/tree/doctype_node.rb
141
142
  - lib/haml_lint/tree/filter_node.rb
@@ -1,87 +0,0 @@
1
- require 'haml'
2
-
3
- module HamlLint
4
- # Parses a HAML document for inspection by linters.
5
- class Parser
6
- attr_reader :contents, :filename, :lines, :tree
7
-
8
- # Creates a parser containing the parse tree of a HAML document.
9
- #
10
- # @param haml_or_filename [String]
11
- # @param options [Hash]
12
- # @option options [true,false] 'skip_frontmatter' Whether to skip
13
- # frontmatter included by frameworks such as Middleman or Jekyll
14
- def initialize(haml_or_filename, options = {})
15
- if File.exist?(haml_or_filename)
16
- build_from_file(haml_or_filename)
17
- else
18
- build_from_string(haml_or_filename)
19
- end
20
-
21
- process_options(options)
22
-
23
- build_parse_tree
24
- end
25
-
26
- private
27
-
28
- # @param path [String]
29
- def build_from_file(path)
30
- @filename = path
31
- @contents = File.read(path)
32
- end
33
-
34
- # @param haml [String]
35
- def build_from_string(haml)
36
- @contents = haml
37
- end
38
-
39
- def build_parse_tree
40
- original_tree = Haml::Parser.new(@contents, Haml::Options.new).parse
41
-
42
- # Remove the trailing empty HAML comment that the parser creates to signal
43
- # the end of the HAML document
44
- if Gem::Requirement.new('~> 4.0.0').satisfied_by?(Gem.loaded_specs['haml'].version)
45
- original_tree.children.pop
46
- end
47
-
48
- @node_transformer = HamlLint::NodeTransformer.new(self)
49
- @tree = convert_tree(original_tree)
50
- end
51
-
52
- def process_options(options)
53
- if options['skip_frontmatter'] &&
54
- @contents =~ /
55
- # From the start of the string
56
- \A
57
- # First-capture match --- followed by optional whitespace up
58
- # to a newline then 0 or more chars followed by an optional newline.
59
- # This matches the --- and the contents of the frontmatter
60
- (---\s*\n.*?\n?)
61
- # From the start of the line
62
- ^
63
- # Second capture match --- or ... followed by optional whitespace
64
- # and newline. This matches the closing --- for the frontmatter.
65
- (---|\.\.\.)\s*$\n?/mx
66
- @contents = $POSTMATCH
67
- end
68
-
69
- @lines = @contents.split("\n")
70
- end
71
-
72
- # Converts a HAML parse tree to a tree of {HamlLint::Tree::Node} objects.
73
- #
74
- # This provides a cleaner interface with which the linters can interact with
75
- # the parse tree.
76
- def convert_tree(haml_node, parent = nil)
77
- new_node = @node_transformer.transform(haml_node)
78
- new_node.parent = parent
79
-
80
- new_node.children = haml_node.children.map do |child|
81
- convert_tree(child, new_node)
82
- end
83
-
84
- new_node
85
- end
86
- end
87
- end