packwerk 2.2.0 → 2.2.1

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 (187) 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 +201 -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_validator.rb +7 -6
  19. data/lib/packwerk/association_inspector.rb +17 -15
  20. data/lib/packwerk/cache.rb +36 -29
  21. data/lib/packwerk/cli.rb +5 -6
  22. data/lib/packwerk/const_node_inspector.rb +8 -7
  23. data/lib/packwerk/constant_name_inspector.rb +2 -2
  24. data/lib/packwerk/deprecated_references.rb +34 -19
  25. data/lib/packwerk/file_processor.rb +14 -14
  26. data/lib/packwerk/files_for_processing.rb +27 -31
  27. data/lib/packwerk/formatters/offenses_formatter.rb +3 -3
  28. data/lib/packwerk/formatters/progress_formatter.rb +2 -2
  29. data/lib/packwerk/node.rb +1 -294
  30. data/lib/packwerk/node_helpers.rb +335 -0
  31. data/lib/packwerk/node_processor.rb +6 -5
  32. data/lib/packwerk/node_processor_factory.rb +3 -3
  33. data/lib/packwerk/node_visitor.rb +1 -1
  34. data/lib/packwerk/offense_collection.rb +6 -3
  35. data/lib/packwerk/offenses_formatter.rb +2 -2
  36. data/lib/packwerk/package.rb +3 -0
  37. data/lib/packwerk/package_set.rb +2 -0
  38. data/lib/packwerk/parse_run.rb +15 -13
  39. data/lib/packwerk/parsed_constant_definitions.rb +23 -20
  40. data/lib/packwerk/parsers/erb.rb +3 -3
  41. data/lib/packwerk/reference_checking/checkers/checker.rb +16 -3
  42. data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +16 -0
  43. data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +18 -0
  44. data/lib/packwerk/reference_checking/reference_checker.rb +3 -1
  45. data/lib/packwerk/reference_extractor.rb +51 -48
  46. data/lib/packwerk/reference_offense.rb +3 -27
  47. data/lib/packwerk/run_context.rb +3 -3
  48. data/lib/packwerk/spring_command.rb +1 -1
  49. data/lib/packwerk/version.rb +1 -1
  50. data/lib/packwerk.rb +1 -0
  51. data/packwerk.gemspec +4 -12
  52. data/sorbet/rbi/gems/actioncable@7.0.3.1.rbi +2754 -0
  53. data/sorbet/rbi/gems/actionmailbox@7.0.3.1.rbi +1496 -0
  54. data/sorbet/rbi/gems/actionmailer@7.0.3.1.rbi +2362 -0
  55. data/sorbet/rbi/gems/actionpack@7.0.3.1.rbi +19397 -0
  56. data/sorbet/rbi/gems/actiontext@7.0.3.1.rbi +1569 -0
  57. data/sorbet/rbi/gems/actionview@7.0.3.1.rbi +14907 -0
  58. data/sorbet/rbi/gems/activejob@7.0.3.1.rbi +2553 -0
  59. data/sorbet/rbi/gems/activemodel@7.0.3.1.rbi +5999 -0
  60. data/sorbet/rbi/gems/activerecord@7.0.3.1.rbi +37832 -0
  61. data/sorbet/rbi/gems/activestorage@7.0.3.1.rbi +2321 -0
  62. data/sorbet/rbi/gems/activesupport@7.0.3.1.rbi +18818 -0
  63. data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +11722 -0
  64. data/sorbet/rbi/gems/constant_resolver@0.2.0.rbi +90 -0
  65. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1079 -0
  66. data/sorbet/rbi/gems/digest@3.1.0.rbi +189 -0
  67. data/sorbet/rbi/gems/erubi@1.11.0.rbi +140 -0
  68. data/sorbet/rbi/gems/globalid@1.0.0.rbi +572 -0
  69. data/sorbet/rbi/gems/i18n@1.12.0.rbi +2296 -0
  70. data/sorbet/rbi/gems/json@2.6.2.rbi +1548 -0
  71. data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +8 -0
  72. data/sorbet/rbi/gems/loofah@2.18.0.rbi +877 -0
  73. data/sorbet/rbi/gems/m@1.6.0.rbi +257 -0
  74. data/sorbet/rbi/gems/marcel@1.0.2.rbi +220 -0
  75. data/sorbet/rbi/gems/mini_mime@1.1.2.rbi +170 -0
  76. data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +8 -0
  77. data/sorbet/rbi/gems/minitest-focus@1.3.1.rbi +104 -0
  78. data/sorbet/rbi/gems/minitest@5.16.2.rbi +2136 -0
  79. data/sorbet/rbi/gems/mocha@1.14.0.rbi +4177 -0
  80. data/sorbet/rbi/gems/net-imap@0.2.3.rbi +2147 -0
  81. data/sorbet/rbi/gems/net-pop@0.1.1.rbi +926 -0
  82. data/sorbet/rbi/gems/net-protocol@0.1.3.rbi +11 -0
  83. data/sorbet/rbi/gems/net-smtp@0.3.1.rbi +1108 -0
  84. data/sorbet/rbi/gems/netrc@0.11.0.rbi +153 -0
  85. data/sorbet/rbi/gems/nio4r@2.5.8.rbi +292 -0
  86. data/sorbet/rbi/gems/nokogiri@1.13.8.rbi +6478 -0
  87. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  88. data/sorbet/rbi/gems/parser@3.1.2.1.rbi +9029 -0
  89. data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +8 -0
  90. data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
  91. data/sorbet/rbi/gems/racc@1.6.0.rbi +152 -0
  92. data/sorbet/rbi/gems/rack-test@2.0.2.rbi +953 -0
  93. data/sorbet/rbi/gems/rack@2.2.4.rbi +5636 -0
  94. data/sorbet/rbi/gems/rails-html-sanitizer@1.4.3.rbi +688 -0
  95. data/sorbet/rbi/gems/rails@7.0.3.1.rbi +8 -0
  96. data/sorbet/rbi/gems/railties@7.0.3.1.rbi +3507 -0
  97. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +392 -0
  98. data/sorbet/rbi/gems/rake@13.0.6.rbi +2924 -0
  99. data/sorbet/rbi/gems/rbi@0.0.15.rbi +3007 -0
  100. data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +3383 -0
  101. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4714 -0
  102. data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +6961 -0
  103. data/sorbet/rbi/gems/rubocop-performance@1.14.3.rbi +2986 -0
  104. data/sorbet/rbi/gems/{rubocop-shopify@2.0.1.rbi → rubocop-shopify@2.9.0.rbi} +4 -4
  105. data/sorbet/rbi/gems/rubocop-sorbet@0.6.11.rbi +992 -0
  106. data/sorbet/rbi/gems/rubocop@1.34.1.rbi +51820 -0
  107. data/sorbet/rbi/gems/ruby-lsp@0.2.1.rbi +11 -0
  108. data/sorbet/rbi/gems/smart_properties@1.17.0.rbi +474 -0
  109. data/sorbet/rbi/gems/spoom@1.1.11.rbi +2181 -0
  110. data/sorbet/rbi/gems/spring@4.0.0.rbi +411 -0
  111. data/sorbet/rbi/gems/strscan@3.0.4.rbi +8 -0
  112. data/sorbet/rbi/gems/syntax_tree@3.3.0.rbi +8 -0
  113. data/sorbet/rbi/gems/tapioca@0.9.2.rbi +3181 -0
  114. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  115. data/sorbet/rbi/gems/timeout@0.3.0.rbi +142 -0
  116. data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +5896 -0
  117. data/sorbet/rbi/gems/unicode-display_width@2.2.0.rbi +48 -0
  118. data/sorbet/rbi/gems/unparser@0.6.5.rbi +4529 -0
  119. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2582 -0
  120. data/sorbet/rbi/gems/websocket-driver@0.7.5.rbi +993 -0
  121. data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +388 -0
  122. data/sorbet/rbi/gems/yard@0.9.28.rbi +18242 -0
  123. data/sorbet/rbi/gems/zeitwerk@2.6.0.rbi +867 -0
  124. data/sorbet/rbi/shims/psych.rbi +5 -0
  125. data/sorbet/tapioca/require.rb +2 -3
  126. metadata +88 -157
  127. data/.github/probots.yml +0 -2
  128. data/library.yml +0 -6
  129. data/service.yml +0 -1
  130. data/sorbet/rbi/gems/actioncable@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -860
  131. data/sorbet/rbi/gems/actionmailbox@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -568
  132. data/sorbet/rbi/gems/actionmailer@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -587
  133. data/sorbet/rbi/gems/actionpack@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -5314
  134. data/sorbet/rbi/gems/actiontext@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -699
  135. data/sorbet/rbi/gems/actionview@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -2515
  136. data/sorbet/rbi/gems/activejob@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -624
  137. data/sorbet/rbi/gems/activemodel@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -1248
  138. data/sorbet/rbi/gems/activerecord@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -8363
  139. data/sorbet/rbi/gems/activestorage@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -876
  140. data/sorbet/rbi/gems/activesupport@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -3987
  141. data/sorbet/rbi/gems/colorize@0.8.1.rbi +0 -40
  142. data/sorbet/rbi/gems/commander@4.5.2.rbi +0 -8
  143. data/sorbet/rbi/gems/concurrent-ruby@1.1.8.rbi +0 -1969
  144. data/sorbet/rbi/gems/constant_resolver@0.1.5.rbi +0 -26
  145. data/sorbet/rbi/gems/erubi@1.10.0.rbi +0 -41
  146. data/sorbet/rbi/gems/globalid@0.4.2.rbi +0 -178
  147. data/sorbet/rbi/gems/highline@2.0.3.rbi +0 -8
  148. data/sorbet/rbi/gems/i18n@1.8.10.rbi +0 -600
  149. data/sorbet/rbi/gems/loofah@2.9.0.rbi +0 -274
  150. data/sorbet/rbi/gems/m@1.5.1.rbi +0 -108
  151. data/sorbet/rbi/gems/marcel@1.0.0.rbi +0 -70
  152. data/sorbet/rbi/gems/mini_mime@1.0.3.rbi +0 -71
  153. data/sorbet/rbi/gems/minitest-focus@1.2.1.rbi +0 -8
  154. data/sorbet/rbi/gems/minitest@5.14.4.rbi +0 -544
  155. data/sorbet/rbi/gems/mocha@1.12.0.rbi +0 -953
  156. data/sorbet/rbi/gems/nio4r@2.5.7.rbi +0 -90
  157. data/sorbet/rbi/gems/nokogiri@1.11.2.rbi +0 -1647
  158. data/sorbet/rbi/gems/parallel@1.20.1.rbi +0 -117
  159. data/sorbet/rbi/gems/parlour@6.0.0.rbi +0 -1272
  160. data/sorbet/rbi/gems/parser@3.0.0.0.rbi +0 -1745
  161. data/sorbet/rbi/gems/pry@0.14.0.rbi +0 -8
  162. data/sorbet/rbi/gems/psych@3.3.2.rbi +0 -24
  163. data/sorbet/rbi/gems/racc@1.5.2.rbi +0 -57
  164. data/sorbet/rbi/gems/rack-test@1.1.0.rbi +0 -335
  165. data/sorbet/rbi/gems/rack@2.2.3.rbi +0 -1718
  166. data/sorbet/rbi/gems/rails-html-sanitizer@1.3.0.rbi +0 -213
  167. data/sorbet/rbi/gems/rails@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -8
  168. data/sorbet/rbi/gems/railties@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -880
  169. data/sorbet/rbi/gems/rainbow@3.0.0.rbi +0 -155
  170. data/sorbet/rbi/gems/rake@13.0.3.rbi +0 -837
  171. data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +0 -8
  172. data/sorbet/rbi/gems/rexml@3.2.4.rbi +0 -8
  173. data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +0 -8
  174. data/sorbet/rbi/gems/rubocop-performance@1.10.2.rbi +0 -8
  175. data/sorbet/rbi/gems/rubocop-sorbet@0.6.1.rbi +0 -8
  176. data/sorbet/rbi/gems/rubocop@1.12.0.rbi +0 -8
  177. data/sorbet/rbi/gems/smart_properties@1.15.0.rbi +0 -168
  178. data/sorbet/rbi/gems/spoom@1.1.0.rbi +0 -1061
  179. data/sorbet/rbi/gems/spring@2.1.1.rbi +0 -160
  180. data/sorbet/rbi/gems/sprockets-rails@3.2.2.rbi +0 -451
  181. data/sorbet/rbi/gems/sprockets@4.0.2.rbi +0 -1133
  182. data/sorbet/rbi/gems/tapioca@0.4.19.rbi +0 -603
  183. data/sorbet/rbi/gems/thor@1.1.0.rbi +0 -893
  184. data/sorbet/rbi/gems/tzinfo@2.0.4.rbi +0 -566
  185. data/sorbet/rbi/gems/unicode-display_width@2.0.0.rbi +0 -8
  186. data/sorbet/rbi/gems/websocket-driver@0.7.3.rbi +0 -438
  187. 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
@@ -89,12 +104,12 @@ module Packwerk
89
104
 
90
105
  private
91
106
 
92
- sig { returns(ENTRIES_TYPE) }
107
+ sig { returns(EntriesType) }
93
108
  def prepare_entries_for_dump
94
109
  @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!
110
+ package_violations.each do |_, entries_for_constant|
111
+ entries_for_constant["violations"].sort!.uniq!
112
+ entries_for_constant["files"].sort!.uniq!
98
113
  end
99
114
  @new_entries[package_name] = package_violations.sort.to_h
100
115
  end
@@ -102,7 +117,7 @@ module Packwerk
102
117
  @new_entries = @new_entries.sort.to_h
103
118
  end
104
119
 
105
- sig { returns(ENTRIES_TYPE) }
120
+ sig { returns(EntriesType) }
106
121
  def deprecated_references
107
122
  @deprecated_references ||= if File.exist?(@filepath)
108
123
  load_yaml(@filepath)
@@ -111,7 +126,7 @@ module Packwerk
111
126
  end
112
127
  end
113
128
 
114
- sig { params(filepath: String).returns(ENTRIES_TYPE) }
129
+ sig { params(filepath: String).returns(EntriesType) }
115
130
  def load_yaml(filepath)
116
131
  YAML.load_file(filepath) || {}
117
132
  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