packwerk 2.2.0 → 2.2.2

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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +29 -20
  3. data/.github/workflows/cla.yml +22 -0
  4. data/.rubocop.yml +48 -19
  5. data/Gemfile +7 -2
  6. data/Gemfile.lock +202 -175
  7. data/README.md +1 -1
  8. data/RESOLVING_VIOLATIONS.md +81 -0
  9. data/Rakefile +1 -1
  10. data/USAGE.md +14 -5
  11. data/bin/m +1 -1
  12. data/bin/rake +1 -1
  13. data/bin/rubocop +1 -1
  14. data/bin/srb +1 -1
  15. data/bin/tapioca +1 -1
  16. data/gemfiles/Gemfile-rails-6-0 +1 -1
  17. data/gemfiles/Gemfile-rails-6-1 +22 -0
  18. data/lib/packwerk/application_load_paths.rb +1 -1
  19. data/lib/packwerk/application_validator.rb +7 -6
  20. data/lib/packwerk/association_inspector.rb +17 -15
  21. data/lib/packwerk/cache.rb +36 -29
  22. data/lib/packwerk/cli.rb +24 -20
  23. data/lib/packwerk/const_node_inspector.rb +8 -7
  24. data/lib/packwerk/constant_name_inspector.rb +2 -2
  25. data/lib/packwerk/deprecated_references.rb +40 -20
  26. data/lib/packwerk/file_processor.rb +14 -14
  27. data/lib/packwerk/files_for_processing.rb +27 -31
  28. data/lib/packwerk/formatters/offenses_formatter.rb +3 -3
  29. data/lib/packwerk/formatters/progress_formatter.rb +2 -2
  30. data/lib/packwerk/node.rb +1 -294
  31. data/lib/packwerk/node_helpers.rb +335 -0
  32. data/lib/packwerk/node_processor.rb +6 -5
  33. data/lib/packwerk/node_processor_factory.rb +3 -3
  34. data/lib/packwerk/node_visitor.rb +1 -1
  35. data/lib/packwerk/offense_collection.rb +27 -8
  36. data/lib/packwerk/offenses_formatter.rb +2 -2
  37. data/lib/packwerk/package.rb +3 -0
  38. data/lib/packwerk/package_set.rb +2 -0
  39. data/lib/packwerk/parse_run.rb +29 -20
  40. data/lib/packwerk/parsed_constant_definitions.rb +23 -20
  41. data/lib/packwerk/parsers/erb.rb +3 -3
  42. data/lib/packwerk/reference_checking/checkers/checker.rb +16 -3
  43. data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +16 -0
  44. data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +18 -0
  45. data/lib/packwerk/reference_checking/reference_checker.rb +3 -1
  46. data/lib/packwerk/reference_extractor.rb +51 -48
  47. data/lib/packwerk/reference_offense.rb +3 -27
  48. data/lib/packwerk/run_context.rb +9 -8
  49. data/lib/packwerk/spring_command.rb +1 -1
  50. data/lib/packwerk/version.rb +1 -1
  51. data/lib/packwerk.rb +1 -0
  52. data/packwerk.gemspec +5 -12
  53. data/sorbet/rbi/gems/actioncable@7.0.3.1.rbi +2754 -0
  54. data/sorbet/rbi/gems/actionmailbox@7.0.3.1.rbi +1496 -0
  55. data/sorbet/rbi/gems/actionmailer@7.0.3.1.rbi +2362 -0
  56. data/sorbet/rbi/gems/actionpack@7.0.3.1.rbi +19397 -0
  57. data/sorbet/rbi/gems/actiontext@7.0.3.1.rbi +1569 -0
  58. data/sorbet/rbi/gems/actionview@7.0.3.1.rbi +14907 -0
  59. data/sorbet/rbi/gems/activejob@7.0.3.1.rbi +2553 -0
  60. data/sorbet/rbi/gems/activemodel@7.0.3.1.rbi +5999 -0
  61. data/sorbet/rbi/gems/activerecord@7.0.3.1.rbi +37832 -0
  62. data/sorbet/rbi/gems/activestorage@7.0.3.1.rbi +2321 -0
  63. data/sorbet/rbi/gems/activesupport@7.0.3.1.rbi +18818 -0
  64. data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +11722 -0
  65. data/sorbet/rbi/gems/constant_resolver@0.2.0.rbi +90 -0
  66. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1079 -0
  67. data/sorbet/rbi/gems/digest@3.1.0.rbi +189 -0
  68. data/sorbet/rbi/gems/erubi@1.11.0.rbi +140 -0
  69. data/sorbet/rbi/gems/globalid@1.0.0.rbi +572 -0
  70. data/sorbet/rbi/gems/i18n@1.12.0.rbi +2296 -0
  71. data/sorbet/rbi/gems/json@2.6.2.rbi +1548 -0
  72. data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +8 -0
  73. data/sorbet/rbi/gems/loofah@2.18.0.rbi +877 -0
  74. data/sorbet/rbi/gems/m@1.6.0.rbi +257 -0
  75. data/sorbet/rbi/gems/marcel@1.0.2.rbi +220 -0
  76. data/sorbet/rbi/gems/mini_mime@1.1.2.rbi +170 -0
  77. data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +8 -0
  78. data/sorbet/rbi/gems/minitest-focus@1.3.1.rbi +104 -0
  79. data/sorbet/rbi/gems/minitest@5.16.2.rbi +2136 -0
  80. data/sorbet/rbi/gems/mocha@1.14.0.rbi +4177 -0
  81. data/sorbet/rbi/gems/net-imap@0.2.3.rbi +2147 -0
  82. data/sorbet/rbi/gems/net-pop@0.1.1.rbi +926 -0
  83. data/sorbet/rbi/gems/net-protocol@0.1.3.rbi +11 -0
  84. data/sorbet/rbi/gems/net-smtp@0.3.1.rbi +1108 -0
  85. data/sorbet/rbi/gems/netrc@0.11.0.rbi +153 -0
  86. data/sorbet/rbi/gems/nio4r@2.5.8.rbi +292 -0
  87. data/sorbet/rbi/gems/nokogiri@1.13.8.rbi +6478 -0
  88. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  89. data/sorbet/rbi/gems/parser@3.1.2.1.rbi +9029 -0
  90. data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +8 -0
  91. data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
  92. data/sorbet/rbi/gems/racc@1.6.0.rbi +152 -0
  93. data/sorbet/rbi/gems/rack-test@2.0.2.rbi +953 -0
  94. data/sorbet/rbi/gems/rack@2.2.4.rbi +5636 -0
  95. data/sorbet/rbi/gems/rails-html-sanitizer@1.4.3.rbi +688 -0
  96. data/sorbet/rbi/gems/rails@7.0.3.1.rbi +8 -0
  97. data/sorbet/rbi/gems/railties@7.0.3.1.rbi +3507 -0
  98. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +392 -0
  99. data/sorbet/rbi/gems/rake@13.0.6.rbi +2924 -0
  100. data/sorbet/rbi/gems/rbi@0.0.15.rbi +3007 -0
  101. data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +3383 -0
  102. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4714 -0
  103. data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +6961 -0
  104. data/sorbet/rbi/gems/rubocop-performance@1.14.3.rbi +2986 -0
  105. data/sorbet/rbi/gems/{rubocop-shopify@2.0.1.rbi → rubocop-shopify@2.9.0.rbi} +4 -4
  106. data/sorbet/rbi/gems/rubocop-sorbet@0.6.11.rbi +992 -0
  107. data/sorbet/rbi/gems/rubocop@1.34.1.rbi +51820 -0
  108. data/sorbet/rbi/gems/ruby-lsp@0.2.1.rbi +11 -0
  109. data/sorbet/rbi/gems/smart_properties@1.17.0.rbi +474 -0
  110. data/sorbet/rbi/gems/spoom@1.1.11.rbi +2181 -0
  111. data/sorbet/rbi/gems/spring@4.0.0.rbi +411 -0
  112. data/sorbet/rbi/gems/strscan@3.0.4.rbi +8 -0
  113. data/sorbet/rbi/gems/syntax_tree@3.3.0.rbi +8 -0
  114. data/sorbet/rbi/gems/tapioca@0.9.2.rbi +3181 -0
  115. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  116. data/sorbet/rbi/gems/timeout@0.3.0.rbi +142 -0
  117. data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +5896 -0
  118. data/sorbet/rbi/gems/unicode-display_width@2.2.0.rbi +48 -0
  119. data/sorbet/rbi/gems/unparser@0.6.5.rbi +4529 -0
  120. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2582 -0
  121. data/sorbet/rbi/gems/websocket-driver@0.7.5.rbi +993 -0
  122. data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +388 -0
  123. data/sorbet/rbi/gems/yard@0.9.28.rbi +18242 -0
  124. data/sorbet/rbi/gems/zeitwerk@2.6.0.rbi +867 -0
  125. data/sorbet/rbi/shims/psych.rbi +5 -0
  126. data/sorbet/tapioca/require.rb +2 -3
  127. metadata +91 -146
  128. data/.github/probots.yml +0 -2
  129. data/library.yml +0 -6
  130. data/service.yml +0 -1
  131. data/sorbet/rbi/gems/actioncable@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -860
  132. data/sorbet/rbi/gems/actionmailbox@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -568
  133. data/sorbet/rbi/gems/actionmailer@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -587
  134. data/sorbet/rbi/gems/actionpack@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -5314
  135. data/sorbet/rbi/gems/actiontext@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -699
  136. data/sorbet/rbi/gems/actionview@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -2515
  137. data/sorbet/rbi/gems/activejob@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -624
  138. data/sorbet/rbi/gems/activemodel@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -1248
  139. data/sorbet/rbi/gems/activerecord@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -8363
  140. data/sorbet/rbi/gems/activestorage@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -876
  141. data/sorbet/rbi/gems/activesupport@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -3987
  142. data/sorbet/rbi/gems/colorize@0.8.1.rbi +0 -40
  143. data/sorbet/rbi/gems/commander@4.5.2.rbi +0 -8
  144. data/sorbet/rbi/gems/concurrent-ruby@1.1.8.rbi +0 -1969
  145. data/sorbet/rbi/gems/constant_resolver@0.1.5.rbi +0 -26
  146. data/sorbet/rbi/gems/erubi@1.10.0.rbi +0 -41
  147. data/sorbet/rbi/gems/globalid@0.4.2.rbi +0 -178
  148. data/sorbet/rbi/gems/highline@2.0.3.rbi +0 -8
  149. data/sorbet/rbi/gems/i18n@1.8.10.rbi +0 -600
  150. data/sorbet/rbi/gems/loofah@2.9.0.rbi +0 -274
  151. data/sorbet/rbi/gems/m@1.5.1.rbi +0 -108
  152. data/sorbet/rbi/gems/marcel@1.0.0.rbi +0 -70
  153. data/sorbet/rbi/gems/mini_mime@1.0.3.rbi +0 -71
  154. data/sorbet/rbi/gems/minitest-focus@1.2.1.rbi +0 -8
  155. data/sorbet/rbi/gems/minitest@5.14.4.rbi +0 -544
  156. data/sorbet/rbi/gems/mocha@1.12.0.rbi +0 -953
  157. data/sorbet/rbi/gems/nio4r@2.5.7.rbi +0 -90
  158. data/sorbet/rbi/gems/nokogiri@1.11.2.rbi +0 -1647
  159. data/sorbet/rbi/gems/parallel@1.20.1.rbi +0 -117
  160. data/sorbet/rbi/gems/parlour@6.0.0.rbi +0 -1272
  161. data/sorbet/rbi/gems/parser@3.0.0.0.rbi +0 -1745
  162. data/sorbet/rbi/gems/pry@0.14.0.rbi +0 -8
  163. data/sorbet/rbi/gems/psych@3.3.2.rbi +0 -24
  164. data/sorbet/rbi/gems/racc@1.5.2.rbi +0 -57
  165. data/sorbet/rbi/gems/rack-test@1.1.0.rbi +0 -335
  166. data/sorbet/rbi/gems/rack@2.2.3.rbi +0 -1718
  167. data/sorbet/rbi/gems/rails-html-sanitizer@1.3.0.rbi +0 -213
  168. data/sorbet/rbi/gems/rails@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -8
  169. data/sorbet/rbi/gems/railties@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -880
  170. data/sorbet/rbi/gems/rainbow@3.0.0.rbi +0 -155
  171. data/sorbet/rbi/gems/rake@13.0.3.rbi +0 -837
  172. data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +0 -8
  173. data/sorbet/rbi/gems/rexml@3.2.4.rbi +0 -8
  174. data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +0 -8
  175. data/sorbet/rbi/gems/rubocop-performance@1.10.2.rbi +0 -8
  176. data/sorbet/rbi/gems/rubocop-sorbet@0.6.1.rbi +0 -8
  177. data/sorbet/rbi/gems/rubocop@1.12.0.rbi +0 -8
  178. data/sorbet/rbi/gems/smart_properties@1.15.0.rbi +0 -168
  179. data/sorbet/rbi/gems/spoom@1.1.0.rbi +0 -1061
  180. data/sorbet/rbi/gems/spring@2.1.1.rbi +0 -160
  181. data/sorbet/rbi/gems/sprockets-rails@3.2.2.rbi +0 -451
  182. data/sorbet/rbi/gems/sprockets@4.0.2.rbi +0 -1133
  183. data/sorbet/rbi/gems/tapioca@0.4.19.rbi +0 -603
  184. data/sorbet/rbi/gems/thor@1.1.0.rbi +0 -893
  185. data/sorbet/rbi/gems/tzinfo@2.0.4.rbi +0 -566
  186. data/sorbet/rbi/gems/unicode-display_width@2.0.0.rbi +0 -8
  187. data/sorbet/rbi/gems/websocket-driver@0.7.3.rbi +0 -438
  188. data/sorbet/rbi/gems/zeitwerk@2.4.2.rbi +0 -177
@@ -7,7 +7,7 @@ module Packwerk
7
7
  class DeprecatedReferences
8
8
  extend T::Sig
9
9
 
10
- ENTRIES_TYPE = T.type_alias do
10
+ EntriesType = T.type_alias do
11
11
  T::Hash[String, T.untyped]
12
12
  end
13
13
 
@@ -15,8 +15,8 @@ module Packwerk
15
15
  def initialize(package, filepath)
16
16
  @package = package
17
17
  @filepath = filepath
18
- @new_entries = T.let({}, ENTRIES_TYPE)
19
- @deprecated_references = T.let(nil, T.nilable(ENTRIES_TYPE))
18
+ @new_entries = T.let({}, EntriesType)
19
+ @deprecated_references = T.let(nil, T.nilable(EntriesType))
20
20
  end
21
21
 
22
22
  sig do
@@ -36,28 +36,43 @@ module Packwerk
36
36
  sig { params(reference: Packwerk::Reference, violation_type: Packwerk::ViolationType).returns(T::Boolean) }
37
37
  def add_entries(reference, violation_type)
38
38
  package_violations = @new_entries.fetch(reference.constant.package.name, {})
39
- entries_for_file = package_violations[reference.constant.name] ||= {}
39
+ entries_for_constant = package_violations[reference.constant.name] ||= {}
40
40
 
41
- entries_for_file["violations"] ||= []
42
- entries_for_file["violations"] << violation_type.serialize
41
+ entries_for_constant["violations"] ||= []
42
+ entries_for_constant["violations"] << violation_type.serialize
43
43
 
44
- entries_for_file["files"] ||= []
45
- entries_for_file["files"] << reference.relative_path.to_s
44
+ entries_for_constant["files"] ||= []
45
+ entries_for_constant["files"] << reference.relative_path.to_s
46
46
 
47
47
  @new_entries[reference.constant.package.name] = package_violations
48
48
  listed?(reference, violation_type: violation_type)
49
49
  end
50
50
 
51
- sig { returns(T::Boolean) }
52
- def stale_violations?
51
+ sig { params(for_files: T::Set[String]).returns(T::Boolean) }
52
+ def stale_violations?(for_files)
53
53
  prepare_entries_for_dump
54
+
54
55
  deprecated_references.any? do |package, package_violations|
55
- package_violations.any? do |constant_name, entries_for_file|
56
+ package_violations_for_files = {}
57
+ package_violations.each do |constant_name, entries_for_constant|
58
+ entries_for_files = for_files & entries_for_constant["files"]
59
+ next if entries_for_files.none?
60
+
61
+ package_violations_for_files[constant_name] = {
62
+ "violations" => entries_for_constant["violations"],
63
+ "files" => entries_for_files.to_a,
64
+ }
65
+ end
66
+
67
+ return true if package_violations_for_files.empty?
68
+
69
+ package_violations_for_files.any? do |constant_name, entries_for_constant|
56
70
  new_entries_violation_types = @new_entries.dig(package, constant_name, "violations")
57
71
  return true if new_entries_violation_types.nil?
58
- if entries_for_file["violations"].all? { |type| new_entries_violation_types.include?(type) }
72
+
73
+ if entries_for_constant["violations"].all? { |type| new_entries_violation_types.include?(type) }
59
74
  stale_violations =
60
- entries_for_file["files"] - Array(@new_entries.dig(package, constant_name, "files"))
75
+ entries_for_constant["files"] - Array(@new_entries.dig(package, constant_name, "files"))
61
76
  stale_violations.any?
62
77
  else
63
78
  return true
@@ -69,7 +84,7 @@ module Packwerk
69
84
  sig { void }
70
85
  def dump
71
86
  if @new_entries.empty?
72
- File.delete(@filepath) if File.exist?(@filepath)
87
+ delete_if_exists
73
88
  else
74
89
  prepare_entries_for_dump
75
90
  message = <<~MESSAGE
@@ -87,14 +102,19 @@ module Packwerk
87
102
  end
88
103
  end
89
104
 
105
+ sig { void }
106
+ def delete_if_exists
107
+ File.delete(@filepath) if File.exist?(@filepath)
108
+ end
109
+
90
110
  private
91
111
 
92
- sig { returns(ENTRIES_TYPE) }
112
+ sig { returns(EntriesType) }
93
113
  def prepare_entries_for_dump
94
114
  @new_entries.each do |package_name, package_violations|
95
- package_violations.each do |_, entries_for_file|
96
- entries_for_file["violations"].sort!.uniq!
97
- entries_for_file["files"].sort!.uniq!
115
+ package_violations.each do |_, entries_for_constant|
116
+ entries_for_constant["violations"].sort!.uniq!
117
+ entries_for_constant["files"].sort!.uniq!
98
118
  end
99
119
  @new_entries[package_name] = package_violations.sort.to_h
100
120
  end
@@ -102,7 +122,7 @@ module Packwerk
102
122
  @new_entries = @new_entries.sort.to_h
103
123
  end
104
124
 
105
- sig { returns(ENTRIES_TYPE) }
125
+ sig { returns(EntriesType) }
106
126
  def deprecated_references
107
127
  @deprecated_references ||= if File.exist?(@filepath)
108
128
  load_yaml(@filepath)
@@ -111,7 +131,7 @@ module Packwerk
111
131
  end
112
132
  end
113
133
 
114
- sig { params(filepath: String).returns(ENTRIES_TYPE) }
134
+ sig { params(filepath: String).returns(EntriesType) }
115
135
  def load_yaml(filepath)
116
136
  YAML.load_file(filepath) || {}
117
137
  rescue Psych::Exception
@@ -35,19 +35,19 @@ module Packwerk
35
35
  end
36
36
 
37
37
  sig do
38
- params(absolute_file: String).returns(ProcessedFile)
38
+ params(relative_file: String).returns(ProcessedFile)
39
39
  end
40
- def call(absolute_file)
41
- parser = parser_for(absolute_file)
40
+ def call(relative_file)
41
+ parser = parser_for(relative_file)
42
42
  if parser.nil?
43
- return ProcessedFile.new(offenses: [UnknownFileTypeResult.new(file: absolute_file)])
43
+ return ProcessedFile.new(offenses: [UnknownFileTypeResult.new(file: relative_file)])
44
44
  end
45
45
 
46
- unresolved_references = @cache.with_cache(absolute_file) do
47
- node = parse_into_ast(absolute_file, parser)
46
+ unresolved_references = @cache.with_cache(relative_file) do
47
+ node = parse_into_ast(relative_file, parser)
48
48
  return ProcessedFile.new unless node
49
49
 
50
- references_from_ast(node, absolute_file)
50
+ references_from_ast(node, relative_file)
51
51
  end
52
52
 
53
53
  ProcessedFile.new(unresolved_references: unresolved_references)
@@ -58,22 +58,22 @@ module Packwerk
58
58
  private
59
59
 
60
60
  sig do
61
- params(node: Parser::AST::Node, absolute_file: String).returns(T::Array[UnresolvedReference])
61
+ params(node: Parser::AST::Node, relative_file: String).returns(T::Array[UnresolvedReference])
62
62
  end
63
- def references_from_ast(node, absolute_file)
63
+ def references_from_ast(node, relative_file)
64
64
  references = []
65
65
 
66
- node_processor = @node_processor_factory.for(absolute_file: absolute_file, node: node)
66
+ node_processor = @node_processor_factory.for(relative_file: relative_file, node: node)
67
67
  node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
68
68
  node_visitor.visit(node, ancestors: [], result: references)
69
69
 
70
70
  references
71
71
  end
72
72
 
73
- sig { params(absolute_file: String, parser: Parsers::ParserInterface).returns(T.untyped) }
74
- def parse_into_ast(absolute_file, parser)
75
- File.open(absolute_file, "r", nil, external_encoding: Encoding::UTF_8) do |file|
76
- parser.call(io: file, file_path: absolute_file)
73
+ sig { params(relative_file: String, parser: Parsers::ParserInterface).returns(T.untyped) }
74
+ def parse_into_ast(relative_file, parser)
75
+ File.open(relative_file, "r", nil, external_encoding: Encoding::UTF_8) do |file|
76
+ parser.call(io: file, file_path: relative_file)
77
77
  end
78
78
  end
79
79
 
@@ -5,7 +5,7 @@ module Packwerk
5
5
  class FilesForProcessing
6
6
  extend T::Sig
7
7
 
8
- AbsoluteFileSet = T.type_alias { T::Set[String] }
8
+ RelativeFileSet = T.type_alias { T::Set[String] }
9
9
 
10
10
  class << self
11
11
  extend T::Sig
@@ -15,7 +15,7 @@ module Packwerk
15
15
  relative_file_paths: T::Array[String],
16
16
  configuration: Configuration,
17
17
  ignore_nested_packages: T::Boolean
18
- ).returns(AbsoluteFileSet)
18
+ ).returns(RelativeFileSet)
19
19
  end
20
20
  def fetch(relative_file_paths:, configuration:, ignore_nested_packages: false)
21
21
  new(relative_file_paths, configuration, ignore_nested_packages).files
@@ -33,15 +33,15 @@ module Packwerk
33
33
  @relative_file_paths = relative_file_paths
34
34
  @configuration = configuration
35
35
  @ignore_nested_packages = ignore_nested_packages
36
- @custom_files = T.let(nil, T.nilable(AbsoluteFileSet))
36
+ @custom_files = T.let(nil, T.nilable(RelativeFileSet))
37
37
  end
38
38
 
39
- sig { returns(AbsoluteFileSet) }
39
+ sig { returns(RelativeFileSet) }
40
40
  def files
41
41
  include_files = if custom_files.empty?
42
42
  configured_included_files
43
43
  else
44
- custom_files
44
+ configured_included_files & custom_files
45
45
  end
46
46
 
47
47
  include_files - configured_excluded_files
@@ -49,59 +49,55 @@ module Packwerk
49
49
 
50
50
  private
51
51
 
52
- sig { returns(AbsoluteFileSet) }
52
+ sig { returns(RelativeFileSet) }
53
53
  def custom_files
54
54
  @custom_files ||= Set.new(
55
55
  @relative_file_paths.map do |relative_file_path|
56
- absolute_file_path = File.expand_path(relative_file_path, @configuration.root_path)
57
- if File.file?(absolute_file_path)
58
- absolute_file_path
56
+ if File.file?(relative_file_path)
57
+ relative_file_path
59
58
  else
60
- custom_included_files(absolute_file_path)
59
+ custom_included_files(relative_file_path)
61
60
  end
62
61
  end
63
62
  ).flatten
64
63
  end
65
64
 
66
- sig { params(absolute_file_path: String).returns(AbsoluteFileSet) }
67
- def custom_included_files(absolute_file_path)
65
+ sig { params(relative_file_path: String).returns(RelativeFileSet) }
66
+ def custom_included_files(relative_file_path)
68
67
  # Note, assuming include globs are always relative paths
69
- absolute_includes = @configuration.include.map do |glob|
70
- File.expand_path(glob, @configuration.root_path)
71
- end
72
-
73
- absolute_files = Dir.glob([File.join(absolute_file_path, "**", "*")]).select do |absolute_path|
74
- absolute_includes.any? do |pattern|
75
- File.fnmatch?(pattern, absolute_path, File::FNM_EXTGLOB)
68
+ relative_includes = @configuration.include
69
+ relative_files = Dir.glob([File.join(relative_file_path, "**", "*")]).select do |relative_path|
70
+ relative_includes.any? do |pattern|
71
+ File.fnmatch?(pattern, relative_path, File::FNM_EXTGLOB)
76
72
  end
77
73
  end
78
74
 
79
75
  if @ignore_nested_packages
80
- nested_packages_absolute_file_paths = Dir.glob(File.join(absolute_file_path, "*", "**", "package.yml"))
81
- nested_packages_absolute_globs = nested_packages_absolute_file_paths.map do |npp|
76
+ nested_packages_relative_file_paths = Dir.glob(File.join(relative_file_path, "*", "**", "package.yml"))
77
+ nested_packages_relative_globs = nested_packages_relative_file_paths.map do |npp|
82
78
  npp.gsub("package.yml", "**/*")
83
79
  end
84
- nested_packages_absolute_globs.each do |absolute_glob|
85
- absolute_files -= Dir.glob(absolute_glob)
80
+ nested_packages_relative_globs.each do |relative_glob|
81
+ relative_files -= Dir.glob(relative_glob)
86
82
  end
87
83
  end
88
84
 
89
- Set.new(absolute_files)
85
+ Set.new(relative_files)
90
86
  end
91
87
 
92
- sig { returns(AbsoluteFileSet) }
88
+ sig { returns(RelativeFileSet) }
93
89
  def configured_included_files
94
- absolute_files_for_globs(@configuration.include)
90
+ relative_files_for_globs(@configuration.include)
95
91
  end
96
92
 
97
- sig { returns(AbsoluteFileSet) }
93
+ sig { returns(RelativeFileSet) }
98
94
  def configured_excluded_files
99
- absolute_files_for_globs(@configuration.exclude)
95
+ relative_files_for_globs(@configuration.exclude)
100
96
  end
101
97
 
102
- sig { params(relative_globs: T::Array[String]).returns(AbsoluteFileSet) }
103
- def absolute_files_for_globs(relative_globs)
104
- Set.new(relative_globs.flat_map { |glob| Dir[File.expand_path(glob, @configuration.root_path)] })
98
+ sig { params(relative_globs: T::Array[String]).returns(RelativeFileSet) }
99
+ def relative_files_for_globs(relative_globs)
100
+ Set.new(relative_globs.flat_map { |glob| Dir[glob] })
105
101
  end
106
102
  end
107
103
  end
@@ -23,9 +23,9 @@ module Packwerk
23
23
  EOS
24
24
  end
25
25
 
26
- sig { override.params(offense_collection: Packwerk::OffenseCollection).returns(String) }
27
- def show_stale_violations(offense_collection)
28
- if offense_collection.stale_violations?
26
+ sig { override.params(offense_collection: Packwerk::OffenseCollection, fileset: T::Set[String]).returns(String) }
27
+ def show_stale_violations(offense_collection, fileset)
28
+ if offense_collection.stale_violations?(fileset)
29
29
  "There were stale violations found, please run `packwerk update-deprecations`"
30
30
  else
31
31
  "No stale violations detected"
@@ -20,10 +20,10 @@ module Packwerk
20
20
  @out.puts("📦 Packwerk is inspecting #{files_size} #{files_string}")
21
21
  end
22
22
 
23
- def started_validation
23
+ def started_validation(&block)
24
24
  @out.puts("📦 Packwerk is running validation...")
25
25
 
26
- execution_time = Benchmark.realtime { yield }
26
+ execution_time = Benchmark.realtime(&block)
27
27
  finished(execution_time)
28
28
 
29
29
  @out.puts("✅ Packages are valid. Use `packwerk check` to run static checks.")
data/lib/packwerk/node.rb CHANGED
@@ -1,301 +1,8 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "parser"
5
- require "parser/ast/node"
6
-
7
4
  module Packwerk
8
- # Convenience methods for working with Parser::AST::Node nodes.
9
- module Node
10
- class TypeError < ArgumentError; end
5
+ class Node
11
6
  Location = Struct.new(:line, :column)
12
-
13
- class << self
14
- extend T::Sig
15
-
16
- def class_or_module_name(class_or_module_node)
17
- case type_of(class_or_module_node)
18
- when CLASS, MODULE
19
- # (class (const nil :Foo) (const nil :Bar) (nil))
20
- # "class Foo < Bar; end"
21
- # (module (const nil :Foo) (nil))
22
- # "module Foo; end"
23
- identifier = class_or_module_node.children[0]
24
- constant_name(identifier)
25
- else
26
- raise TypeError
27
- end
28
- end
29
-
30
- def constant_name(constant_node)
31
- case type_of(constant_node)
32
- when CONSTANT_ROOT_NAMESPACE
33
- ""
34
- when CONSTANT, CONSTANT_ASSIGNMENT, SELF
35
- # (const nil :Foo)
36
- # "Foo"
37
- # (const (cbase) :Foo)
38
- # "::Foo"
39
- # (const (lvar :a) :Foo)
40
- # "a::Foo"
41
- # (casgn nil :Foo (int 1))
42
- # "Foo = 1"
43
- # (casgn (cbase) :Foo (int 1))
44
- # "::Foo = 1"
45
- # (casgn (lvar :a) :Foo (int 1))
46
- # "a::Foo = 1"
47
- # (casgn (self) :Foo (int 1))
48
- # "self::Foo = 1"
49
- namespace, name = constant_node.children
50
- if namespace
51
- [constant_name(namespace), name].join("::")
52
- else
53
- name.to_s
54
- end
55
- else
56
- raise TypeError
57
- end
58
- end
59
-
60
- def each_child(node)
61
- if block_given?
62
- node.children.each do |child|
63
- yield child if child.is_a?(Parser::AST::Node)
64
- end
65
- else
66
- enum_for(:each_child, node)
67
- end
68
- end
69
-
70
- def enclosing_namespace_path(starting_node, ancestors:)
71
- ancestors.select { |n| [CLASS, MODULE].include?(type_of(n)) }
72
- .each_with_object([]) do |node, namespace|
73
- # when evaluating `class Child < Parent`, the const node for `Parent` is a child of the class
74
- # node, so it'll be an ancestor, but `Parent` is not evaluated in the namespace of `Child`, so
75
- # we need to skip it here
76
- next if type_of(node) == CLASS && parent_class(node) == starting_node
77
-
78
- namespace.prepend(class_or_module_name(node))
79
- end
80
- end
81
-
82
- def literal_value(string_or_symbol_node)
83
- case type_of(string_or_symbol_node)
84
- when STRING, SYMBOL
85
- # (str "foo")
86
- # "'foo'"
87
- # (sym :foo)
88
- # ":foo"
89
- string_or_symbol_node.children[0]
90
- else
91
- raise TypeError
92
- end
93
- end
94
-
95
- def location(node)
96
- location = node.location
97
- Location.new(location.line, location.column)
98
- end
99
-
100
- def constant?(node)
101
- type_of(node) == CONSTANT
102
- end
103
-
104
- def constant_assignment?(node)
105
- type_of(node) == CONSTANT_ASSIGNMENT
106
- end
107
-
108
- def class?(node)
109
- type_of(node) == CLASS
110
- end
111
-
112
- def method_call?(node)
113
- type_of(node) == METHOD_CALL
114
- end
115
-
116
- def hash?(node)
117
- type_of(node) == HASH
118
- end
119
-
120
- def string?(node)
121
- type_of(node) == STRING
122
- end
123
-
124
- def symbol?(node)
125
- type_of(node) == SYMBOL
126
- end
127
-
128
- def method_arguments(method_call_node)
129
- raise TypeError unless method_call?(method_call_node)
130
-
131
- # (send (lvar :foo) :bar (int 1))
132
- # "foo.bar(1)"
133
- method_call_node.children.slice(2..-1)
134
- end
135
-
136
- def method_name(method_call_node)
137
- raise TypeError unless method_call?(method_call_node)
138
-
139
- # (send (lvar :foo) :bar (int 1))
140
- # "foo.bar(1)"
141
- method_call_node.children[1]
142
- end
143
-
144
- def module_name_from_definition(node)
145
- case type_of(node)
146
- when CLASS, MODULE
147
- # "class My::Class; end"
148
- # "module My::Module; end"
149
- class_or_module_name(node)
150
- when CONSTANT_ASSIGNMENT
151
- # "My::Class = ..."
152
- # "My::Module = ..."
153
- rvalue = node.children.last
154
-
155
- case type_of(rvalue)
156
- when METHOD_CALL
157
- # "Class.new"
158
- # "Module.new"
159
- constant_name(node) if module_creation?(rvalue)
160
- when BLOCK
161
- # "Class.new do end"
162
- # "Module.new do end"
163
- constant_name(node) if module_creation?(method_call_node(rvalue))
164
- end
165
- end
166
- end
167
-
168
- def name_location(node)
169
- location = node.location
170
-
171
- if location.respond_to?(:name)
172
- name = location.name
173
- Location.new(name.line, name.column)
174
- end
175
- end
176
-
177
- def parent_class(class_node)
178
- raise TypeError unless type_of(class_node) == CLASS
179
-
180
- # (class (const nil :Foo) (const nil :Bar) (nil))
181
- # "class Foo < Bar; end"
182
- class_node.children[1]
183
- end
184
-
185
- sig { params(ancestors: T::Array[AST::Node]).returns(String) }
186
- def parent_module_name(ancestors:)
187
- definitions = ancestors
188
- .select { |n| [CLASS, MODULE, CONSTANT_ASSIGNMENT, BLOCK].include?(type_of(n)) }
189
-
190
- names = definitions.map do |definition|
191
- name_part_from_definition(definition)
192
- end.compact
193
-
194
- names.empty? ? "Object" : names.reverse.join("::")
195
- end
196
-
197
- def value_from_hash(hash_node, key)
198
- raise TypeError unless hash?(hash_node)
199
- pair = hash_pairs(hash_node).detect { |pair_node| literal_value(hash_pair_key(pair_node)) == key }
200
- hash_pair_value(pair) if pair
201
- end
202
-
203
- private
204
-
205
- BLOCK = :block
206
- CLASS = :class
207
- CONSTANT = :const
208
- CONSTANT_ASSIGNMENT = :casgn
209
- CONSTANT_ROOT_NAMESPACE = :cbase
210
- HASH = :hash
211
- HASH_PAIR = :pair
212
- METHOD_CALL = :send
213
- MODULE = :module
214
- SELF = :self
215
- STRING = :str
216
- SYMBOL = :sym
217
-
218
- private_constant(
219
- :BLOCK, :CLASS, :CONSTANT, :CONSTANT_ASSIGNMENT, :CONSTANT_ROOT_NAMESPACE, :HASH, :HASH_PAIR, :METHOD_CALL,
220
- :MODULE, :SELF, :STRING, :SYMBOL,
221
- )
222
-
223
- def type_of(node)
224
- node.type
225
- end
226
-
227
- def hash_pair_key(hash_pair_node)
228
- raise TypeError unless type_of(hash_pair_node) == HASH_PAIR
229
-
230
- # (pair (int 1) (int 2))
231
- # "1 => 2"
232
- # (pair (sym :answer) (int 42))
233
- # "answer: 42"
234
- hash_pair_node.children[0]
235
- end
236
-
237
- def hash_pair_value(hash_pair_node)
238
- raise TypeError unless type_of(hash_pair_node) == HASH_PAIR
239
-
240
- # (pair (int 1) (int 2))
241
- # "1 => 2"
242
- # (pair (sym :answer) (int 42))
243
- # "answer: 42"
244
- hash_pair_node.children[1]
245
- end
246
-
247
- def hash_pairs(hash_node)
248
- raise TypeError unless hash?(hash_node)
249
-
250
- # (hash (pair (int 1) (int 2)) (pair (int 3) (int 4)))
251
- # "{1 => 2, 3 => 4}"
252
- hash_node.children.select { |n| type_of(n) == HASH_PAIR }
253
- end
254
-
255
- def method_call_node(block_node)
256
- raise TypeError unless type_of(block_node) == BLOCK
257
-
258
- # (block (send (lvar :foo) :bar) (args) (int 42))
259
- # "foo.bar do 42 end"
260
- block_node.children[0]
261
- end
262
-
263
- def module_creation?(node)
264
- # "Class.new"
265
- # "Module.new"
266
- method_call?(node) &&
267
- receiver(node) &&
268
- constant?(receiver(node)) &&
269
- ["Class", "Module"].include?(constant_name(receiver(node))) &&
270
- method_name(node) == :new
271
- end
272
-
273
- def name_from_block_definition(node)
274
- if method_name(method_call_node(node)) == :class_eval
275
- receiver = receiver(node)
276
- constant_name(receiver) if receiver && constant?(receiver)
277
- end
278
- end
279
-
280
- def name_part_from_definition(node)
281
- case type_of(node)
282
- when CLASS, MODULE, CONSTANT_ASSIGNMENT
283
- module_name_from_definition(node)
284
- when BLOCK
285
- name_from_block_definition(node)
286
- end
287
- end
288
-
289
- def receiver(method_call_or_block_node)
290
- case type_of(method_call_or_block_node)
291
- when METHOD_CALL
292
- method_call_or_block_node.children[0]
293
- when BLOCK
294
- receiver(method_call_node(method_call_or_block_node))
295
- else
296
- raise TypeError
297
- end
298
- end
299
- end
300
7
  end
301
8
  end