packwerk 2.2.0 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
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