packwerk 2.3.0 → 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 (78) 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/TROUBLESHOOT.md +0 -22
  8. data/USAGE.md +141 -51
  9. data/dev.yml +1 -1
  10. data/exe/packwerk +1 -0
  11. data/gemfiles/Gemfile-rails-6-1 +1 -1
  12. data/lib/packwerk/application_validator.rb +54 -285
  13. data/lib/packwerk/association_inspector.rb +2 -0
  14. data/lib/packwerk/cache.rb +6 -5
  15. data/lib/packwerk/checker.rb +54 -0
  16. data/lib/packwerk/cli/result.rb +11 -0
  17. data/lib/packwerk/cli.rb +55 -40
  18. data/lib/packwerk/configuration.rb +61 -40
  19. data/lib/packwerk/const_node_inspector.rb +2 -0
  20. data/lib/packwerk/constant_context.rb +8 -0
  21. data/lib/packwerk/constant_discovery.rb +5 -6
  22. data/lib/packwerk/constant_name_inspector.rb +2 -0
  23. data/lib/packwerk/disable_sorbet.rb +41 -0
  24. data/lib/packwerk/extension_loader.rb +24 -0
  25. data/lib/packwerk/file_processor.rb +3 -1
  26. data/lib/packwerk/files_for_processing.rb +25 -12
  27. data/lib/packwerk/formatters/default_offenses_formatter.rb +77 -0
  28. data/lib/packwerk/formatters/progress_formatter.rb +31 -12
  29. data/lib/packwerk/generators/configuration_file.rb +7 -2
  30. data/lib/packwerk/generators/root_package.rb +5 -1
  31. data/lib/packwerk/generators/templates/package.yml +0 -10
  32. data/lib/packwerk/graph.rb +10 -2
  33. data/lib/packwerk/node.rb +1 -1
  34. data/lib/packwerk/node_helpers.rb +14 -7
  35. data/lib/packwerk/node_processor.rb +2 -0
  36. data/lib/packwerk/node_processor_factory.rb +6 -4
  37. data/lib/packwerk/node_visitor.rb +10 -1
  38. data/lib/packwerk/offense_collection.rb +26 -18
  39. data/lib/packwerk/offenses_formatter.rb +59 -2
  40. data/lib/packwerk/package.rb +7 -35
  41. data/lib/packwerk/package_set.rb +1 -1
  42. data/lib/packwerk/package_todo.rb +15 -9
  43. data/lib/packwerk/parse_run.rb +27 -34
  44. data/lib/packwerk/parsed_constant_definitions.rb +28 -5
  45. data/lib/packwerk/parsers/erb.rb +23 -4
  46. data/lib/packwerk/parsers/factory.rb +11 -2
  47. data/lib/packwerk/parsers/parser_interface.rb +1 -1
  48. data/lib/packwerk/parsers/ruby.rb +13 -3
  49. data/lib/packwerk/parsers.rb +6 -2
  50. data/lib/packwerk/{application_load_paths.rb → rails_load_paths.rb} +6 -4
  51. data/lib/packwerk/reference.rb +7 -1
  52. data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +29 -6
  53. data/lib/packwerk/reference_checking/reference_checker.rb +1 -1
  54. data/lib/packwerk/reference_extractor.rb +24 -12
  55. data/lib/packwerk/reference_offense.rb +2 -2
  56. data/lib/packwerk/run_context.rb +7 -10
  57. data/lib/packwerk/spring_command.rb +11 -2
  58. data/lib/packwerk/unresolved_reference.rb +9 -1
  59. data/lib/packwerk/validator/result.rb +18 -0
  60. data/lib/packwerk/validator.rb +90 -0
  61. data/lib/packwerk/validators/dependency_validator.rb +154 -0
  62. data/lib/packwerk/version.rb +1 -1
  63. data/lib/packwerk.rb +64 -26
  64. data/packwerk.gemspec +4 -2
  65. data/sorbet/rbi/gems/{zeitwerk@2.6.0.rbi → zeitwerk@2.6.4.rbi} +291 -228
  66. data/sorbet/rbi/shims/minitest/test.rb +8 -0
  67. data/sorbet/rbi/shims/packwerk/reference.rbi +33 -0
  68. data/sorbet/rbi/shims/packwerk/unresolved_reference.rbi +33 -0
  69. data/sorbet/rbi/shims/parser.rbi +13 -0
  70. metadata +34 -15
  71. data/lib/packwerk/formatters/offenses_formatter.rb +0 -52
  72. data/lib/packwerk/reference_checking/checkers/checker.rb +0 -34
  73. data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +0 -76
  74. data/lib/packwerk/result.rb +0 -9
  75. data/lib/packwerk/sanity_checker.rb +0 -8
  76. data/lib/packwerk/violation_type.rb +0 -11
  77. data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +0 -46
  78. data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +0 -8
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "benchmark"
@@ -14,37 +14,56 @@ module Packwerk
14
14
  @style = style
15
15
  end
16
16
 
17
- def started(target_files)
18
- files_size = target_files.size
19
- files_string = "file".pluralize(files_size)
20
- @out.puts("📦 Packwerk is inspecting #{files_size} #{files_string}")
21
- end
22
-
17
+ sig { params(block: T.proc.void).void }
23
18
  def started_validation(&block)
24
- @out.puts("📦 Packwerk is running validation...")
19
+ start_validation
25
20
 
26
21
  execution_time = Benchmark.realtime(&block)
27
22
  finished(execution_time)
23
+ end
28
24
 
29
- @out.puts("✅ Packages are valid. Use `packwerk check` to run static checks.")
25
+ sig { params(target_files: FilesForProcessing::RelativeFileSet, block: T.proc.void).void }
26
+ def started_inspection(target_files, &block)
27
+ start_inspection(target_files)
28
+
29
+ execution_time = Benchmark.realtime(&block)
30
+ finished(execution_time)
30
31
  end
31
32
 
33
+ sig { void }
32
34
  def mark_as_inspected
33
35
  @out.print(".")
34
36
  end
35
37
 
38
+ sig { void }
36
39
  def mark_as_failed
37
40
  @out.print("#{@style.error}E#{@style.reset}")
38
41
  end
39
42
 
43
+ sig { void }
44
+ def interrupted
45
+ @out.puts
46
+ @out.puts("Manually interrupted. Violations caught so far are listed below:")
47
+ end
48
+
49
+ private
50
+
51
+ sig { params(execution_time: Float).void }
40
52
  def finished(execution_time)
41
53
  @out.puts
42
54
  @out.puts("📦 Finished in #{execution_time.round(2)} seconds")
43
55
  end
44
56
 
45
- def interrupted
46
- @out.puts
47
- @out.puts("Manually interrupted. Violations caught so far are listed below:")
57
+ sig { void }
58
+ def start_validation
59
+ @out.puts("📦 Packwerk is running validation...")
60
+ end
61
+
62
+ sig { params(target_files: FilesForProcessing::RelativeFileSet).void }
63
+ def start_inspection(target_files)
64
+ files_size = target_files.size
65
+ files_string = "file".pluralize(files_size)
66
+ @out.puts("📦 Packwerk is inspecting #{files_size} #{files_string}")
48
67
  end
49
68
  end
50
69
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "erb"
@@ -11,6 +11,9 @@ module Packwerk
11
11
  CONFIGURATION_TEMPLATE_FILE_PATH = "templates/packwerk.yml.erb"
12
12
 
13
13
  class << self
14
+ extend T::Sig
15
+
16
+ sig { params(root: String, out: T.any(IO, StringIO)).returns(T::Boolean) }
14
17
  def generate(root:, out:)
15
18
  new(root: root, out: out).generate
16
19
  end
@@ -25,7 +28,7 @@ module Packwerk
25
28
  sig { returns(T::Boolean) }
26
29
  def generate
27
30
  @out.puts("📦 Generating Packwerk configuration file...")
28
- default_config_path = File.join(@root, ::Packwerk::Configuration::DEFAULT_CONFIG_PATH)
31
+ default_config_path = File.join(@root, Configuration::DEFAULT_CONFIG_PATH)
29
32
 
30
33
  if File.exist?(default_config_path)
31
34
  @out.puts("⚠️ Packwerk configuration file already exists.")
@@ -40,10 +43,12 @@ module Packwerk
40
43
 
41
44
  private
42
45
 
46
+ sig { returns(String) }
43
47
  def render
44
48
  ERB.new(template, trim_mode: "-").result(binding)
45
49
  end
46
50
 
51
+ sig { returns(String) }
47
52
  def template
48
53
  template_file_path = File.join(__dir__, CONFIGURATION_TEMPLATE_FILE_PATH)
49
54
  File.read(template_file_path)
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Packwerk
@@ -7,11 +7,15 @@ module Packwerk
7
7
  extend T::Sig
8
8
 
9
9
  class << self
10
+ extend T::Sig
11
+
12
+ sig { params(root: String, out: T.any(IO, StringIO)).returns(T::Boolean) }
10
13
  def generate(root:, out:)
11
14
  new(root: root, out: out).generate
12
15
  end
13
16
  end
14
17
 
18
+ sig { params(root: String, out: T.any(IO, StringIO)).void }
15
19
  def initialize(root:, out: $stdout)
16
20
  @root = root
17
21
  @out = out
@@ -5,16 +5,6 @@
5
5
  # Turn on dependency checks for this package
6
6
  enforce_dependencies: true
7
7
 
8
- # Turn on privacy checks for this package
9
- # enforcing privacy is often not useful for the root package, because it would require defining a public interface
10
- # for something that should only be a thin wrapper in the first place.
11
- # We recommend enabling this for any new packages you create to aid with encapsulation.
12
- enforce_privacy: false
13
-
14
- # By default the public path will be app/public/, however this may not suit all applications' architecture so
15
- # this allows you to modify what your package's public path is.
16
- # public_path: app/public/
17
-
18
8
  # A list of this package's dependencies
19
9
  # Note that packages in this list require their own `package.yml` file
20
10
  # dependencies:
@@ -4,8 +4,14 @@
4
4
  module Packwerk
5
5
  # A general implementation of a graph data structure with the ability to check for - and list - cycles.
6
6
  class Graph
7
- # @param [Array<Array>] edges The edges of the graph; An edge being represented as an Array of two nodes.
8
- def initialize(*edges)
7
+ extend T::Sig
8
+ sig do
9
+ params(
10
+ # The edges of the graph; An edge being represented as an Array of two nodes.
11
+ edges: T::Array[T::Array[T.any(String, Integer, NilClass)]]
12
+ ).void
13
+ end
14
+ def initialize(edges)
9
15
  @edges = edges.uniq
10
16
  @cycles = Set.new
11
17
  process
@@ -73,4 +79,6 @@ module Packwerk
73
79
  @cycles << cycle
74
80
  end
75
81
  end
82
+
83
+ private_constant :Graph
76
84
  end
data/lib/packwerk/node.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Packwerk
5
- class Node
5
+ module Node
6
6
  Location = Struct.new(:line, :column)
7
7
  end
8
8
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "parser"
@@ -59,11 +59,16 @@ module Packwerk
59
59
  end
60
60
  end
61
61
 
62
- sig { params(node: AST::Node).returns(T.untyped) }
63
- def each_child(node)
64
- if block_given?
62
+ sig do
63
+ params(
64
+ node: AST::Node,
65
+ block: T.nilable(T.proc.params(arg0: Parser::AST::Node).void),
66
+ ).returns(T::Enumerable[AST::Node])
67
+ end
68
+ def each_child(node, &block)
69
+ if block
65
70
  node.children.each do |child|
66
- yield child if child.is_a?(Parser::AST::Node)
71
+ yield(child) if child.is_a?(Parser::AST::Node)
67
72
  end
68
73
  else
69
74
  enum_for(:each_child, node)
@@ -181,9 +186,9 @@ module Packwerk
181
186
  end
182
187
  end
183
188
 
184
- sig { params(node: Parser::AST::Node).returns(T.nilable(Node::Location)) }
189
+ sig { params(node: AST::Node).returns(T.nilable(Node::Location)) }
185
190
  def name_location(node)
186
- location = node.location
191
+ location = T.cast(node, Parser::AST::Node).location
187
192
 
188
193
  if location.respond_to?(:name)
189
194
  name = location.name
@@ -332,4 +337,6 @@ module Packwerk
332
337
  end
333
338
  end
334
339
  end
340
+
341
+ private_constant :NodeHelpers
335
342
  end
@@ -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
@@ -18,6 +18,7 @@ module Packwerk
18
18
  @root_path = root_path
19
19
  @package_todo = T.let(package_todo, T::Hash[Packwerk::Package, Packwerk::PackageTodo])
20
20
  @new_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
21
+ @strict_mode_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
21
22
  @errors = T.let([], T::Array[Packwerk::Offense])
22
23
  end
23
24
 
@@ -27,6 +28,9 @@ module Packwerk
27
28
  sig { returns(T::Array[Packwerk::Offense]) }
28
29
  attr_reader :errors
29
30
 
31
+ sig { returns(T::Array[Packwerk::ReferenceOffense]) }
32
+ attr_reader :strict_mode_violations
33
+
30
34
  sig do
31
35
  params(offense: Packwerk::Offense)
32
36
  .returns(T::Boolean)
@@ -35,7 +39,7 @@ module Packwerk
35
39
  return false unless offense.is_a?(ReferenceOffense)
36
40
 
37
41
  reference = offense.reference
38
- package_todo_for(reference.source_package).listed?(reference, violation_type: offense.violation_type)
42
+ package_todo_for(reference.package).listed?(reference, violation_type: offense.violation_type)
39
43
  end
40
44
 
41
45
  sig do
@@ -46,9 +50,11 @@ module Packwerk
46
50
  @errors << offense
47
51
  return
48
52
  end
49
- package_todo = package_todo_for(offense.reference.source_package)
50
- unless package_todo.add_entries(offense.reference, offense.violation_type)
53
+
54
+ if !already_listed?(offense)
51
55
  new_violations << offense
56
+ elsif strict_mode_violation?(offense)
57
+ strict_mode_violations << offense
52
58
  end
53
59
  end
54
60
 
@@ -70,12 +76,19 @@ module Packwerk
70
76
  errors + new_violations
71
77
  end
72
78
 
73
- sig { void }
74
- def dump_package_todo_files
75
- @package_todo.each_value(&:dump)
79
+ private
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)
76
85
  end
77
86
 
78
- private
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
79
92
 
80
93
  sig { params(package_set: Packwerk::PackageSet).void }
81
94
  def cleanup_extra_package_todo_files(package_set)
@@ -89,6 +102,11 @@ module Packwerk
89
102
  end
90
103
  end
91
104
 
105
+ sig { void }
106
+ def dump_package_todo_files
107
+ @package_todo.each_value(&:dump)
108
+ end
109
+
92
110
  sig { params(package: Packwerk::Package).returns(Packwerk::PackageTodo) }
93
111
  def package_todo_for(package)
94
112
  @package_todo[package] ||= Packwerk::PackageTodo.new(
@@ -99,17 +117,7 @@ module Packwerk
99
117
 
100
118
  sig { params(package: Packwerk::Package).returns(String) }
101
119
  def package_todo_file_for(package)
102
- if Pathname.new(@root_path).join(package.name, "deprecated_references.yml").exist?
103
- warning = <<~WARNING.squish
104
- DEPRECATION WARNING: `deprecated_references.yml` files have been renamed to `package_todo.yml`.
105
- Run `packwerk update-todo` to rename files automatically.
106
- WARNING
107
-
108
- warn(warning)
109
- File.join(@root_path, package.name, "deprecated_references.yml")
110
- else
111
- File.join(@root_path, package.name, "package_todo.yml")
112
- end
120
+ File.join(@root_path, package.name, "package_todo.yml")
113
121
  end
114
122
  end
115
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)
@@ -20,7 +20,7 @@ module Packwerk
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:)
@@ -30,16 +30,18 @@ module Packwerk
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
@@ -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 =
@@ -97,9 +105,7 @@ module Packwerk
97
105
  #
98
106
  # bin/packwerk update-todo #{@package.name}
99
107
  MESSAGE
100
- package_todo_filepath = File.join(File.dirname(@filepath), "package_todo.yml")
101
-
102
- File.open(package_todo_filepath, "w") do |f|
108
+ File.open(@filepath, "w") do |f|
103
109
  f.write(message)
104
110
  f.write(@new_entries.to_yaml)
105
111
  end