packwerk 2.2.2 → 3.0.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -5
  3. data/.ruby-version +1 -1
  4. data/Gemfile +0 -1
  5. data/Gemfile.lock +5 -95
  6. data/README.md +2 -7
  7. data/RESOLVING_VIOLATIONS.md +7 -7
  8. data/TROUBLESHOOT.md +1 -23
  9. data/USAGE.md +149 -59
  10. data/dev.yml +1 -1
  11. data/exe/packwerk +1 -0
  12. data/gemfiles/Gemfile-rails-6-1 +1 -1
  13. data/lib/packwerk/application_validator.rb +54 -285
  14. data/lib/packwerk/association_inspector.rb +2 -0
  15. data/lib/packwerk/cache.rb +6 -5
  16. data/lib/packwerk/checker.rb +54 -0
  17. data/lib/packwerk/cli/result.rb +11 -0
  18. data/lib/packwerk/cli.rb +56 -31
  19. data/lib/packwerk/configuration.rb +61 -40
  20. data/lib/packwerk/const_node_inspector.rb +2 -0
  21. data/lib/packwerk/constant_context.rb +8 -0
  22. data/lib/packwerk/constant_discovery.rb +5 -6
  23. data/lib/packwerk/constant_name_inspector.rb +2 -0
  24. data/lib/packwerk/disable_sorbet.rb +41 -0
  25. data/lib/packwerk/extension_loader.rb +24 -0
  26. data/lib/packwerk/file_processor.rb +3 -1
  27. data/lib/packwerk/files_for_processing.rb +25 -12
  28. data/lib/packwerk/formatters/default_offenses_formatter.rb +77 -0
  29. data/lib/packwerk/formatters/progress_formatter.rb +31 -12
  30. data/lib/packwerk/generators/configuration_file.rb +7 -2
  31. data/lib/packwerk/generators/root_package.rb +5 -1
  32. data/lib/packwerk/generators/templates/package.yml +0 -10
  33. data/lib/packwerk/graph.rb +10 -2
  34. data/lib/packwerk/node.rb +1 -1
  35. data/lib/packwerk/node_helpers.rb +14 -7
  36. data/lib/packwerk/node_processor.rb +2 -0
  37. data/lib/packwerk/node_processor_factory.rb +6 -4
  38. data/lib/packwerk/node_visitor.rb +10 -1
  39. data/lib/packwerk/offense_collection.rb +43 -23
  40. data/lib/packwerk/offenses_formatter.rb +59 -2
  41. data/lib/packwerk/package.rb +7 -35
  42. data/lib/packwerk/package_set.rb +1 -1
  43. data/lib/packwerk/{deprecated_references.rb → package_todo.rb} +29 -13
  44. data/lib/packwerk/parse_run.rb +29 -36
  45. data/lib/packwerk/parsed_constant_definitions.rb +28 -5
  46. data/lib/packwerk/parsers/erb.rb +23 -4
  47. data/lib/packwerk/parsers/factory.rb +11 -2
  48. data/lib/packwerk/parsers/parser_interface.rb +1 -1
  49. data/lib/packwerk/parsers/ruby.rb +13 -3
  50. data/lib/packwerk/parsers.rb +6 -2
  51. data/lib/packwerk/{application_load_paths.rb → rails_load_paths.rb} +6 -4
  52. data/lib/packwerk/reference.rb +7 -1
  53. data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +29 -6
  54. data/lib/packwerk/reference_checking/reference_checker.rb +1 -1
  55. data/lib/packwerk/reference_extractor.rb +24 -12
  56. data/lib/packwerk/reference_offense.rb +2 -2
  57. data/lib/packwerk/run_context.rb +7 -10
  58. data/lib/packwerk/spring_command.rb +11 -2
  59. data/lib/packwerk/unresolved_reference.rb +9 -1
  60. data/lib/packwerk/validator/result.rb +18 -0
  61. data/lib/packwerk/validator.rb +90 -0
  62. data/lib/packwerk/validators/dependency_validator.rb +154 -0
  63. data/lib/packwerk/version.rb +1 -1
  64. data/lib/packwerk.rb +64 -26
  65. data/packwerk.gemspec +4 -2
  66. data/sorbet/rbi/gems/{zeitwerk@2.6.0.rbi → zeitwerk@2.6.4.rbi} +291 -228
  67. data/sorbet/rbi/shims/minitest/test.rb +8 -0
  68. data/sorbet/rbi/shims/packwerk/reference.rbi +33 -0
  69. data/sorbet/rbi/shims/packwerk/unresolved_reference.rbi +33 -0
  70. data/sorbet/rbi/shims/parser.rbi +13 -0
  71. metadata +35 -16
  72. data/lib/packwerk/formatters/offenses_formatter.rb +0 -52
  73. data/lib/packwerk/reference_checking/checkers/checker.rb +0 -34
  74. data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +0 -76
  75. data/lib/packwerk/result.rb +0 -9
  76. data/lib/packwerk/sanity_checker.rb +0 -8
  77. data/lib/packwerk/violation_type.rb +0 -11
  78. data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +0 -46
  79. data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +0 -8
@@ -29,4 +29,6 @@ module Packwerk
29
29
  @reference_extractor.reference_from_node(node, ancestors: ancestors, relative_file: @relative_file)
30
30
  end
31
31
  end
32
+
33
+ private_constant :NodeProcessor
32
34
  end
@@ -6,12 +6,12 @@ module Packwerk
6
6
  extend T::Sig
7
7
 
8
8
  const :root_path, String
9
- const :context_provider, Packwerk::ConstantDiscovery
9
+ const :context_provider, ConstantDiscovery
10
10
  const :constant_name_inspectors, T::Array[ConstantNameInspector]
11
11
 
12
12
  sig { params(relative_file: String, node: AST::Node).returns(NodeProcessor) }
13
13
  def for(relative_file:, node:)
14
- ::Packwerk::NodeProcessor.new(
14
+ NodeProcessor.new(
15
15
  reference_extractor: reference_extractor(node: node),
16
16
  relative_file: relative_file,
17
17
  )
@@ -19,13 +19,15 @@ module Packwerk
19
19
 
20
20
  private
21
21
 
22
- sig { params(node: AST::Node).returns(::Packwerk::ReferenceExtractor) }
22
+ sig { params(node: AST::Node).returns(ReferenceExtractor) }
23
23
  def reference_extractor(node:)
24
- ::Packwerk::ReferenceExtractor.new(
24
+ ReferenceExtractor.new(
25
25
  constant_name_inspectors: constant_name_inspectors,
26
26
  root_node: node,
27
27
  root_path: root_path,
28
28
  )
29
29
  end
30
30
  end
31
+
32
+ private_constant :NodeProcessorFactory
31
33
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Packwerk
@@ -11,6 +11,13 @@ module Packwerk
11
11
  @node_processor = node_processor
12
12
  end
13
13
 
14
+ sig do
15
+ params(
16
+ node: Parser::AST::Node,
17
+ ancestors: T::Array[Parser::AST::Node],
18
+ result: T::Array[UnresolvedReference],
19
+ ).void
20
+ end
14
21
  def visit(node, ancestors:, result:)
15
22
  reference = @node_processor.call(node, ancestors)
16
23
  result << reference if reference
@@ -21,4 +28,6 @@ module Packwerk
21
28
  end
22
29
  end
23
30
  end
31
+
32
+ private_constant :NodeVisitor
24
33
  end
@@ -1,6 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "pathname"
5
+
4
6
  module Packwerk
5
7
  class OffenseCollection
6
8
  extend T::Sig
@@ -9,13 +11,14 @@ module Packwerk
9
11
  sig do
10
12
  params(
11
13
  root_path: String,
12
- deprecated_references: T::Hash[Packwerk::Package, Packwerk::DeprecatedReferences]
14
+ package_todo: T::Hash[Packwerk::Package, Packwerk::PackageTodo]
13
15
  ).void
14
16
  end
15
- def initialize(root_path, deprecated_references = {})
17
+ def initialize(root_path, package_todo = {})
16
18
  @root_path = root_path
17
- @deprecated_references = T.let(deprecated_references, T::Hash[Packwerk::Package, Packwerk::DeprecatedReferences])
19
+ @package_todo = T.let(package_todo, T::Hash[Packwerk::Package, Packwerk::PackageTodo])
18
20
  @new_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
21
+ @strict_mode_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
19
22
  @errors = T.let([], T::Array[Packwerk::Offense])
20
23
  end
21
24
 
@@ -25,6 +28,9 @@ module Packwerk
25
28
  sig { returns(T::Array[Packwerk::Offense]) }
26
29
  attr_reader :errors
27
30
 
31
+ sig { returns(T::Array[Packwerk::ReferenceOffense]) }
32
+ attr_reader :strict_mode_violations
33
+
28
34
  sig do
29
35
  params(offense: Packwerk::Offense)
30
36
  .returns(T::Boolean)
@@ -33,7 +39,7 @@ module Packwerk
33
39
  return false unless offense.is_a?(ReferenceOffense)
34
40
 
35
41
  reference = offense.reference
36
- deprecated_references_for(reference.source_package).listed?(reference, violation_type: offense.violation_type)
42
+ package_todo_for(reference.package).listed?(reference, violation_type: offense.violation_type)
37
43
  end
38
44
 
39
45
  sig do
@@ -44,23 +50,25 @@ module Packwerk
44
50
  @errors << offense
45
51
  return
46
52
  end
47
- deprecated_references = deprecated_references_for(offense.reference.source_package)
48
- unless deprecated_references.add_entries(offense.reference, offense.violation_type)
53
+
54
+ if !already_listed?(offense)
49
55
  new_violations << offense
56
+ elsif strict_mode_violation?(offense)
57
+ strict_mode_violations << offense
50
58
  end
51
59
  end
52
60
 
53
61
  sig { params(for_files: T::Set[String]).returns(T::Boolean) }
54
62
  def stale_violations?(for_files)
55
- @deprecated_references.values.any? do |deprecated_references|
56
- deprecated_references.stale_violations?(for_files)
63
+ @package_todo.values.any? do |package_todo|
64
+ package_todo.stale_violations?(for_files)
57
65
  end
58
66
  end
59
67
 
60
68
  sig { params(package_set: Packwerk::PackageSet).void }
61
- def persist_deprecated_references_files(package_set)
62
- dump_deprecated_references_files
63
- cleanup_extra_deprecated_references_files(package_set)
69
+ def persist_package_todo_files(package_set)
70
+ dump_package_todo_files
71
+ cleanup_extra_package_todo_files(package_set)
64
72
  end
65
73
 
66
74
  sig { returns(T::Array[Packwerk::Offense]) }
@@ -70,34 +78,46 @@ module Packwerk
70
78
 
71
79
  private
72
80
 
81
+ sig { params(offense: ReferenceOffense).returns(T::Boolean) }
82
+ def already_listed?(offense)
83
+ package_todo_for(offense.reference.package).add_entries(offense.reference,
84
+ offense.violation_type)
85
+ end
86
+
87
+ sig { params(offense: ReferenceOffense).returns(T::Boolean) }
88
+ def strict_mode_violation?(offense)
89
+ checker = Checker.find(offense.violation_type)
90
+ checker.strict_mode_violation?(offense)
91
+ end
92
+
73
93
  sig { params(package_set: Packwerk::PackageSet).void }
74
- def cleanup_extra_deprecated_references_files(package_set)
75
- packages_without_todos = (package_set.packages.values - @deprecated_references.keys)
94
+ def cleanup_extra_package_todo_files(package_set)
95
+ packages_without_todos = (package_set.packages.values - @package_todo.keys)
76
96
 
77
97
  packages_without_todos.each do |package|
78
- Packwerk::DeprecatedReferences.new(
98
+ Packwerk::PackageTodo.new(
79
99
  package,
80
- deprecated_references_file_for(package),
100
+ package_todo_file_for(package),
81
101
  ).delete_if_exists
82
102
  end
83
103
  end
84
104
 
85
105
  sig { void }
86
- def dump_deprecated_references_files
87
- @deprecated_references.each_value(&:dump)
106
+ def dump_package_todo_files
107
+ @package_todo.each_value(&:dump)
88
108
  end
89
109
 
90
- sig { params(package: Packwerk::Package).returns(Packwerk::DeprecatedReferences) }
91
- def deprecated_references_for(package)
92
- @deprecated_references[package] ||= Packwerk::DeprecatedReferences.new(
110
+ sig { params(package: Packwerk::Package).returns(Packwerk::PackageTodo) }
111
+ def package_todo_for(package)
112
+ @package_todo[package] ||= Packwerk::PackageTodo.new(
93
113
  package,
94
- deprecated_references_file_for(package),
114
+ package_todo_file_for(package),
95
115
  )
96
116
  end
97
117
 
98
118
  sig { params(package: Packwerk::Package).returns(String) }
99
- def deprecated_references_file_for(package)
100
- File.join(@root_path, package.name, "deprecated_references.yml")
119
+ def package_todo_file_for(package)
120
+ File.join(@root_path, package.name, "package_todo.yml")
101
121
  end
102
122
  end
103
123
  end
@@ -6,14 +6,71 @@ module Packwerk
6
6
  extend T::Sig
7
7
  extend T::Helpers
8
8
 
9
- interface!
9
+ abstract!
10
+
11
+ class DuplicateFormatterError < StandardError
12
+ extend T::Sig
13
+
14
+ sig { params(identifier: String).void }
15
+ def initialize(identifier)
16
+ super("Cannot have multiple identifiers with the same key (`#{identifier}`)")
17
+ end
18
+ end
19
+
20
+ class << self
21
+ extend T::Sig
22
+
23
+ sig { params(base: Class).void }
24
+ def included(base)
25
+ @offenses_formatters ||= T.let(@offenses_formatters, T.nilable(T::Array[Class]))
26
+ @offenses_formatters ||= []
27
+ @offenses_formatters << base
28
+ end
29
+
30
+ sig { returns(T::Array[OffensesFormatter]) }
31
+ def all
32
+ T.unsafe(@offenses_formatters).map(&:new)
33
+ end
34
+
35
+ sig { params(identifier: String).returns(OffensesFormatter) }
36
+ def find(identifier)
37
+ formatter_by_identifier(identifier)
38
+ end
39
+
40
+ private
41
+
42
+ sig { params(name: String).returns(OffensesFormatter) }
43
+ def formatter_by_identifier(name)
44
+ @formatter_by_identifier ||= T.let(nil, T.nilable(T::Hash[String, T.nilable(OffensesFormatter)]))
45
+ @formatter_by_identifier ||= begin
46
+ index = T.let({}, T::Hash[String, T.nilable(OffensesFormatter)])
47
+ OffensesFormatter.all.each do |formatter|
48
+ identifier = formatter.identifier
49
+ raise DuplicateFormatterError, identifier if index.key?(identifier)
50
+
51
+ index[identifier] = formatter
52
+ end
53
+ T.let(index, T.nilable(T::Hash[String, T.nilable(OffensesFormatter)]))
54
+ end
55
+
56
+ T.must(T.must(@formatter_by_identifier)[name])
57
+ end
58
+ end
10
59
 
11
60
  sig { abstract.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
12
61
  def show_offenses(offenses)
13
62
  end
14
63
 
15
- sig { abstract.params(offense_collection: Packwerk::OffenseCollection, for_files: T::Set[String]).returns(String) }
64
+ sig { abstract.params(offense_collection: OffenseCollection, for_files: T::Set[String]).returns(String) }
16
65
  def show_stale_violations(offense_collection, for_files)
17
66
  end
67
+
68
+ sig { abstract.returns(String) }
69
+ def identifier
70
+ end
71
+
72
+ sig { abstract.params(strict_mode_violations: T::Array[ReferenceOffense]).returns(String) }
73
+ def show_strict_mode_violations(strict_mode_violations)
74
+ end
18
75
  end
19
76
  end
@@ -17,22 +17,20 @@ module Packwerk
17
17
  sig { returns(T::Array[String]) }
18
18
  attr_reader :dependencies
19
19
 
20
- sig { params(name: String, config: T.nilable(T.any(T::Hash[T.untyped, T.untyped], FalseClass))).void }
21
- def initialize(name:, config:)
20
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
21
+ attr_reader :config
22
+
23
+ sig { params(name: String, config: T.nilable(T::Hash[String, T.untyped])).void }
24
+ def initialize(name:, config: nil)
22
25
  @name = name
23
- @config = T.let(config || {}, T::Hash[T.untyped, T.untyped])
26
+ @config = T.let(config || {}, T::Hash[String, T.untyped])
24
27
  @dependencies = T.let(Array(@config["dependencies"]).freeze, T::Array[String])
25
28
  @public_path = T.let(nil, T.nilable(String))
26
29
  end
27
30
 
28
- sig { returns(T.nilable(T.any(T::Boolean, T::Array[String]))) }
29
- def enforce_privacy
30
- @config["enforce_privacy"]
31
- end
32
-
33
31
  sig { returns(T::Boolean) }
34
32
  def enforce_dependencies?
35
- @config["enforce_dependencies"] == true
33
+ [true, "strict"].include?(@config["enforce_dependencies"])
36
34
  end
37
35
 
38
36
  sig { params(package: Package).returns(T::Boolean) }
@@ -47,32 +45,6 @@ module Packwerk
47
45
  path.start_with?(@name)
48
46
  end
49
47
 
50
- sig { returns(String) }
51
- def public_path
52
- @public_path ||= begin
53
- unprefixed_public_path = user_defined_public_path || "app/public/"
54
-
55
- if root?
56
- unprefixed_public_path
57
- else
58
- File.join(@name, unprefixed_public_path)
59
- end
60
- end
61
- end
62
-
63
- sig { params(path: String).returns(T::Boolean) }
64
- def public_path?(path)
65
- path.start_with?(public_path)
66
- end
67
-
68
- sig { returns(T.nilable(String)) }
69
- def user_defined_public_path
70
- return unless @config["public_path"]
71
- return @config["public_path"] if @config["public_path"].end_with?("/")
72
-
73
- @config["public_path"] + "/"
74
- end
75
-
76
48
  sig { params(other: T.untyped).returns(T.nilable(Integer)) }
77
49
  def <=>(other)
78
50
  return nil unless other.is_a?(self.class)
@@ -26,7 +26,7 @@ module Packwerk
26
26
 
27
27
  packages = package_paths.map do |path|
28
28
  root_relative = path.dirname.relative_path_from(root_path)
29
- Package.new(name: root_relative.to_s, config: YAML.load_file(path))
29
+ Package.new(name: root_relative.to_s, config: YAML.load_file(path, fallback: nil))
30
30
  end
31
31
 
32
32
  create_root_package_if_none_in(packages)
@@ -4,7 +4,7 @@
4
4
  require "yaml"
5
5
 
6
6
  module Packwerk
7
- class DeprecatedReferences
7
+ class PackageTodo
8
8
  extend T::Sig
9
9
 
10
10
  EntriesType = T.type_alias do
@@ -16,30 +16,32 @@ module Packwerk
16
16
  @package = package
17
17
  @filepath = filepath
18
18
  @new_entries = T.let({}, EntriesType)
19
- @deprecated_references = T.let(nil, T.nilable(EntriesType))
19
+ @todo_list = T.let(nil, T.nilable(EntriesType))
20
20
  end
21
21
 
22
22
  sig do
23
- params(reference: Packwerk::Reference, violation_type: ViolationType)
23
+ params(reference: Packwerk::Reference, violation_type: String)
24
24
  .returns(T::Boolean)
25
25
  end
26
26
  def listed?(reference, violation_type:)
27
- violated_constants_found = deprecated_references.dig(reference.constant.package.name, reference.constant.name)
27
+ violated_constants_found = todo_list.dig(reference.constant.package.name, reference.constant.name)
28
28
  return false unless violated_constants_found
29
29
 
30
30
  violated_constant_in_file = violated_constants_found.fetch("files", []).include?(reference.relative_path)
31
31
  return false unless violated_constant_in_file
32
32
 
33
- violated_constants_found.fetch("violations", []).include?(violation_type.serialize)
33
+ violated_constants_found.fetch("violations", []).include?(violation_type)
34
34
  end
35
35
 
36
- sig { params(reference: Packwerk::Reference, violation_type: Packwerk::ViolationType).returns(T::Boolean) }
36
+ sig do
37
+ params(reference: Packwerk::Reference, violation_type: String).returns(T::Boolean)
38
+ end
37
39
  def add_entries(reference, violation_type)
38
40
  package_violations = @new_entries.fetch(reference.constant.package.name, {})
39
41
  entries_for_constant = package_violations[reference.constant.name] ||= {}
40
42
 
41
43
  entries_for_constant["violations"] ||= []
42
- entries_for_constant["violations"] << violation_type.serialize
44
+ entries_for_constant["violations"] << violation_type
43
45
 
44
46
  entries_for_constant["files"] ||= []
45
47
  entries_for_constant["files"] << reference.relative_path.to_s
@@ -52,7 +54,7 @@ module Packwerk
52
54
  def stale_violations?(for_files)
53
55
  prepare_entries_for_dump
54
56
 
55
- deprecated_references.any? do |package, package_violations|
57
+ todo_list.any? do |package, package_violations|
56
58
  package_violations_for_files = {}
57
59
  package_violations.each do |constant_name, entries_for_constant|
58
60
  entries_for_files = for_files & entries_for_constant["files"]
@@ -64,11 +66,17 @@ module Packwerk
64
66
  }
65
67
  end
66
68
 
67
- return true if package_violations_for_files.empty?
69
+ # We `next false` because if we cannot find existing violations for `for_files` within
70
+ # the `package_todo.yml` file, then there are no violations that
71
+ # can be considered stale.
72
+ next false if package_violations_for_files.empty?
68
73
 
69
74
  package_violations_for_files.any? do |constant_name, entries_for_constant|
70
75
  new_entries_violation_types = @new_entries.dig(package, constant_name, "violations")
71
- return true if new_entries_violation_types.nil?
76
+ # If there are no NEW entries that match the old entries `for_files`,
77
+ # @new_entries is from the list of violations we get when we check this file.
78
+ # If this list is empty, we also must have stale violations.
79
+ next true if new_entries_violation_types.nil?
72
80
 
73
81
  if entries_for_constant["violations"].all? { |type| new_entries_violation_types.include?(type) }
74
82
  stale_violations =
@@ -83,6 +91,8 @@ module Packwerk
83
91
 
84
92
  sig { void }
85
93
  def dump
94
+ delete_old_deprecated_references
95
+
86
96
  if @new_entries.empty?
87
97
  delete_if_exists
88
98
  else
@@ -93,7 +103,7 @@ module Packwerk
93
103
  #
94
104
  # You can regenerate this file using the following command:
95
105
  #
96
- # bin/packwerk update-deprecations #{@package.name}
106
+ # bin/packwerk update-todo #{@package.name}
97
107
  MESSAGE
98
108
  File.open(@filepath, "w") do |f|
99
109
  f.write(message)
@@ -109,6 +119,12 @@ module Packwerk
109
119
 
110
120
  private
111
121
 
122
+ sig { void }
123
+ def delete_old_deprecated_references
124
+ deprecated_references_filepath = File.join(File.dirname(@filepath), "deprecated_references.yml")
125
+ File.delete(deprecated_references_filepath) if File.exist?(deprecated_references_filepath)
126
+ end
127
+
112
128
  sig { returns(EntriesType) }
113
129
  def prepare_entries_for_dump
114
130
  @new_entries.each do |package_name, package_violations|
@@ -123,8 +139,8 @@ module Packwerk
123
139
  end
124
140
 
125
141
  sig { returns(EntriesType) }
126
- def deprecated_references
127
- @deprecated_references ||= if File.exist?(@filepath)
142
+ def todo_list
143
+ @todo_list ||= if File.exist?(@filepath)
128
144
  load_yaml(@filepath)
129
145
  else
130
146
  {}
@@ -1,7 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "benchmark"
5
4
  require "parallel"
6
5
 
7
6
  module Packwerk
@@ -16,77 +15,71 @@ module Packwerk
16
15
  params(
17
16
  relative_file_set: FilesForProcessing::RelativeFileSet,
18
17
  configuration: Configuration,
18
+ file_set_specified: T::Boolean,
19
+ offenses_formatter: T.nilable(OffensesFormatter),
19
20
  progress_formatter: Formatters::ProgressFormatter,
20
- offenses_formatter: OffensesFormatter,
21
21
  ).void
22
22
  end
23
23
  def initialize(
24
24
  relative_file_set:,
25
25
  configuration:,
26
- progress_formatter: Formatters::ProgressFormatter.new(StringIO.new),
27
- offenses_formatter: Formatters::OffensesFormatter.new
26
+ file_set_specified: false,
27
+ offenses_formatter: nil,
28
+ progress_formatter: Formatters::ProgressFormatter.new(StringIO.new)
28
29
  )
30
+
29
31
  @configuration = configuration
30
32
  @progress_formatter = progress_formatter
31
- @offenses_formatter = offenses_formatter
33
+ @offenses_formatter = T.let(offenses_formatter || configuration.offenses_formatter, Packwerk::OffensesFormatter)
32
34
  @relative_file_set = relative_file_set
35
+ @file_set_specified = file_set_specified
33
36
  end
34
37
 
35
- sig { returns(Result) }
36
- def detect_stale_violations
37
- warn(<<~WARNING.squish)
38
- DEPRECATION WARNING: `detect-stale-violation` is deprecated,
39
- the output of `check` includes stale references.
40
- WARNING
41
-
42
- run_context = Packwerk::RunContext.from_configuration(@configuration)
43
- offense_collection = find_offenses(run_context)
38
+ sig { returns(Cli::Result) }
39
+ def update_todo
40
+ if @file_set_specified
41
+ message = <<~MSG.squish
42
+ ⚠️ update-todo must be called without any file arguments.
43
+ MSG
44
44
 
45
- result_status = !offense_collection.stale_violations?(@relative_file_set)
46
- message = @offenses_formatter.show_stale_violations(offense_collection, @relative_file_set)
47
-
48
- Result.new(message: message, status: result_status)
49
- end
45
+ return Cli::Result.new(message: message, status: false)
46
+ end
50
47
 
51
- sig { returns(Result) }
52
- def update_deprecations
53
- run_context = Packwerk::RunContext.from_configuration(@configuration)
48
+ run_context = RunContext.from_configuration(@configuration)
54
49
  offense_collection = find_offenses(run_context)
55
- offense_collection.persist_deprecated_references_files(run_context.package_set)
50
+ offense_collection.persist_package_todo_files(run_context.package_set)
56
51
 
57
52
  message = <<~EOS
58
53
  #{@offenses_formatter.show_offenses(offense_collection.errors)}
59
- ✅ `deprecated_references.yml` has been updated.
54
+ ✅ `package_todo.yml` has been updated.
60
55
  EOS
61
56
 
62
- Result.new(message: message, status: offense_collection.errors.empty?)
57
+ Cli::Result.new(message: message, status: offense_collection.errors.empty?)
63
58
  end
64
59
 
65
- sig { returns(Result) }
60
+ sig { returns(Cli::Result) }
66
61
  def check
67
- run_context = Packwerk::RunContext.from_configuration(@configuration)
62
+ run_context = RunContext.from_configuration(@configuration)
68
63
  offense_collection = find_offenses(run_context, show_errors: true)
69
64
 
70
65
  messages = [
71
66
  @offenses_formatter.show_offenses(offense_collection.outstanding_offenses),
72
67
  @offenses_formatter.show_stale_violations(offense_collection, @relative_file_set),
68
+ @offenses_formatter.show_strict_mode_violations(offense_collection.strict_mode_violations),
73
69
  ]
74
70
 
75
71
  result_status = offense_collection.outstanding_offenses.empty? &&
76
- !offense_collection.stale_violations?(@relative_file_set)
72
+ !offense_collection.stale_violations?(@relative_file_set) && offense_collection.strict_mode_violations.empty?
77
73
 
78
- Result.new(message: messages.join("\n") + "\n", status: result_status)
74
+ Cli::Result.new(message: messages.select(&:present?).join("\n") + "\n", status: result_status)
79
75
  end
80
76
 
81
77
  private
82
78
 
83
- sig { params(run_context: Packwerk::RunContext, show_errors: T::Boolean).returns(OffenseCollection) }
79
+ sig { params(run_context: RunContext, show_errors: T::Boolean).returns(OffenseCollection) }
84
80
  def find_offenses(run_context, show_errors: false)
85
81
  offense_collection = OffenseCollection.new(@configuration.root_path)
86
- @progress_formatter.started(@relative_file_set)
87
-
88
82
  all_offenses = T.let([], T::Array[Offense])
89
-
90
83
  process_file = T.let(->(relative_file) do
91
84
  run_context.process_file(relative_file: relative_file).tap do |offenses|
92
85
  failed = show_errors && offenses.any? { |offense| !offense_collection.listed?(offense) }
@@ -94,7 +87,7 @@ module Packwerk
94
87
  end
95
88
  end, ProcessFileProc)
96
89
 
97
- execution_time = Benchmark.realtime do
90
+ @progress_formatter.started_inspection(@relative_file_set) do
98
91
  all_offenses = if @configuration.parallel?
99
92
  Parallel.flat_map(@relative_file_set, &process_file)
100
93
  else
@@ -102,8 +95,6 @@ module Packwerk
102
95
  end
103
96
  end
104
97
 
105
- @progress_formatter.finished(execution_time)
106
-
107
98
  all_offenses.each { |offense| offense_collection.add_offense(offense) }
108
99
  offense_collection
109
100
  end
@@ -132,4 +123,6 @@ module Packwerk
132
123
  end
133
124
  end
134
125
  end
126
+
127
+ private_constant :ParseRun
135
128
  end