packwerk 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +14 -5
  3. data/.ruby-version +1 -1
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +129 -111
  6. data/README.md +8 -1
  7. data/USAGE.md +39 -17
  8. data/dev.yml +1 -1
  9. data/exe/packwerk +1 -1
  10. data/gemfiles/Gemfile-rails-6-0 +22 -0
  11. data/lib/packwerk.rb +73 -34
  12. data/lib/packwerk/application_load_paths.rb +3 -2
  13. data/lib/packwerk/application_validator.rb +85 -69
  14. data/lib/packwerk/association_inspector.rb +23 -11
  15. data/lib/packwerk/checker.rb +4 -7
  16. data/lib/packwerk/cli.rb +36 -93
  17. data/lib/packwerk/configuration.rb +10 -2
  18. data/lib/packwerk/const_node_inspector.rb +13 -14
  19. data/lib/packwerk/constant_discovery.rb +2 -0
  20. data/lib/packwerk/constant_name_inspector.rb +0 -1
  21. data/lib/packwerk/dependency_checker.rb +12 -17
  22. data/lib/packwerk/deprecated_references.rb +25 -8
  23. data/lib/packwerk/file_processor.rb +0 -4
  24. data/lib/packwerk/formatters/offenses_formatter.rb +43 -0
  25. data/lib/packwerk/formatters/progress_formatter.rb +9 -4
  26. data/lib/packwerk/generators/configuration_file.rb +0 -1
  27. data/lib/packwerk/inflector.rb +0 -2
  28. data/lib/packwerk/node.rb +9 -2
  29. data/lib/packwerk/node_processor.rb +15 -32
  30. data/lib/packwerk/node_processor_factory.rb +0 -5
  31. data/lib/packwerk/node_visitor.rb +1 -4
  32. data/lib/packwerk/offense.rb +2 -8
  33. data/lib/packwerk/offense_collection.rb +84 -0
  34. data/lib/packwerk/offenses_formatter.rb +15 -0
  35. data/lib/packwerk/output_style.rb +20 -0
  36. data/lib/packwerk/output_styles/coloured.rb +29 -0
  37. data/lib/packwerk/output_styles/plain.rb +26 -0
  38. data/lib/packwerk/package.rb +8 -0
  39. data/lib/packwerk/package_set.rb +8 -5
  40. data/lib/packwerk/parse_run.rb +104 -0
  41. data/lib/packwerk/parsed_constant_definitions.rb +2 -4
  42. data/lib/packwerk/parsers.rb +0 -2
  43. data/lib/packwerk/parsers/erb.rb +4 -2
  44. data/lib/packwerk/parsers/factory.rb +10 -3
  45. data/lib/packwerk/privacy_checker.rb +22 -17
  46. data/lib/packwerk/reference_extractor.rb +0 -8
  47. data/lib/packwerk/reference_offense.rb +49 -0
  48. data/lib/packwerk/result.rb +9 -0
  49. data/lib/packwerk/run_context.rb +4 -20
  50. data/lib/packwerk/sanity_checker.rb +1 -3
  51. data/lib/packwerk/spring_command.rb +1 -1
  52. data/lib/packwerk/version.rb +1 -1
  53. data/lib/packwerk/violation_type.rb +0 -2
  54. data/library.yml +1 -1
  55. data/packwerk.gemspec +1 -0
  56. data/service.yml +1 -4
  57. data/shipit.rubygems.yml +5 -1
  58. data/sorbet/rbi/gems/{actioncable@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actioncable@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +56 -36
  59. data/sorbet/rbi/gems/{actionmailbox@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionmailbox@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +25 -28
  60. data/sorbet/rbi/gems/{actionmailer@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionmailer@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +43 -24
  61. data/sorbet/rbi/gems/{actionpack@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionpack@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +382 -284
  62. data/sorbet/rbi/gems/{actiontext@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actiontext@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +76 -40
  63. data/sorbet/rbi/gems/{actionview@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionview@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +206 -195
  64. data/sorbet/rbi/gems/{activejob@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activejob@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +64 -75
  65. data/sorbet/rbi/gems/{activemodel@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activemodel@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +103 -56
  66. data/sorbet/rbi/gems/{activerecord@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activerecord@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +1250 -898
  67. data/sorbet/rbi/gems/{activestorage@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activestorage@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +92 -120
  68. data/sorbet/rbi/gems/{activesupport@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activesupport@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +292 -193
  69. data/sorbet/rbi/gems/{ast@2.4.1.rbi → ast@2.4.2.rbi} +2 -1
  70. data/sorbet/rbi/gems/{better_html@1.0.15.rbi → better_html@1.0.16.rbi} +2 -2
  71. data/sorbet/rbi/gems/{concurrent-ruby@1.1.6.rbi → concurrent-ruby@1.1.8.rbi} +12 -9
  72. data/sorbet/rbi/gems/{erubi@1.9.0.rbi → erubi@1.10.0.rbi} +3 -1
  73. data/sorbet/rbi/gems/{i18n@1.8.2.rbi → i18n@1.8.10.rbi} +19 -52
  74. data/sorbet/rbi/gems/{loofah@2.5.0.rbi → loofah@2.9.0.rbi} +3 -1
  75. data/sorbet/rbi/gems/marcel@1.0.0.rbi +70 -0
  76. data/sorbet/rbi/gems/{mini_mime@1.0.2.rbi → mini_mime@1.0.3.rbi} +6 -6
  77. data/sorbet/rbi/gems/{mini_portile2@2.4.0.rbi → minitest-focus@1.2.1.rbi} +2 -2
  78. data/sorbet/rbi/gems/{minitest@5.14.0.rbi → minitest@5.14.4.rbi} +31 -29
  79. data/sorbet/rbi/gems/{mocha@1.11.2.rbi → mocha@1.12.0.rbi} +25 -36
  80. data/sorbet/rbi/gems/{nio4r@2.5.2.rbi → nio4r@2.5.7.rbi} +21 -20
  81. data/sorbet/rbi/gems/{nokogiri@1.10.9.rbi → nokogiri@1.11.2.rbi} +193 -154
  82. data/sorbet/rbi/gems/parallel@1.20.1.rbi +117 -0
  83. data/sorbet/rbi/gems/parlour@6.0.0.rbi +1272 -0
  84. data/sorbet/rbi/gems/{parser@2.7.1.4.rbi → parser@3.0.0.0.rbi} +287 -174
  85. data/sorbet/rbi/gems/{pry@0.13.1.rbi → pry@0.14.0.rbi} +1 -1
  86. data/sorbet/rbi/gems/racc@1.5.2.rbi +57 -0
  87. data/sorbet/rbi/gems/{rack@2.2.2.rbi → rack@2.2.3.rbi} +23 -35
  88. data/sorbet/rbi/gems/{rails@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → rails@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +1 -1
  89. data/sorbet/rbi/gems/{railties@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → railties@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +132 -121
  90. data/sorbet/rbi/gems/{rake@13.0.1.rbi → rake@13.0.3.rbi} +16 -20
  91. data/sorbet/rbi/gems/{parallel@1.19.1.rbi → regexp_parser@2.1.1.rbi} +2 -2
  92. data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +8 -0
  93. data/sorbet/rbi/gems/{rubocop-performance@1.5.2.rbi → rubocop-performance@1.10.2.rbi} +1 -1
  94. data/sorbet/rbi/gems/{rubocop-shopify@1.0.2.rbi → rubocop-shopify@2.0.1.rbi} +1 -1
  95. data/sorbet/rbi/gems/{rubocop-sorbet@0.3.7.rbi → rubocop-sorbet@0.6.1.rbi} +1 -1
  96. data/sorbet/rbi/gems/{rubocop@0.82.0.rbi → rubocop@1.12.0.rbi} +1 -1
  97. data/sorbet/rbi/gems/{ruby-progressbar@1.10.1.rbi → ruby-progressbar@1.11.0.rbi} +1 -1
  98. data/sorbet/rbi/gems/spoom@1.1.0.rbi +1061 -0
  99. data/sorbet/rbi/gems/{spring@2.1.0.rbi → spring@2.1.1.rbi} +7 -7
  100. data/sorbet/rbi/gems/{sprockets-rails@3.2.1.rbi → sprockets-rails@3.2.2.rbi} +88 -68
  101. data/sorbet/rbi/gems/{sprockets@4.0.0.rbi → sprockets@4.0.2.rbi} +8 -7
  102. data/sorbet/rbi/gems/{tapioca@0.4.5.rbi → tapioca@0.4.19.rbi} +109 -24
  103. data/sorbet/rbi/gems/{thor@1.0.1.rbi → thor@1.1.0.rbi} +16 -15
  104. data/sorbet/rbi/gems/{tzinfo@2.0.2.rbi → tzinfo@2.0.4.rbi} +21 -2
  105. data/sorbet/rbi/gems/{unicode-display_width@1.7.0.rbi → unicode-display_width@2.0.0.rbi} +1 -1
  106. data/sorbet/rbi/gems/{websocket-driver@0.7.1.rbi → websocket-driver@0.7.3.rbi} +29 -29
  107. data/sorbet/rbi/gems/{websocket-extensions@0.1.4.rbi → websocket-extensions@0.1.5.rbi} +2 -2
  108. data/sorbet/rbi/gems/zeitwerk@2.4.2.rbi +177 -0
  109. data/sorbet/tapioca/require.rb +1 -0
  110. metadata +78 -57
  111. data/lib/packwerk/checking_deprecated_references.rb +0 -40
  112. data/lib/packwerk/output_styles.rb +0 -41
  113. data/lib/packwerk/reference_lister.rb +0 -23
  114. data/lib/packwerk/updating_deprecated_references.rb +0 -51
  115. data/sorbet/rbi/gems/jaro_winkler@1.5.4.rbi +0 -8
  116. data/sorbet/rbi/gems/marcel@0.3.3.rbi +0 -30
  117. data/sorbet/rbi/gems/mimemagic@0.3.5.rbi +0 -47
  118. data/sorbet/rbi/gems/parlour@4.0.1.rbi +0 -561
  119. data/sorbet/rbi/gems/spoom@1.0.4.rbi +0 -418
  120. data/sorbet/rbi/gems/zeitwerk@2.3.0.rbi +0 -8
@@ -1,9 +1,6 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
- require "packwerk/reference_lister"
6
-
7
4
  module Packwerk
8
5
  module Checker
9
6
  extend T::Sig
@@ -11,10 +8,10 @@ module Packwerk
11
8
 
12
9
  interface!
13
10
 
14
- sig { params(reference: Reference, reference_lister: ReferenceLister).returns(T::Boolean).abstract }
15
- def invalid_reference?(reference, reference_lister); end
11
+ sig { returns(ViolationType).abstract }
12
+ def violation_type; end
16
13
 
17
- sig { params(reference: Reference).returns(String).abstract }
18
- def message_for(reference); end
14
+ sig { params(reference: Reference).returns(T::Boolean).abstract }
15
+ def invalid_reference?(reference); end
19
16
  end
20
17
  end
data/lib/packwerk/cli.rb CHANGED
@@ -1,32 +1,32 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
- require "benchmark"
4
- require "sorbet-runtime"
5
-
6
- require "packwerk/application_validator"
7
- require "packwerk/configuration"
8
- require "packwerk/files_for_processing"
9
- require "packwerk/formatters/progress_formatter"
10
- require "packwerk/inflector"
11
- require "packwerk/output_styles"
12
- require "packwerk/run_context"
13
- require "packwerk/updating_deprecated_references"
14
- require "packwerk/checking_deprecated_references"
15
3
 
16
4
  module Packwerk
17
5
  class Cli
18
6
  extend T::Sig
19
7
 
20
- def initialize(run_context: nil, configuration: nil, out: $stdout, err_out: $stderr, style: OutputStyles::Plain)
8
+ sig do
9
+ params(
10
+ configuration: T.nilable(Configuration),
11
+ out: T.any(StringIO, IO),
12
+ err_out: T.any(StringIO, IO),
13
+ style: Packwerk::OutputStyle,
14
+ offenses_formatter: T.nilable(Packwerk::OffensesFormatter)
15
+ ).void
16
+ end
17
+ def initialize(
18
+ configuration: nil,
19
+ out: $stdout,
20
+ err_out: $stderr,
21
+ style: OutputStyles::Plain.new,
22
+ offenses_formatter: nil
23
+ )
21
24
  @out = out
22
25
  @err_out = err_out
23
26
  @style = style
24
27
  @configuration = configuration || Configuration.from_path
25
- @run_context = run_context || Packwerk::RunContext.from_configuration(
26
- @configuration,
27
- reference_lister: ::Packwerk::CheckingDeprecatedReferences.new(@configuration.root_path),
28
- )
29
28
  @progress_formatter = Formatters::ProgressFormatter.new(@out, style: style)
29
+ @offenses_formatter = offenses_formatter || Formatters::OffensesFormatter.new(style: @style)
30
30
  end
31
31
 
32
32
  sig { params(args: T::Array[String]).returns(T.noreturn) }
@@ -44,11 +44,13 @@ module Packwerk
44
44
  when "generate_configs"
45
45
  generate_configs
46
46
  when "check"
47
- check(args)
47
+ output_result(parse_run(args).check)
48
+ when "detect-stale-violations"
49
+ output_result(parse_run(args).detect_stale_violations)
48
50
  when "update"
49
51
  update(args)
50
52
  when "update-deprecations"
51
- update_deprecations(args)
53
+ output_result(parse_run(args).update_deprecations)
52
54
  when "validate"
53
55
  validate(args)
54
56
  when nil, "help"
@@ -96,7 +98,7 @@ module Packwerk
96
98
 
97
99
  def generate_configs
98
100
  configuration_file = Packwerk::Generators::ConfigurationFile.generate(
99
- load_paths: @configuration.load_paths,
101
+ load_paths: Packwerk::ApplicationLoadPaths.extract_relevant_paths,
100
102
  root: @configuration.root_path,
101
103
  out: @out
102
104
  )
@@ -125,60 +127,13 @@ module Packwerk
125
127
 
126
128
  def update(paths)
127
129
  warn("`packwerk update` is deprecated in favor of `packwerk update-deprecations`.")
128
- update_deprecations(paths)
130
+ output_result(parse_run(paths).update_deprecations)
129
131
  end
130
132
 
131
- def update_deprecations(paths)
132
- updating_deprecated_references = ::Packwerk::UpdatingDeprecatedReferences.new(@configuration.root_path)
133
- @run_context = Packwerk::RunContext.from_configuration(
134
- @configuration,
135
- reference_lister: updating_deprecated_references
136
- )
137
-
138
- files = fetch_files_to_process(paths)
139
-
140
- @progress_formatter.started(files)
141
-
142
- all_offenses = T.let([], T.untyped)
143
- execution_time = Benchmark.realtime do
144
- all_offenses = files.flat_map do |path|
145
- @run_context.process_file(file: path).tap { |offenses| mark_progress(offenses) }
146
- end
147
-
148
- updating_deprecated_references.dump_deprecated_references_files
149
- end
150
-
151
- @out.puts # put a new line after the progress dots
152
- show_offenses(all_offenses)
153
- @progress_formatter.finished(execution_time)
154
- @out.puts("✅ `deprecated_references.yml` has been updated.")
155
-
156
- all_offenses.empty?
157
- end
158
-
159
- def check(paths)
160
- files = fetch_files_to_process(paths)
161
-
162
- @progress_formatter.started(files)
163
-
164
- all_offenses = T.let([], T.untyped)
165
- execution_time = Benchmark.realtime do
166
- files.each do |path|
167
- @run_context.process_file(file: path).tap do |offenses|
168
- mark_progress(offenses)
169
- all_offenses.concat(offenses)
170
- end
171
- end
172
- rescue Interrupt
173
- @out.puts
174
- @out.puts("Manually interrupted. Violations caught so far are listed below:")
175
- end
176
-
177
- @out.puts # put a new line after the progress dots
178
- show_offenses(all_offenses)
179
- @progress_formatter.finished(execution_time)
180
-
181
- all_offenses.empty?
133
+ def output_result(result)
134
+ @out.puts
135
+ @out.puts(result.message)
136
+ result.status
182
137
  end
183
138
 
184
139
  def fetch_files_to_process(paths)
@@ -188,14 +143,6 @@ module Packwerk
188
143
  files
189
144
  end
190
145
 
191
- def mark_progress(offenses)
192
- if offenses.empty?
193
- @progress_formatter.mark_as_inspected
194
- else
195
- @progress_formatter.mark_as_failed
196
- end
197
- end
198
-
199
146
  def validate(_paths)
200
147
  warn("`packwerk validate` should be run within the application. "\
201
148
  "Generate the bin script using `packwerk init` and"\
@@ -214,19 +161,6 @@ module Packwerk
214
161
  end
215
162
  end
216
163
 
217
- def show_offenses(offenses)
218
- if offenses.empty?
219
- @out.puts("No offenses detected 🎉")
220
- else
221
- offenses.each do |offense|
222
- @out.puts(offense.to_s(@style))
223
- end
224
-
225
- offenses_string = Inflector.default.pluralize("offense", offenses.length)
226
- @out.puts("#{offenses.length} #{offenses_string} detected")
227
- end
228
- end
229
-
230
164
  def list_validation_errors(result)
231
165
  @out.puts
232
166
  if result.ok?
@@ -245,5 +179,14 @@ module Packwerk
245
179
  false
246
180
  end
247
181
  end
182
+
183
+ def parse_run(paths)
184
+ ParseRun.new(
185
+ files: fetch_files_to_process(paths),
186
+ configuration: @configuration,
187
+ progress_formatter: @progress_formatter,
188
+ offenses_formatter: @offenses_formatter
189
+ )
190
+ end
248
191
  end
249
192
  end
@@ -22,7 +22,10 @@ module Packwerk
22
22
  private
23
23
 
24
24
  def from_packwerk_config(path)
25
- new(YAML.load_file(path), config_path: path)
25
+ new(
26
+ YAML.load_file(path) || {},
27
+ config_path: path
28
+ )
26
29
  end
27
30
  end
28
31
 
@@ -42,10 +45,15 @@ module Packwerk
42
45
  @root_path = File.expand_path(root)
43
46
  @package_paths = configs["package_paths"] || "**/"
44
47
  @custom_associations = configs["custom_associations"] || []
45
- @load_paths = configs["load_paths"]
48
+ @load_paths = configs["load_paths"] || []
46
49
  @inflections_file = File.expand_path(configs["inflections_file"] || "config/inflections.yml", @root_path)
50
+ @parallel = configs.key?("parallel") ? configs["parallel"] : true
47
51
 
48
52
  @config_path = config_path
49
53
  end
54
+
55
+ def parallel?
56
+ @parallel
57
+ end
50
58
  end
51
59
  end
@@ -1,20 +1,24 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/constant_name_inspector"
5
-
6
4
  module Packwerk
7
5
  # Extracts a constant name from an AST node of type :const
8
6
  class ConstNodeInspector
7
+ extend T::Sig
9
8
  include ConstantNameInspector
10
9
 
10
+ sig do
11
+ override
12
+ .params(node: AST::Node, ancestors: T::Array[AST::Node])
13
+ .returns(T.nilable(String))
14
+ end
11
15
  def constant_name_from_node(node, ancestors:)
12
16
  return nil unless Node.constant?(node)
13
17
  parent = ancestors.first
14
18
  return nil unless root_constant?(parent)
15
19
 
16
20
  if parent && constant_in_module_or_class_definition?(node, parent: parent)
17
- fully_qualify_constant(node, ancestors: ancestors)
21
+ fully_qualify_constant(ancestors)
18
22
  else
19
23
  begin
20
24
  Node.constant_name(node)
@@ -28,27 +32,22 @@ module Packwerk
28
32
 
29
33
  # Only process the root `const` node for namespaced constant references. For example, in the
30
34
  # reference `Spam::Eggs::Thing`, we only process the const node associated with `Spam`.
35
+ sig { params(parent: T.nilable(AST::Node)).returns(T::Boolean) }
31
36
  def root_constant?(parent)
32
37
  !(parent && Node.constant?(parent))
33
38
  end
34
39
 
40
+ sig { params(node: AST::Node, parent: AST::Node).returns(T.nilable(T::Boolean)) }
35
41
  def constant_in_module_or_class_definition?(node, parent:)
36
42
  parent_name = Node.module_name_from_definition(parent)
37
43
  parent_name && parent_name == Node.constant_name(node)
38
44
  end
39
45
 
40
- def fully_qualify_constant(node, ancestors:)
46
+ sig { params(ancestors: T::Array[AST::Node]).returns(String) }
47
+ def fully_qualify_constant(ancestors)
41
48
  # We're defining a class with this name, in which case the constant is implicitly fully qualified by its
42
49
  # enclosing namespace
43
- name = Node.parent_module_name(ancestors: ancestors)
44
- name ||= generate_qualified_constant(node, ancestors)
45
- "::" + name
46
- end
47
-
48
- def generate_qualified_constant(node, ancestors:)
49
- namespace_path = Node.enclosing_namespace_path(node, ancestors: ancestors)
50
- constant_name = Node.constant_name(node)
51
- namespace_path.push(constant_name).join("::")
50
+ "::" + Node.parent_module_name(ancestors: ancestors)
52
51
  end
53
52
  end
54
53
  end
@@ -1,6 +1,8 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "constant_resolver"
5
+
4
6
  module Packwerk
5
7
  # Get information about (partially qualified) constants without loading the application code.
6
8
  # Information gathered: Fully qualified name, path to file containing the definition, package,
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ast"
5
- require "sorbet-runtime"
6
5
 
7
6
  module Packwerk
8
7
  # An interface describing some object that can extract a constant name from an AST node
@@ -1,31 +1,26 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "packwerk/violation_type"
5
- require "packwerk/checker"
6
-
7
4
  module Packwerk
8
5
  class DependencyChecker
6
+ extend T::Sig
9
7
  include Checker
10
8
 
9
+ sig { override.returns(ViolationType) }
11
10
  def violation_type
12
11
  ViolationType::Dependency
13
12
  end
14
13
 
15
- def invalid_reference?(reference, reference_lister)
16
- return unless reference.source_package
17
- return unless reference.source_package.enforce_dependencies?
18
- return if reference.source_package.dependency?(reference.constant.package)
19
- return if reference_lister.listed?(reference, violation_type: violation_type)
20
- true
14
+ sig do
15
+ override
16
+ .params(reference: Packwerk::Reference)
17
+ .returns(T::Boolean)
21
18
  end
22
-
23
- def message_for(reference)
24
- "Dependency violation: #{reference.constant.name} belongs to '#{reference.constant.package}', but " \
25
- "'#{reference.source_package}' does not specify a dependency on " \
26
- "'#{reference.constant.package}'.\n" \
27
- "Are we missing an abstraction?\n" \
28
- "Is the code making the reference, and the referenced constant, in the right packages?\n"
19
+ def invalid_reference?(reference)
20
+ return false unless reference.source_package
21
+ return false unless reference.source_package.enforce_dependencies?
22
+ return false if reference.source_package.dependency?(reference.constant.package)
23
+ true
29
24
  end
30
25
  end
31
26
  end
@@ -1,18 +1,13 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
4
  require "yaml"
6
5
 
7
- require "packwerk/reference"
8
- require "packwerk/reference_lister"
9
- require "packwerk/violation_type"
10
-
11
6
  module Packwerk
12
7
  class DeprecatedReferences
13
8
  extend T::Sig
14
- include ReferenceLister
15
9
 
10
+ sig { params(package: Packwerk::Package, filepath: String).void }
16
11
  def initialize(package, filepath)
17
12
  @package = package
18
13
  @filepath = filepath
@@ -22,7 +17,6 @@ module Packwerk
22
17
  sig do
23
18
  params(reference: Packwerk::Reference, violation_type: ViolationType)
24
19
  .returns(T::Boolean)
25
- .override
26
20
  end
27
21
  def listed?(reference, violation_type:)
28
22
  violated_constants_found = deprecated_references.dig(reference.constant.package.name, reference.constant.name)
@@ -34,19 +28,40 @@ module Packwerk
34
28
  violated_constants_found.fetch("violations", []).include?(violation_type.serialize)
35
29
  end
36
30
 
31
+ sig { params(reference: Packwerk::Reference, violation_type: Packwerk::ViolationType).returns(T::Boolean) }
37
32
  def add_entries(reference, violation_type)
38
33
  package_violations = @new_entries.fetch(reference.constant.package.name, {})
39
34
  entries_for_file = package_violations[reference.constant.name] ||= {}
40
35
 
41
36
  entries_for_file["violations"] ||= []
42
- entries_for_file["violations"] << violation_type
37
+ entries_for_file["violations"] << violation_type.serialize
43
38
 
44
39
  entries_for_file["files"] ||= []
45
40
  entries_for_file["files"] << reference.relative_path.to_s
46
41
 
47
42
  @new_entries[reference.constant.package.name] = package_violations
43
+ listed?(reference, violation_type: violation_type)
44
+ end
45
+
46
+ sig { returns(T::Boolean) }
47
+ def stale_violations?
48
+ prepare_entries_for_dump
49
+ deprecated_references.any? do |package, package_violations|
50
+ package_violations.any? do |constant_name, entries_for_file|
51
+ new_entries_violation_types = @new_entries.dig(package, constant_name, "violations")
52
+ return true if new_entries_violation_types.nil?
53
+ if entries_for_file["violations"].all? { |type| new_entries_violation_types.include?(type) }
54
+ stale_violations =
55
+ entries_for_file["files"] - Array(@new_entries.dig(package, constant_name, "files"))
56
+ stale_violations.present?
57
+ else
58
+ return true
59
+ end
60
+ end
61
+ end
48
62
  end
49
63
 
64
+ sig { void }
50
65
  def dump
51
66
  if @new_entries.empty?
52
67
  File.delete(@filepath) if File.exist?(@filepath)
@@ -69,6 +84,7 @@ module Packwerk
69
84
 
70
85
  private
71
86
 
87
+ sig { returns(Hash) }
72
88
  def prepare_entries_for_dump
73
89
  @new_entries.each do |package_name, package_violations|
74
90
  package_violations.each do |_, entries_for_file|
@@ -81,6 +97,7 @@ module Packwerk
81
97
  @new_entries = @new_entries.sort.to_h
82
98
  end
83
99
 
100
+ sig { returns(Hash) }
84
101
  def deprecated_references
85
102
  @deprecated_references ||= if File.exist?(@filepath)
86
103
  YAML.load_file(@filepath) || {}