packwerk 2.2.2 → 3.0.0

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