query_packwerk 0.1.25 → 0.1.26

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17113dea9c0b350f64d243c1a5006ecdf43852d7f33365f651fe398fe07e0a33
4
- data.tar.gz: 97a781754bd44f134f48dddcb055ad8708b99bbde8aa78d121cc7d20193a6097
3
+ metadata.gz: 98b9a4d914a8be019fa0b1795cd527ac2bfd4ce7931b1e5a204704b5bb503847
4
+ data.tar.gz: 209fa3baaa03c0687daca480890ff32ec6c2111c1c902c3088771614d4127c53
5
5
  SHA512:
6
- metadata.gz: d8587392bb0d8934d219c4d55b933b8102dda8eedc6f5214533f78b6d3cf3fbc17918ae84e4fd3577539f59e9faf51d1bb1f81132af195c650cebfa3585b825a
7
- data.tar.gz: 92356183da79a996c30433570254c86090c5df4924b0fceb747551253e885298ce1ffc73dc21dd45666e5329f95bbb90a583977a894586820dca941ff951d51c
6
+ metadata.gz: 406b5e9039a31dda4264391d1287fa89684dc21a982521c212734c05055e727ecaea62b9ed234e296a151587fd9cbaee5dd99915573f5ccb90429509f12de046
7
+ data.tar.gz: e11dfd82ec7324d499e37c0214d7b12ad735d03732f996bcd8e8397f36f684fd99ff6e3b58fa73a1adbc63f7e87d4be455e62814bb690e51cca0d27d7d8d22dc
@@ -9,7 +9,9 @@ module QueryPackwerk
9
9
  class FileCache
10
10
  extend T::Sig
11
11
 
12
- RUBY_FILE = T.let(/\.(rb|rake)\z/, Regexp)
12
+ # The files packwerk parses as Ruby, from Packwerk::Parsers::Factory
13
+ RUBY_FILE = T.let(/(\.(rb|rake|builder|gemspec|ru)|Gemfile|Rakefile)\z/, Regexp)
14
+ ERB_FILE = T.let(/\.erb\z/, Regexp)
13
15
 
14
16
  sig { void }
15
17
  def initialize
@@ -62,12 +64,18 @@ module QueryPackwerk
62
64
 
63
65
  sig { params(file_name: String).returns(RuboCop::AST::Node) }
64
66
  def get_file_ast(file_name)
65
- @file_ast_cache[file_name] ||= ast_from(get_file(file_name))
67
+ @file_ast_cache[file_name] ||= begin
68
+ source = processed_source(ruby_source(file_name))
69
+ warn_unparseable(file_name, source) unless source.valid_syntax?
70
+
71
+ # The AST is also nil for source with no code, like a template that's only HTML
72
+ source.ast || ast_from('x')
73
+ end
66
74
  end
67
75
 
68
76
  sig { params(file_name: String).returns(String) }
69
77
  def get_file(file_name)
70
- @file_cache[file_name] ||= if RUBY_FILE.match?(file_name) && File.exist?(file_name)
78
+ @file_cache[file_name] ||= if (RUBY_FILE.match?(file_name) || ERB_FILE.match?(file_name)) && File.exist?(file_name)
71
79
  File.read(file_name)
72
80
  else
73
81
  'x'
@@ -84,7 +92,7 @@ module QueryPackwerk
84
92
  absolute_const_node = ast_from(class_name)
85
93
  relative_const_node = ast_from(class_name.delete_prefix('::'))
86
94
 
87
- @file_const_cache[const_key] = get_file_ast(file_name).each_descendant.select do |node|
95
+ @file_const_cache[const_key] = get_file_ast(file_name).each_node.select do |node|
88
96
  next false unless node.const_type?
89
97
 
90
98
  node == absolute_const_node || node == relative_const_node
@@ -133,10 +141,32 @@ module QueryPackwerk
133
141
 
134
142
  private
135
143
 
144
+ # Blanks out everything in an ERB template except its Ruby code, so line numbers still match the template
145
+ sig { params(file_name: String).returns(String) }
146
+ def ruby_source(file_name)
147
+ source = get_file(file_name)
148
+ return source unless ERB_FILE.match?(file_name)
149
+
150
+ Herb.extract_ruby(source, semicolons: true, comments: false, preserve_positions: true)
151
+ end
152
+
153
+ sig { params(string: String).returns(RuboCop::ProcessedSource) }
154
+ def processed_source(string)
155
+ RuboCop::ProcessedSource.new(string, RUBY_VERSION.to_f)
156
+ end
157
+
158
+ sig { params(file_name: String, source: RuboCop::ProcessedSource).void }
159
+ def warn_unparseable(file_name, source)
160
+ error = source.diagnostics.find { |diagnostic| diagnostic.level == :error }
161
+ location = error ? "#{file_name}:#{error.location.line} (#{error.message})" : file_name
162
+
163
+ warn "Could not parse #{location}, so its violations are not included"
164
+ end
165
+
136
166
  # Turns a string into a Ruby AST
137
167
  sig { params(string: String).returns(RuboCop::AST::Node) }
138
168
  def ast_from(string)
139
- RuboCop::ProcessedSource.new(string, RUBY_VERSION.to_f).ast
169
+ processed_source(string).ast
140
170
  end
141
171
 
142
172
  # We can find a constant, but by going up its parents we can find the full call chain.
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module QueryPackwerk
5
- VERSION = '0.1.25'
5
+ VERSION = '0.1.26'
6
6
  end
@@ -9,8 +9,6 @@ module QueryPackwerk
9
9
  class Violation
10
10
  extend T::Sig
11
11
 
12
- # This does not play nicely with ERB files which may have violations
13
- RUBY_FILE = T.let(/\.(rb|rake)\z/, Regexp)
14
12
  ALL_CAPS = T.let(/\A[A-Z_]+\z/, Regexp)
15
13
 
16
14
  sig { returns(QueryPackwerk::Package) }
@@ -168,13 +166,15 @@ module QueryPackwerk
168
166
  load_cache!
169
167
 
170
168
  file_sources = files.flat_map do |file_name|
169
+ lines = @file_cache.get_file(file_name).lines
170
+
171
171
  @file_cache.get_full_sources(file_name: file_name, class_name: class_name).map do |s|
172
172
  line_number = s.loc.line
173
- start_pos = line_number - start_offset
174
- end_pos = line_number + end_offset
173
+ start_pos = [line_number - start_offset, 1].max
174
+ end_pos = [line_number + end_offset, lines.size].min
175
175
 
176
176
  location = "#{file_name}:#{s.loc.line} (L#{start_pos}..#{end_pos})"
177
- context = @file_cache.get_file(file_name).lines.slice(start_pos..end_pos)
177
+ context = lines[(start_pos - 1)...end_pos]
178
178
  full_context = unindent((context || ['']).join)
179
179
 
180
180
  [@file_cache.anonymize_arguments(s.source), "> #{location}\n\n#{full_context}"]
@@ -192,7 +192,9 @@ module QueryPackwerk
192
192
  def sources_with_contexts(start_offset: 3, end_offset: 3)
193
193
  load_sources!
194
194
 
195
- deep_merge_hash_groups(@original_collection) { |v| [v.class_name, v.sources_with_contexts] }
195
+ deep_merge_hash_groups(@original_collection) do |v|
196
+ [v.class_name, v.sources_with_contexts(start_offset:, end_offset:)]
197
+ end
196
198
  end
197
199
 
198
200
  sig { params(start_offset: Integer, end_offset: Integer).returns(String) }
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'sorbet-runtime'
5
+ require 'herb'
5
6
  require 'parse_packwerk'
6
7
  require 'rubocop'
7
8
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: query_packwerk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.25
4
+ version: 0.1.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineering
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: herb
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.9'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.9'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: packwerk
28
42
  requirement: !ruby/object:Gem::Requirement