packwerk 2.0.0 → 2.2.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.
- 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?"
|