packwerk 2.3.0 → 3.0.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -5
- data/.ruby-version +1 -1
- data/Gemfile +0 -1
- data/Gemfile.lock +5 -95
- data/README.md +2 -7
- data/RESOLVING_VIOLATIONS.md +3 -8
- data/TROUBLESHOOT.md +2 -25
- data/UPGRADING.md +12 -0
- data/USAGE.md +136 -54
- data/dev.yml +1 -1
- data/exe/packwerk +4 -0
- data/gemfiles/Gemfile-rails-6-1 +1 -1
- data/lib/packwerk/application_validator.rb +54 -285
- data/lib/packwerk/association_inspector.rb +2 -0
- data/lib/packwerk/cache.rb +6 -5
- data/lib/packwerk/checker.rb +54 -0
- data/lib/packwerk/cli/result.rb +11 -0
- data/lib/packwerk/cli.rb +55 -40
- data/lib/packwerk/configuration.rb +61 -40
- data/lib/packwerk/const_node_inspector.rb +2 -0
- data/lib/packwerk/constant_context.rb +8 -0
- data/lib/packwerk/constant_discovery.rb +5 -6
- data/lib/packwerk/constant_name_inspector.rb +2 -0
- data/lib/packwerk/disable_sorbet.rb +41 -0
- data/lib/packwerk/extension_loader.rb +24 -0
- data/lib/packwerk/file_processor.rb +3 -1
- data/lib/packwerk/files_for_processing.rb +25 -12
- data/lib/packwerk/formatters/default_offenses_formatter.rb +77 -0
- data/lib/packwerk/formatters/progress_formatter.rb +31 -12
- data/lib/packwerk/generators/configuration_file.rb +7 -2
- data/lib/packwerk/generators/root_package.rb +5 -1
- data/lib/packwerk/generators/templates/package.yml +0 -10
- data/lib/packwerk/graph.rb +10 -2
- data/lib/packwerk/node.rb +1 -1
- data/lib/packwerk/node_helpers.rb +14 -7
- data/lib/packwerk/node_processor.rb +2 -0
- data/lib/packwerk/node_processor_factory.rb +6 -4
- data/lib/packwerk/node_visitor.rb +10 -1
- data/lib/packwerk/offense_collection.rb +26 -18
- data/lib/packwerk/offenses_formatter.rb +59 -2
- data/lib/packwerk/package.rb +7 -35
- data/lib/packwerk/package_set.rb +1 -1
- data/lib/packwerk/package_todo.rb +19 -20
- data/lib/packwerk/parse_run.rb +27 -34
- data/lib/packwerk/parsed_constant_definitions.rb +28 -5
- data/lib/packwerk/parsers/erb.rb +23 -4
- data/lib/packwerk/parsers/factory.rb +11 -2
- data/lib/packwerk/parsers/parser_interface.rb +1 -1
- data/lib/packwerk/parsers/ruby.rb +13 -3
- data/lib/packwerk/parsers.rb +6 -2
- data/lib/packwerk/{application_load_paths.rb → rails_load_paths.rb} +6 -4
- data/lib/packwerk/reference.rb +7 -1
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +29 -6
- data/lib/packwerk/reference_checking/reference_checker.rb +1 -1
- data/lib/packwerk/reference_extractor.rb +24 -12
- data/lib/packwerk/reference_offense.rb +2 -2
- data/lib/packwerk/run_context.rb +7 -10
- data/lib/packwerk/spring_command.rb +9 -2
- data/lib/packwerk/unresolved_reference.rb +9 -1
- data/lib/packwerk/validator/result.rb +18 -0
- data/lib/packwerk/validator.rb +90 -0
- data/lib/packwerk/validators/dependency_validator.rb +154 -0
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +64 -26
- data/packwerk.gemspec +4 -2
- data/sorbet/rbi/gems/{zeitwerk@2.6.0.rbi → zeitwerk@2.6.4.rbi} +291 -228
- data/sorbet/rbi/shims/minitest/test.rb +8 -0
- data/sorbet/rbi/shims/packwerk/reference.rbi +33 -0
- data/sorbet/rbi/shims/packwerk/unresolved_reference.rbi +33 -0
- data/sorbet/rbi/shims/parser.rbi +13 -0
- metadata +34 -15
- data/lib/packwerk/formatters/offenses_formatter.rb +0 -52
- data/lib/packwerk/reference_checking/checkers/checker.rb +0 -34
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +0 -76
- data/lib/packwerk/result.rb +0 -9
- data/lib/packwerk/sanity_checker.rb +0 -8
- data/lib/packwerk/violation_type.rb +0 -11
- data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +0 -46
- data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +0 -8
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
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
|
-
|
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
|
-
|
19
|
+
start_validation
|
25
20
|
|
26
21
|
execution_time = Benchmark.realtime(&block)
|
27
22
|
finished(execution_time)
|
23
|
+
end
|
28
24
|
|
29
|
-
|
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
|
-
|
46
|
-
|
47
|
-
@out.puts("
|
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:
|
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,
|
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:
|
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:
|
data/lib/packwerk/graph.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
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
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
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
|
63
|
-
|
64
|
-
|
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
|
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:
|
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
|
@@ -6,12 +6,12 @@ module Packwerk
|
|
6
6
|
extend T::Sig
|
7
7
|
|
8
8
|
const :root_path, String
|
9
|
-
const :context_provider,
|
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
|
-
|
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(
|
22
|
+
sig { params(node: AST::Node).returns(ReferenceExtractor) }
|
23
23
|
def reference_extractor(node:)
|
24
|
-
|
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:
|
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.
|
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
|
-
|
50
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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:
|
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
|
data/lib/packwerk/package.rb
CHANGED
@@ -17,22 +17,20 @@ module Packwerk
|
|
17
17
|
sig { returns(T::Array[String]) }
|
18
18
|
attr_reader :dependencies
|
19
19
|
|
20
|
-
sig {
|
21
|
-
|
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[
|
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"]
|
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)
|
data/lib/packwerk/package_set.rb
CHANGED
@@ -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:
|
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
|
33
|
+
violated_constants_found.fetch("violations", []).include?(violation_type)
|
34
34
|
end
|
35
35
|
|
36
|
-
sig
|
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
|
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
|
-
|
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
|
-
|
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,23 +91,20 @@ module Packwerk
|
|
83
91
|
|
84
92
|
sig { void }
|
85
93
|
def dump
|
86
|
-
delete_old_deprecated_references
|
87
|
-
|
88
94
|
if @new_entries.empty?
|
89
95
|
delete_if_exists
|
90
96
|
else
|
91
97
|
prepare_entries_for_dump
|
92
98
|
message = <<~MESSAGE
|
93
|
-
# This file contains a list of dependencies that are not part of the long term plan for
|
94
|
-
#
|
99
|
+
# This file contains a list of dependencies that are not part of the long term plan for the
|
100
|
+
# '#{@package.name}' package.
|
101
|
+
# We should generally work to reduce this list over time.
|
95
102
|
#
|
96
103
|
# You can regenerate this file using the following command:
|
97
104
|
#
|
98
|
-
# bin/packwerk update-todo
|
105
|
+
# bin/packwerk update-todo
|
99
106
|
MESSAGE
|
100
|
-
|
101
|
-
|
102
|
-
File.open(package_todo_filepath, "w") do |f|
|
107
|
+
File.open(@filepath, "w") do |f|
|
103
108
|
f.write(message)
|
104
109
|
f.write(@new_entries.to_yaml)
|
105
110
|
end
|
@@ -113,12 +118,6 @@ module Packwerk
|
|
113
118
|
|
114
119
|
private
|
115
120
|
|
116
|
-
sig { void }
|
117
|
-
def delete_old_deprecated_references
|
118
|
-
deprecated_references_filepath = File.join(File.dirname(@filepath), "deprecated_references.yml")
|
119
|
-
File.delete(deprecated_references_filepath) if File.exist?(deprecated_references_filepath)
|
120
|
-
end
|
121
|
-
|
122
121
|
sig { returns(EntriesType) }
|
123
122
|
def prepare_entries_for_dump
|
124
123
|
@new_entries.each do |package_name, package_violations|
|