packwerk 2.0.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +26 -22
- data/README.md +13 -1
- data/USAGE.md +7 -0
- data/lib/packwerk/application_load_paths.rb +12 -18
- data/lib/packwerk/application_validator.rb +88 -40
- data/lib/packwerk/cache.rb +169 -0
- data/lib/packwerk/cli.rb +29 -13
- data/lib/packwerk/configuration.rb +17 -12
- data/lib/packwerk/constant_discovery.rb +20 -4
- data/lib/packwerk/constant_name_inspector.rb +1 -1
- data/lib/packwerk/deprecated_references.rb +1 -1
- data/lib/packwerk/file_processor.rb +43 -22
- data/lib/packwerk/files_for_processing.rb +55 -26
- data/lib/packwerk/generators/templates/packwerk.yml.erb +6 -0
- data/lib/packwerk/node.rb +2 -1
- data/lib/packwerk/node_processor.rb +6 -6
- data/lib/packwerk/node_processor_factory.rb +3 -4
- data/lib/packwerk/node_visitor.rb +3 -0
- data/lib/packwerk/offense.rb +10 -2
- data/lib/packwerk/package.rb +1 -1
- data/lib/packwerk/package_set.rb +4 -3
- data/lib/packwerk/parse_run.rb +37 -17
- data/lib/packwerk/parsed_constant_definitions.rb +4 -4
- data/lib/packwerk/parsers/erb.rb +2 -0
- data/lib/packwerk/parsers/factory.rb +2 -0
- data/lib/packwerk/parsers/parser_interface.rb +19 -0
- data/lib/packwerk/parsers/ruby.rb +2 -0
- data/lib/packwerk/parsers.rb +1 -0
- data/lib/packwerk/reference_checking/checkers/checker.rb +1 -1
- data/lib/packwerk/reference_checking/reference_checker.rb +3 -4
- data/lib/packwerk/reference_extractor.rb +72 -20
- data/lib/packwerk/reference_offense.rb +8 -3
- data/lib/packwerk/result.rb +2 -2
- data/lib/packwerk/run_context.rb +62 -36
- data/lib/packwerk/spring_command.rb +1 -1
- data/lib/packwerk/unresolved_reference.rb +10 -0
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +2 -0
- data/packwerk.gemspec +4 -2
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/tapioca@0.4.19.rbi +1 -1
- data/sorbet/tapioca/require.rb +1 -1
- metadata +36 -5
data/lib/packwerk/node.rb
CHANGED
@@ -5,7 +5,7 @@ require "parser"
|
|
5
5
|
require "parser/ast/node"
|
6
6
|
|
7
7
|
module Packwerk
|
8
|
-
# Convenience methods for working with AST nodes.
|
8
|
+
# Convenience methods for working with Parser::AST::Node nodes.
|
9
9
|
module Node
|
10
10
|
class TypeError < ArgumentError; end
|
11
11
|
Location = Struct.new(:line, :column)
|
@@ -264,6 +264,7 @@ module Packwerk
|
|
264
264
|
# "Class.new"
|
265
265
|
# "Module.new"
|
266
266
|
method_call?(node) &&
|
267
|
+
receiver(node) &&
|
267
268
|
constant?(receiver(node)) &&
|
268
269
|
["Class", "Module"].include?(constant_name(receiver(node))) &&
|
269
270
|
method_name(node) == :new
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -9,23 +9,23 @@ module Packwerk
|
|
9
9
|
sig do
|
10
10
|
params(
|
11
11
|
reference_extractor: ReferenceExtractor,
|
12
|
-
|
12
|
+
absolute_file: String,
|
13
13
|
).void
|
14
14
|
end
|
15
|
-
def initialize(reference_extractor:,
|
15
|
+
def initialize(reference_extractor:, absolute_file:)
|
16
16
|
@reference_extractor = reference_extractor
|
17
|
-
@
|
17
|
+
@absolute_file = absolute_file
|
18
18
|
end
|
19
19
|
|
20
20
|
sig do
|
21
21
|
params(
|
22
22
|
node: Parser::AST::Node,
|
23
23
|
ancestors: T::Array[Parser::AST::Node]
|
24
|
-
).returns(T.nilable(
|
24
|
+
).returns(T.nilable(UnresolvedReference))
|
25
25
|
end
|
26
26
|
def call(node, ancestors)
|
27
27
|
return unless Node.method_call?(node) || Node.constant?(node)
|
28
|
-
@reference_extractor.reference_from_node(node, ancestors: ancestors,
|
28
|
+
@reference_extractor.reference_from_node(node, ancestors: ancestors, absolute_file: @absolute_file)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -9,11 +9,11 @@ module Packwerk
|
|
9
9
|
const :context_provider, Packwerk::ConstantDiscovery
|
10
10
|
const :constant_name_inspectors, T::Array[ConstantNameInspector]
|
11
11
|
|
12
|
-
sig { params(
|
13
|
-
def for(
|
12
|
+
sig { params(absolute_file: String, node: AST::Node).returns(NodeProcessor) }
|
13
|
+
def for(absolute_file:, node:)
|
14
14
|
::Packwerk::NodeProcessor.new(
|
15
15
|
reference_extractor: reference_extractor(node: node),
|
16
|
-
|
16
|
+
absolute_file: absolute_file,
|
17
17
|
)
|
18
18
|
end
|
19
19
|
|
@@ -22,7 +22,6 @@ module Packwerk
|
|
22
22
|
sig { params(node: AST::Node).returns(::Packwerk::ReferenceExtractor) }
|
23
23
|
def reference_extractor(node:)
|
24
24
|
::Packwerk::ReferenceExtractor.new(
|
25
|
-
context_provider: context_provider,
|
26
25
|
constant_name_inspectors: constant_name_inspectors,
|
27
26
|
root_node: node,
|
28
27
|
root_path: root_path,
|
data/lib/packwerk/offense.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "parser/source/map"
|
@@ -8,7 +8,14 @@ module Packwerk
|
|
8
8
|
extend T::Sig
|
9
9
|
extend T::Helpers
|
10
10
|
|
11
|
-
|
11
|
+
sig { returns(T.nilable(Node::Location)) }
|
12
|
+
attr_reader :location
|
13
|
+
|
14
|
+
sig { returns(String) }
|
15
|
+
attr_reader :file
|
16
|
+
|
17
|
+
sig { returns(String) }
|
18
|
+
attr_reader :message
|
12
19
|
|
13
20
|
sig do
|
14
21
|
params(file: String, message: String, location: T.nilable(Node::Location))
|
@@ -22,6 +29,7 @@ module Packwerk
|
|
22
29
|
|
23
30
|
sig { params(style: OutputStyle).returns(String) }
|
24
31
|
def to_s(style = OutputStyles::Plain.new)
|
32
|
+
location = self.location
|
25
33
|
if location
|
26
34
|
<<~EOS
|
27
35
|
#{style.filename}#{file}#{style.reset}:#{location.line}:#{location.column}
|
data/lib/packwerk/package.rb
CHANGED
@@ -21,6 +21,7 @@ module Packwerk
|
|
21
21
|
@name = name
|
22
22
|
@config = T.let(config || {}, T::Hash[T.untyped, T.untyped])
|
23
23
|
@dependencies = T.let(Array(@config["dependencies"]).freeze, T::Array[String])
|
24
|
+
@public_path = T.let(nil, T.nilable(String))
|
24
25
|
end
|
25
26
|
|
26
27
|
sig { returns(T.nilable(T.any(T::Boolean, T::Array[String]))) }
|
@@ -46,7 +47,6 @@ module Packwerk
|
|
46
47
|
|
47
48
|
sig { returns(String) }
|
48
49
|
def public_path
|
49
|
-
@public_path = T.let(@public_path, T.nilable(String))
|
50
50
|
@public_path ||= begin
|
51
51
|
unprefixed_public_path = user_defined_public_path || "app/public/"
|
52
52
|
|
data/lib/packwerk/package_set.rb
CHANGED
@@ -12,7 +12,7 @@ module Packwerk
|
|
12
12
|
extend T::Generic
|
13
13
|
include Enumerable
|
14
14
|
|
15
|
-
Elem = type_member
|
15
|
+
Elem = type_member { { fixed: Package } }
|
16
16
|
|
17
17
|
PACKAGE_CONFIG_FILENAME = "package.yml"
|
18
18
|
|
@@ -79,6 +79,7 @@ module Packwerk
|
|
79
79
|
sorted_packages = packages.sort_by { |package| -package.name.length }
|
80
80
|
packages = sorted_packages.each_with_object({}) { |package, hash| hash[package.name] = package }
|
81
81
|
@packages = T.let(packages, T::Hash[String, Package])
|
82
|
+
@package_from_path = T.let({}, T::Hash[String, T.nilable(Package)])
|
82
83
|
end
|
83
84
|
|
84
85
|
sig { override.params(blk: T.proc.params(arg0: Package).returns(T.untyped)).returns(T.untyped) }
|
@@ -91,10 +92,10 @@ module Packwerk
|
|
91
92
|
packages[name]
|
92
93
|
end
|
93
94
|
|
94
|
-
sig { params(file_path: T.any(Pathname, String)).returns(
|
95
|
+
sig { params(file_path: T.any(Pathname, String)).returns(Package) }
|
95
96
|
def package_from_path(file_path)
|
96
97
|
path_string = file_path.to_s
|
97
|
-
packages.values.find { |package| package.package_path?(path_string) }
|
98
|
+
@package_from_path[path_string] ||= T.must(packages.values.find { |package| package.package_path?(path_string) })
|
98
99
|
end
|
99
100
|
end
|
100
101
|
end
|
data/lib/packwerk/parse_run.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "benchmark"
|
@@ -8,8 +8,20 @@ module Packwerk
|
|
8
8
|
class ParseRun
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
+
ProcessFileProc = T.type_alias do
|
12
|
+
T.proc.params(path: String).returns(T::Array[Offense])
|
13
|
+
end
|
14
|
+
|
15
|
+
sig do
|
16
|
+
params(
|
17
|
+
absolute_file_set: FilesForProcessing::AbsoluteFileSet,
|
18
|
+
configuration: Configuration,
|
19
|
+
progress_formatter: Formatters::ProgressFormatter,
|
20
|
+
offenses_formatter: OffensesFormatter,
|
21
|
+
).void
|
22
|
+
end
|
11
23
|
def initialize(
|
12
|
-
|
24
|
+
absolute_file_set:,
|
13
25
|
configuration:,
|
14
26
|
progress_formatter: Formatters::ProgressFormatter.new(StringIO.new),
|
15
27
|
offenses_formatter: Formatters::OffensesFormatter.new
|
@@ -17,9 +29,10 @@ module Packwerk
|
|
17
29
|
@configuration = configuration
|
18
30
|
@progress_formatter = progress_formatter
|
19
31
|
@offenses_formatter = offenses_formatter
|
20
|
-
@
|
32
|
+
@absolute_file_set = absolute_file_set
|
21
33
|
end
|
22
34
|
|
35
|
+
sig { returns(Result) }
|
23
36
|
def detect_stale_violations
|
24
37
|
offense_collection = find_offenses
|
25
38
|
|
@@ -29,6 +42,7 @@ module Packwerk
|
|
29
42
|
Result.new(message: message, status: result_status)
|
30
43
|
end
|
31
44
|
|
45
|
+
sig { returns(Result) }
|
32
46
|
def update_deprecations
|
33
47
|
offense_collection = find_offenses
|
34
48
|
offense_collection.dump_deprecated_references_files
|
@@ -41,6 +55,7 @@ module Packwerk
|
|
41
55
|
Result.new(message: message, status: offense_collection.errors.empty?)
|
42
56
|
end
|
43
57
|
|
58
|
+
sig { returns(Result) }
|
44
59
|
def check
|
45
60
|
offense_collection = find_offenses(show_errors: true)
|
46
61
|
|
@@ -55,23 +70,24 @@ module Packwerk
|
|
55
70
|
|
56
71
|
private
|
57
72
|
|
73
|
+
sig { params(show_errors: T::Boolean).returns(OffenseCollection) }
|
58
74
|
def find_offenses(show_errors: false)
|
59
75
|
offense_collection = OffenseCollection.new(@configuration.root_path)
|
60
|
-
@progress_formatter.started(@
|
76
|
+
@progress_formatter.started(@absolute_file_set)
|
61
77
|
|
62
78
|
run_context = Packwerk::RunContext.from_configuration(@configuration)
|
63
|
-
all_offenses = T.let([], T
|
79
|
+
all_offenses = T.let([], T::Array[Offense])
|
64
80
|
|
65
|
-
process_file = -> (
|
66
|
-
run_context.process_file(
|
81
|
+
process_file = T.let(-> (absolute_file) do
|
82
|
+
run_context.process_file(absolute_file: absolute_file).tap do |offenses|
|
67
83
|
failed = show_errors && offenses.any? { |offense| !offense_collection.listed?(offense) }
|
68
84
|
update_progress(failed: failed)
|
69
85
|
end
|
70
|
-
end
|
86
|
+
end, ProcessFileProc)
|
71
87
|
|
72
88
|
execution_time = Benchmark.realtime do
|
73
89
|
all_offenses = if @configuration.parallel?
|
74
|
-
Parallel.flat_map(@
|
90
|
+
Parallel.flat_map(@absolute_file_set, &process_file)
|
75
91
|
else
|
76
92
|
serial_find_offenses(&process_file)
|
77
93
|
end
|
@@ -83,18 +99,22 @@ module Packwerk
|
|
83
99
|
offense_collection
|
84
100
|
end
|
85
101
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
102
|
+
sig { params(block: ProcessFileProc).returns(T::Array[Offense]) }
|
103
|
+
def serial_find_offenses(&block)
|
104
|
+
all_offenses = T.let([], T::Array[Offense])
|
105
|
+
begin
|
106
|
+
@absolute_file_set.each do |absolute_file|
|
107
|
+
offenses = block.call(absolute_file)
|
108
|
+
all_offenses.concat(offenses)
|
109
|
+
end
|
110
|
+
rescue Interrupt
|
111
|
+
@progress_formatter.interrupted
|
112
|
+
all_offenses
|
91
113
|
end
|
92
114
|
all_offenses
|
93
|
-
rescue Interrupt
|
94
|
-
@progress_formatter.interrupted
|
95
|
-
all_offenses
|
96
115
|
end
|
97
116
|
|
117
|
+
sig { params(failed: T::Boolean).void }
|
98
118
|
def update_progress(failed: false)
|
99
119
|
if failed
|
100
120
|
@progress_formatter.mark_as_failed
|
@@ -25,13 +25,13 @@ module Packwerk
|
|
25
25
|
def self.reference_qualifications(constant_name, namespace_path:)
|
26
26
|
return [constant_name] if constant_name.start_with?("::")
|
27
27
|
|
28
|
-
|
28
|
+
resolved_constant_name = "::#{constant_name}"
|
29
29
|
|
30
30
|
possible_namespaces = namespace_path.each_with_object([""]) do |current, acc|
|
31
31
|
acc << "#{acc.last}::#{current}" if acc.last && current
|
32
32
|
end
|
33
33
|
|
34
|
-
possible_namespaces.map { |namespace| namespace +
|
34
|
+
possible_namespaces.map { |namespace| namespace + resolved_constant_name }
|
35
35
|
end
|
36
36
|
|
37
37
|
private
|
@@ -53,9 +53,9 @@ module Packwerk
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def add_definition(constant_name, current_namespace_path, location)
|
56
|
-
|
56
|
+
resolved_constant = [""].concat(current_namespace_path).push(constant_name).join("::")
|
57
57
|
|
58
|
-
@local_definitions[
|
58
|
+
@local_definitions[resolved_constant] = location
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/packwerk/parsers/erb.rb
CHANGED
@@ -6,6 +6,7 @@ require "singleton"
|
|
6
6
|
module Packwerk
|
7
7
|
module Parsers
|
8
8
|
class Factory
|
9
|
+
extend T::Sig
|
9
10
|
include Singleton
|
10
11
|
|
11
12
|
RUBY_REGEX = %r{
|
@@ -19,6 +20,7 @@ module Packwerk
|
|
19
20
|
ERB_REGEX = /\.erb\Z/
|
20
21
|
private_constant :ERB_REGEX
|
21
22
|
|
23
|
+
sig { params(path: String).returns(T.nilable(ParserInterface)) }
|
22
24
|
def for_path(path)
|
23
25
|
case path
|
24
26
|
when RUBY_REGEX
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Parsers
|
6
|
+
module ParserInterface
|
7
|
+
extend T::Helpers
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
requires_ancestor { Kernel }
|
11
|
+
|
12
|
+
interface!
|
13
|
+
|
14
|
+
sig { abstract.params(io: File, file_path: String).returns(T.untyped) }
|
15
|
+
def call(io:, file_path:)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/packwerk/parsers.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -6,18 +6,17 @@ module Packwerk
|
|
6
6
|
class ReferenceChecker
|
7
7
|
extend T::Sig
|
8
8
|
|
9
|
+
sig { params(checkers: T::Array[Checkers::Checker]).void }
|
9
10
|
def initialize(checkers)
|
10
11
|
@checkers = checkers
|
11
12
|
end
|
12
13
|
|
13
14
|
sig do
|
14
15
|
params(
|
15
|
-
reference:
|
16
|
+
reference: Reference
|
16
17
|
).returns(T::Array[Packwerk::Offense])
|
17
18
|
end
|
18
19
|
def call(reference)
|
19
|
-
return [reference] if reference.is_a?(Packwerk::Offense)
|
20
|
-
|
21
20
|
@checkers.each_with_object([]) do |checker, violations|
|
22
21
|
next unless checker.invalid_reference?(reference)
|
23
22
|
offense = Packwerk::ReferenceOffense.new(
|
@@ -8,58 +8,110 @@ module Packwerk
|
|
8
8
|
|
9
9
|
sig do
|
10
10
|
params(
|
11
|
-
context_provider: Packwerk::ConstantDiscovery,
|
12
11
|
constant_name_inspectors: T::Array[Packwerk::ConstantNameInspector],
|
13
12
|
root_node: ::AST::Node,
|
14
13
|
root_path: String,
|
15
14
|
).void
|
16
15
|
end
|
17
16
|
def initialize(
|
18
|
-
context_provider:,
|
19
17
|
constant_name_inspectors:,
|
20
18
|
root_node:,
|
21
19
|
root_path:
|
22
20
|
)
|
23
|
-
@context_provider = context_provider
|
24
21
|
@constant_name_inspectors = constant_name_inspectors
|
25
22
|
@root_path = root_path
|
26
23
|
@local_constant_definitions = ParsedConstantDefinitions.new(root_node: root_node)
|
27
24
|
end
|
28
25
|
|
29
|
-
|
26
|
+
sig do
|
27
|
+
params(
|
28
|
+
node: Parser::AST::Node,
|
29
|
+
ancestors: T::Array[Parser::AST::Node],
|
30
|
+
absolute_file: String
|
31
|
+
).returns(T.nilable(UnresolvedReference))
|
32
|
+
end
|
33
|
+
def reference_from_node(node, ancestors:, absolute_file:)
|
30
34
|
constant_name = T.let(nil, T.nilable(String))
|
31
35
|
|
32
36
|
@constant_name_inspectors.each do |inspector|
|
33
37
|
constant_name = inspector.constant_name_from_node(node, ancestors: ancestors)
|
38
|
+
|
34
39
|
break if constant_name
|
35
40
|
end
|
36
41
|
|
37
|
-
|
42
|
+
if constant_name
|
43
|
+
reference_from_constant(
|
44
|
+
constant_name,
|
45
|
+
node: node,
|
46
|
+
ancestors: ancestors,
|
47
|
+
absolute_file: absolute_file
|
48
|
+
)
|
49
|
+
end
|
38
50
|
end
|
39
51
|
|
40
|
-
|
52
|
+
sig do
|
53
|
+
params(
|
54
|
+
unresolved_references: T::Array[UnresolvedReference],
|
55
|
+
context_provider: ConstantDiscovery
|
56
|
+
).returns(T::Array[Reference])
|
57
|
+
end
|
58
|
+
def self.get_fully_qualified_references_from(unresolved_references, context_provider)
|
59
|
+
fully_qualified_references = T.let([], T::Array[Reference])
|
41
60
|
|
42
|
-
|
43
|
-
|
44
|
-
return if local_reference?(constant_name, Node.name_location(node), namespace_path)
|
61
|
+
unresolved_references.each do |unresolved_references_or_offense|
|
62
|
+
unresolved_reference = unresolved_references_or_offense
|
45
63
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
64
|
+
constant =
|
65
|
+
context_provider.context_for(
|
66
|
+
unresolved_reference.constant_name,
|
67
|
+
current_namespace_path: unresolved_reference.namespace_path
|
68
|
+
)
|
69
|
+
|
70
|
+
next if constant.nil?
|
71
|
+
|
72
|
+
package_for_constant = constant.package
|
73
|
+
|
74
|
+
next if package_for_constant.nil?
|
75
|
+
|
76
|
+
source_package = context_provider.package_from_path(unresolved_reference.relative_path)
|
77
|
+
|
78
|
+
next if source_package == package_for_constant
|
79
|
+
|
80
|
+
fully_qualified_references << Reference.new(
|
81
|
+
source_package,
|
82
|
+
unresolved_reference.relative_path,
|
83
|
+
constant,
|
84
|
+
unresolved_reference.source_location
|
50
85
|
)
|
86
|
+
end
|
51
87
|
|
52
|
-
|
88
|
+
fully_qualified_references
|
89
|
+
end
|
53
90
|
|
54
|
-
|
55
|
-
Pathname.new(file_path)
|
56
|
-
.relative_path_from(@root_path).to_s
|
91
|
+
private
|
57
92
|
|
58
|
-
|
93
|
+
sig do
|
94
|
+
params(
|
95
|
+
constant_name: String,
|
96
|
+
node: Parser::AST::Node,
|
97
|
+
ancestors: T::Array[Parser::AST::Node],
|
98
|
+
absolute_file: String
|
99
|
+
).returns(T.nilable(UnresolvedReference))
|
100
|
+
end
|
101
|
+
def reference_from_constant(constant_name, node:, ancestors:, absolute_file:)
|
102
|
+
namespace_path = Node.enclosing_namespace_path(node, ancestors: ancestors)
|
59
103
|
|
60
|
-
return if
|
104
|
+
return if local_reference?(constant_name, Node.name_location(node), namespace_path)
|
61
105
|
|
62
|
-
|
106
|
+
relative_file = Pathname.new(absolute_file).relative_path_from(@root_path).to_s
|
107
|
+
location = Node.location(node)
|
108
|
+
|
109
|
+
UnresolvedReference.new(
|
110
|
+
constant_name,
|
111
|
+
namespace_path,
|
112
|
+
relative_file,
|
113
|
+
location
|
114
|
+
)
|
63
115
|
end
|
64
116
|
|
65
117
|
def local_reference?(constant_name, name_location, namespace_path)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -7,7 +7,11 @@ module Packwerk
|
|
7
7
|
extend T::Sig
|
8
8
|
extend T::Helpers
|
9
9
|
|
10
|
-
|
10
|
+
sig { returns(Reference) }
|
11
|
+
attr_reader :reference
|
12
|
+
|
13
|
+
sig { returns(ViolationType) }
|
14
|
+
attr_reader :violation_type
|
11
15
|
|
12
16
|
sig do
|
13
17
|
params(
|
@@ -25,10 +29,11 @@ module Packwerk
|
|
25
29
|
|
26
30
|
private
|
27
31
|
|
32
|
+
sig { params(reference: Reference, violation_type: ViolationType).returns(String) }
|
28
33
|
def build_message(reference, violation_type)
|
29
34
|
violation_message = case violation_type
|
30
35
|
when ViolationType::Privacy
|
31
|
-
source_desc =
|
36
|
+
source_desc = "'#{reference.source_package}'"
|
32
37
|
"Privacy violation: '#{reference.constant.name}' is private to '#{reference.constant.package}' but " \
|
33
38
|
"referenced from #{source_desc}.\n" \
|
34
39
|
"Is there a public entrypoint in '#{reference.constant.package.public_path}' that you can use instead?"
|