packwerk 2.2.2 → 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.
- 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 +7 -7
- data/TROUBLESHOOT.md +1 -23
- data/USAGE.md +149 -59
- data/dev.yml +1 -1
- data/exe/packwerk +1 -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 +56 -31
- 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 +43 -23
- 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/{deprecated_references.rb → package_todo.rb} +29 -13
- data/lib/packwerk/parse_run.rb +29 -36
- 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 +11 -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 +35 -16
- 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 "ast/node"
|
@@ -6,27 +6,40 @@ require "ast/node"
|
|
6
6
|
module Packwerk
|
7
7
|
# A collection of constant definitions parsed from an Abstract Syntax Tree (AST).
|
8
8
|
class ParsedConstantDefinitions
|
9
|
+
extend T::Sig
|
10
|
+
|
9
11
|
class << self
|
12
|
+
extend T::Sig
|
13
|
+
|
10
14
|
# What fully qualified constants can this constant refer to in this context?
|
15
|
+
sig { params(constant_name: String, namespace_path: T::Array[T.nilable(String)]).returns(T::Array[String]) }
|
11
16
|
def reference_qualifications(constant_name, namespace_path:)
|
12
17
|
return [constant_name] if constant_name.start_with?("::")
|
13
18
|
|
14
19
|
resolved_constant_name = "::#{constant_name}"
|
15
20
|
|
16
21
|
possible_namespaces = namespace_path.each_with_object([""]) do |current, acc|
|
17
|
-
acc << "#{acc.last}::#{current}" if
|
22
|
+
acc << "#{acc.last}::#{current}" if current
|
18
23
|
end
|
19
24
|
|
20
25
|
possible_namespaces.map { |namespace| namespace + resolved_constant_name }
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
29
|
+
sig { params(root_node: T.nilable(AST::Node)).void }
|
24
30
|
def initialize(root_node:)
|
25
|
-
@local_definitions = {}
|
31
|
+
@local_definitions = T.let({}, T::Hash[String, T.nilable(Node::Location)])
|
26
32
|
|
27
33
|
collect_local_definitions_from_root(root_node) if root_node
|
28
34
|
end
|
29
35
|
|
36
|
+
sig do
|
37
|
+
params(
|
38
|
+
constant_name: String,
|
39
|
+
location: T.nilable(Node::Location),
|
40
|
+
namespace_path: T::Array[String],
|
41
|
+
).returns(T::Boolean)
|
42
|
+
end
|
30
43
|
def local_reference?(constant_name, location: nil, namespace_path: [])
|
31
44
|
qualifications = self.class.reference_qualifications(constant_name, namespace_path: namespace_path)
|
32
45
|
|
@@ -38,13 +51,14 @@ module Packwerk
|
|
38
51
|
|
39
52
|
private
|
40
53
|
|
54
|
+
sig { params(node: AST::Node, current_namespace_path: T::Array[T.nilable(String)]).void }
|
41
55
|
def collect_local_definitions_from_root(node, current_namespace_path = [])
|
42
56
|
if NodeHelpers.constant_assignment?(node)
|
43
57
|
add_definition(NodeHelpers.constant_name(node), current_namespace_path, NodeHelpers.name_location(node))
|
44
58
|
elsif NodeHelpers.module_name_from_definition(node)
|
45
59
|
# handle compact constant nesting (e.g. "module Sales::Order")
|
46
|
-
tempnode = node
|
47
|
-
while (tempnode = NodeHelpers.each_child(tempnode).find { |
|
60
|
+
tempnode = T.let(node, T.nilable(AST::Node))
|
61
|
+
while (tempnode = NodeHelpers.each_child(T.must(tempnode)).find { |node| NodeHelpers.constant?(node) })
|
48
62
|
add_definition(NodeHelpers.constant_name(tempnode), current_namespace_path,
|
49
63
|
NodeHelpers.name_location(tempnode))
|
50
64
|
end
|
@@ -55,10 +69,19 @@ module Packwerk
|
|
55
69
|
NodeHelpers.each_child(node) { |child| collect_local_definitions_from_root(child, current_namespace_path) }
|
56
70
|
end
|
57
71
|
|
72
|
+
sig do
|
73
|
+
params(
|
74
|
+
constant_name: String,
|
75
|
+
current_namespace_path: T::Array[T.nilable(String)],
|
76
|
+
location: T.nilable(Node::Location),
|
77
|
+
).void
|
78
|
+
end
|
58
79
|
def add_definition(constant_name, current_namespace_path, location)
|
59
80
|
resolved_constant = [""].concat(current_namespace_path).push(constant_name).join("::")
|
60
81
|
|
61
82
|
@local_definitions[resolved_constant] = location
|
62
83
|
end
|
63
84
|
end
|
85
|
+
|
86
|
+
private_constant :ParsedConstantDefinitions
|
64
87
|
end
|
data/lib/packwerk/parsers/erb.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "ast/node"
|
@@ -9,19 +9,24 @@ require "parser/source/buffer"
|
|
9
9
|
module Packwerk
|
10
10
|
module Parsers
|
11
11
|
class Erb
|
12
|
+
extend T::Sig
|
13
|
+
|
12
14
|
include ParserInterface
|
13
15
|
|
16
|
+
sig { params(parser_class: T.untyped, ruby_parser: Ruby).void }
|
14
17
|
def initialize(parser_class: BetterHtml::Parser, ruby_parser: Ruby.new)
|
15
|
-
@parser_class = parser_class
|
18
|
+
@parser_class = T.let(parser_class, T.class_of(BetterHtml::Parser))
|
16
19
|
@ruby_parser = ruby_parser
|
17
20
|
end
|
18
21
|
|
22
|
+
sig { override.params(io: T.any(IO, StringIO), file_path: String).returns(T.untyped) }
|
19
23
|
def call(io:, file_path: "<unknown>")
|
20
24
|
buffer = Parser::Source::Buffer.new(file_path)
|
21
25
|
buffer.source = io.read
|
22
26
|
parse_buffer(buffer, file_path: file_path)
|
23
27
|
end
|
24
28
|
|
29
|
+
sig { params(buffer: Parser::Source::Buffer, file_path: String).returns(T.nilable(AST::Node)) }
|
25
30
|
def parse_buffer(buffer, file_path:)
|
26
31
|
parser = @parser_class.new(buffer, template_language: :html)
|
27
32
|
to_ruby_ast(parser.ast, file_path)
|
@@ -35,12 +40,18 @@ module Packwerk
|
|
35
40
|
|
36
41
|
private
|
37
42
|
|
43
|
+
sig do
|
44
|
+
params(
|
45
|
+
erb_ast: T.all(::AST::Node, Object),
|
46
|
+
file_path: String
|
47
|
+
).returns(T.nilable(::AST::Node))
|
48
|
+
end
|
38
49
|
def to_ruby_ast(erb_ast, file_path)
|
39
50
|
# Note that we're not using the source location (line/column) at the moment, but if we did
|
40
51
|
# care about that, we'd need to tweak this to insert empty lines and spaces so that things
|
41
52
|
# line up with the ERB file
|
42
|
-
code_pieces = code_nodes(erb_ast).map do |node|
|
43
|
-
node.children.first
|
53
|
+
code_pieces = T.must(code_nodes(erb_ast)).map do |node|
|
54
|
+
T.cast(node, ::AST::Node).children.first
|
44
55
|
end
|
45
56
|
|
46
57
|
@ruby_parser.call(
|
@@ -49,6 +60,14 @@ module Packwerk
|
|
49
60
|
)
|
50
61
|
end
|
51
62
|
|
63
|
+
sig do
|
64
|
+
params(
|
65
|
+
node: T.any(::AST::Node, String, NilClass),
|
66
|
+
block: T.nilable(T.proc.params(arg0: ::AST::Node).void),
|
67
|
+
).returns(
|
68
|
+
T.any(T::Enumerator[::AST::Node], T::Array[String], NilClass)
|
69
|
+
)
|
70
|
+
end
|
52
71
|
def code_nodes(node, &block)
|
53
72
|
return enum_for(:code_nodes, node) unless block
|
54
73
|
return unless node.is_a?(::AST::Node)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "singleton"
|
@@ -20,20 +20,29 @@ module Packwerk
|
|
20
20
|
ERB_REGEX = /\.erb\Z/
|
21
21
|
private_constant :ERB_REGEX
|
22
22
|
|
23
|
+
sig { void }
|
24
|
+
def initialize
|
25
|
+
@ruby_parser = T.let(nil, T.nilable(ParserInterface))
|
26
|
+
@erb_parser = T.let(nil, T.nilable(ParserInterface))
|
27
|
+
@erb_parser_class = T.let(nil, T.nilable(Class))
|
28
|
+
end
|
29
|
+
|
23
30
|
sig { params(path: String).returns(T.nilable(ParserInterface)) }
|
24
31
|
def for_path(path)
|
25
32
|
case path
|
26
33
|
when RUBY_REGEX
|
27
34
|
@ruby_parser ||= Ruby.new
|
28
35
|
when ERB_REGEX
|
29
|
-
@erb_parser ||= erb_parser_class.new
|
36
|
+
@erb_parser ||= T.unsafe(erb_parser_class).new
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
40
|
+
sig { returns(Class) }
|
33
41
|
def erb_parser_class
|
34
42
|
@erb_parser_class ||= Erb
|
35
43
|
end
|
36
44
|
|
45
|
+
sig { params(klass: T.nilable(Class)).void }
|
37
46
|
def erb_parser_class=(klass)
|
38
47
|
@erb_parser_class = klass
|
39
48
|
@erb_parser = nil
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "parser"
|
@@ -7,9 +7,14 @@ require "parser/current"
|
|
7
7
|
module Packwerk
|
8
8
|
module Parsers
|
9
9
|
class Ruby
|
10
|
+
extend T::Sig
|
11
|
+
|
10
12
|
include ParserInterface
|
11
13
|
|
12
14
|
class RaiseExceptionsParser < Parser::CurrentRuby
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { params(builder: T.untyped).void }
|
13
18
|
def initialize(builder)
|
14
19
|
super(builder)
|
15
20
|
super.diagnostics.all_errors_are_fatal = true
|
@@ -17,16 +22,21 @@ module Packwerk
|
|
17
22
|
end
|
18
23
|
|
19
24
|
class TolerateInvalidUtf8Builder < Parser::Builders::Default
|
25
|
+
extend T::Sig
|
26
|
+
|
27
|
+
sig { params(token: T.untyped).returns(T.untyped) }
|
20
28
|
def string_value(token)
|
21
29
|
value(token)
|
22
30
|
end
|
23
31
|
end
|
24
32
|
|
33
|
+
sig { params(parser_class: T.untyped).void }
|
25
34
|
def initialize(parser_class: RaiseExceptionsParser)
|
26
|
-
@builder = TolerateInvalidUtf8Builder.new
|
27
|
-
@parser_class = parser_class
|
35
|
+
@builder = T.let(TolerateInvalidUtf8Builder.new, Object)
|
36
|
+
@parser_class = T.let(parser_class, T.class_of(RaiseExceptionsParser))
|
28
37
|
end
|
29
38
|
|
39
|
+
sig { override.params(io: T.any(IO, StringIO), file_path: String).returns(T.nilable(Parser::AST::Node)) }
|
30
40
|
def call(io:, file_path: "<unknown>")
|
31
41
|
buffer = Parser::Source::Buffer.new(file_path)
|
32
42
|
buffer.source = io.read
|
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
|
@@ -11,8 +11,12 @@ module Packwerk
|
|
11
11
|
class ParseResult < Offense; end
|
12
12
|
|
13
13
|
class ParseError < StandardError
|
14
|
-
|
14
|
+
extend T::Sig
|
15
15
|
|
16
|
+
sig { returns(ParseResult) }
|
17
|
+
attr_reader(:result)
|
18
|
+
|
19
|
+
sig { params(result: ParseResult).void }
|
16
20
|
def initialize(result)
|
17
21
|
super(result.message)
|
18
22
|
@result = result
|
@@ -2,15 +2,17 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "bundler"
|
5
|
+
gem "railties", ">= 6.0"
|
6
|
+
require "rails/railtie"
|
5
7
|
|
6
8
|
module Packwerk
|
7
9
|
# Extracts the load paths from the analyzed application so that we can map constant names to paths.
|
8
|
-
module
|
10
|
+
module RailsLoadPaths
|
9
11
|
class << self
|
10
12
|
extend T::Sig
|
11
13
|
|
12
14
|
sig { params(root: String, environment: String).returns(T::Hash[String, Module]) }
|
13
|
-
def
|
15
|
+
def for(root, environment:)
|
14
16
|
require_application(root, environment)
|
15
17
|
all_paths = extract_application_autoload_paths
|
16
18
|
relevant_paths = filter_relevant_paths(all_paths)
|
@@ -18,6 +20,8 @@ module Packwerk
|
|
18
20
|
relative_path_strings(relevant_paths)
|
19
21
|
end
|
20
22
|
|
23
|
+
private
|
24
|
+
|
21
25
|
sig { returns(T::Hash[String, Module]) }
|
22
26
|
def extract_application_autoload_paths
|
23
27
|
Rails.autoloaders.inject({}) do |h, loader|
|
@@ -44,8 +48,6 @@ module Packwerk
|
|
44
48
|
load_paths.transform_keys { |path| Pathname.new(path).relative_path_from(rails_root).to_s }
|
45
49
|
end
|
46
50
|
|
47
|
-
private
|
48
|
-
|
49
51
|
sig { params(root: String, environment: String).void }
|
50
52
|
def require_application(root, environment)
|
51
53
|
environment_file = "#{root}/config/environment"
|
data/lib/packwerk/reference.rb
CHANGED
@@ -3,5 +3,11 @@
|
|
3
3
|
|
4
4
|
module Packwerk
|
5
5
|
# A reference from a file in one package to a constant that may be defined in a different package.
|
6
|
-
Reference = Struct.new(
|
6
|
+
Reference = Struct.new(
|
7
|
+
:package,
|
8
|
+
:relative_path,
|
9
|
+
:constant,
|
10
|
+
:source_location,
|
11
|
+
keyword_init: true,
|
12
|
+
)
|
7
13
|
end
|
@@ -9,9 +9,11 @@ module Packwerk
|
|
9
9
|
extend T::Sig
|
10
10
|
include Checker
|
11
11
|
|
12
|
-
|
12
|
+
VIOLATION_TYPE = T.let("dependency", String)
|
13
|
+
|
14
|
+
sig { override.returns(String) }
|
13
15
|
def violation_type
|
14
|
-
|
16
|
+
VIOLATION_TYPE
|
15
17
|
end
|
16
18
|
|
17
19
|
sig do
|
@@ -20,9 +22,8 @@ module Packwerk
|
|
20
22
|
.returns(T::Boolean)
|
21
23
|
end
|
22
24
|
def invalid_reference?(reference)
|
23
|
-
return false unless reference.
|
24
|
-
return false
|
25
|
-
return false if reference.source_package.dependency?(reference.constant.package)
|
25
|
+
return false unless reference.package.enforce_dependencies?
|
26
|
+
return false if reference.package.dependency?(reference.constant.package)
|
26
27
|
|
27
28
|
true
|
28
29
|
end
|
@@ -33,14 +34,36 @@ module Packwerk
|
|
33
34
|
.returns(String)
|
34
35
|
end
|
35
36
|
def message(reference)
|
37
|
+
const_name = reference.constant.name
|
38
|
+
const_package = reference.constant.package
|
39
|
+
ref_package = reference.package
|
40
|
+
|
36
41
|
<<~EOS
|
37
|
-
Dependency violation: #{
|
42
|
+
Dependency violation: #{const_name} belongs to '#{const_package}', but '#{ref_package}' does not specify a dependency on '#{const_package}'.
|
38
43
|
Are we missing an abstraction?
|
39
44
|
Is the code making the reference, and the referenced constant, in the right packages?
|
40
45
|
|
41
46
|
#{standard_help_message(reference)}
|
42
47
|
EOS
|
43
48
|
end
|
49
|
+
|
50
|
+
sig { override.params(listed_offense: ReferenceOffense).returns(T::Boolean) }
|
51
|
+
def strict_mode_violation?(listed_offense)
|
52
|
+
referencing_package = listed_offense.reference.package
|
53
|
+
referencing_package.config["enforce_dependencies"] == "strict"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
sig { params(reference: Reference).returns(String) }
|
59
|
+
def standard_help_message(reference)
|
60
|
+
standard_message = <<~EOS
|
61
|
+
Inference details: this is a reference to #{reference.constant.name} which seems to be defined in #{reference.constant.location}.
|
62
|
+
To receive help interpreting or resolving this error message, see: https://github.com/Shopify/packwerk/blob/main/TROUBLESHOOT.md#Troubleshooting-violations
|
63
|
+
EOS
|
64
|
+
|
65
|
+
standard_message.chomp
|
66
|
+
end
|
44
67
|
end
|
45
68
|
end
|
46
69
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Packwerk
|
@@ -38,10 +38,10 @@ module Packwerk
|
|
38
38
|
next if source_package == package_for_constant
|
39
39
|
|
40
40
|
fully_qualified_references << Reference.new(
|
41
|
-
source_package,
|
42
|
-
unresolved_reference.relative_path,
|
43
|
-
constant,
|
44
|
-
unresolved_reference.source_location
|
41
|
+
package: source_package,
|
42
|
+
relative_path: unresolved_reference.relative_path,
|
43
|
+
constant: constant,
|
44
|
+
source_location: unresolved_reference.source_location,
|
45
45
|
)
|
46
46
|
end
|
47
47
|
|
@@ -51,8 +51,8 @@ module Packwerk
|
|
51
51
|
|
52
52
|
sig do
|
53
53
|
params(
|
54
|
-
constant_name_inspectors: T::Array[
|
55
|
-
root_node:
|
54
|
+
constant_name_inspectors: T::Array[ConstantNameInspector],
|
55
|
+
root_node: AST::Node,
|
56
56
|
root_path: String,
|
57
57
|
).void
|
58
58
|
end
|
@@ -63,7 +63,10 @@ module Packwerk
|
|
63
63
|
)
|
64
64
|
@constant_name_inspectors = constant_name_inspectors
|
65
65
|
@root_path = root_path
|
66
|
-
@local_constant_definitions =
|
66
|
+
@local_constant_definitions = T.let(
|
67
|
+
ParsedConstantDefinitions.new(root_node: root_node),
|
68
|
+
ParsedConstantDefinitions,
|
69
|
+
)
|
67
70
|
end
|
68
71
|
|
69
72
|
sig do
|
@@ -110,13 +113,20 @@ module Packwerk
|
|
110
113
|
location = NodeHelpers.location(node)
|
111
114
|
|
112
115
|
UnresolvedReference.new(
|
113
|
-
constant_name,
|
114
|
-
namespace_path,
|
115
|
-
relative_file,
|
116
|
-
location
|
116
|
+
constant_name: constant_name,
|
117
|
+
namespace_path: namespace_path,
|
118
|
+
relative_path: relative_file,
|
119
|
+
source_location: location
|
117
120
|
)
|
118
121
|
end
|
119
122
|
|
123
|
+
sig do
|
124
|
+
params(
|
125
|
+
constant_name: String,
|
126
|
+
name_location: T.nilable(Node::Location),
|
127
|
+
namespace_path: T::Array[String],
|
128
|
+
).returns(T::Boolean)
|
129
|
+
end
|
120
130
|
def local_reference?(constant_name, name_location, namespace_path)
|
121
131
|
@local_constant_definitions.local_reference?(
|
122
132
|
constant_name,
|
@@ -125,4 +135,6 @@ module Packwerk
|
|
125
135
|
)
|
126
136
|
end
|
127
137
|
end
|
138
|
+
|
139
|
+
private_constant :ReferenceExtractor
|
128
140
|
end
|
@@ -10,13 +10,13 @@ module Packwerk
|
|
10
10
|
sig { returns(Reference) }
|
11
11
|
attr_reader :reference
|
12
12
|
|
13
|
-
sig { returns(
|
13
|
+
sig { returns(String) }
|
14
14
|
attr_reader :violation_type
|
15
15
|
|
16
16
|
sig do
|
17
17
|
params(
|
18
18
|
reference: Packwerk::Reference,
|
19
|
-
violation_type:
|
19
|
+
violation_type: String,
|
20
20
|
message: String,
|
21
21
|
location: T.nilable(Node::Location)
|
22
22
|
)
|
data/lib/packwerk/run_context.rb
CHANGED
@@ -8,11 +8,6 @@ module Packwerk
|
|
8
8
|
class RunContext
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
-
DEFAULT_CHECKERS = T.let([
|
12
|
-
::Packwerk::ReferenceChecking::Checkers::DependencyChecker.new,
|
13
|
-
::Packwerk::ReferenceChecking::Checkers::PrivacyChecker.new,
|
14
|
-
], T::Array[ReferenceChecking::Checkers::Checker])
|
15
|
-
|
16
11
|
class << self
|
17
12
|
extend T::Sig
|
18
13
|
|
@@ -44,7 +39,7 @@ module Packwerk
|
|
44
39
|
config_path: T.nilable(String),
|
45
40
|
package_paths: T.nilable(T.any(T::Array[String], String)),
|
46
41
|
custom_associations: AssociationInspector::CustomAssociations,
|
47
|
-
checkers: T::Array[
|
42
|
+
checkers: T::Array[Checker],
|
48
43
|
cache_enabled: T::Boolean,
|
49
44
|
).void
|
50
45
|
end
|
@@ -56,7 +51,7 @@ module Packwerk
|
|
56
51
|
config_path: nil,
|
57
52
|
package_paths: nil,
|
58
53
|
custom_associations: [],
|
59
|
-
checkers:
|
54
|
+
checkers: Checker.all,
|
60
55
|
cache_enabled: false
|
61
56
|
)
|
62
57
|
@root_path = root_path
|
@@ -114,7 +109,7 @@ module Packwerk
|
|
114
109
|
|
115
110
|
sig { returns(ConstantDiscovery) }
|
116
111
|
def context_provider
|
117
|
-
@context_provider ||=
|
112
|
+
@context_provider ||= ConstantDiscovery.new(
|
118
113
|
constant_resolver: resolver,
|
119
114
|
packages: package_set
|
120
115
|
)
|
@@ -132,9 +127,11 @@ module Packwerk
|
|
132
127
|
sig { returns(T::Array[ConstantNameInspector]) }
|
133
128
|
def constant_name_inspectors
|
134
129
|
[
|
135
|
-
|
136
|
-
|
130
|
+
ConstNodeInspector.new,
|
131
|
+
AssociationInspector.new(inflector: @inflector, custom_associations: @custom_associations),
|
137
132
|
]
|
138
133
|
end
|
139
134
|
end
|
135
|
+
|
136
|
+
private_constant :RunContext
|
140
137
|
end
|
@@ -1,24 +1,31 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "spring/commands"
|
5
|
+
require "sorbet-runtime"
|
5
6
|
|
6
7
|
module Packwerk
|
7
8
|
class SpringCommand
|
8
|
-
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(args: T.untyped).returns(String) }
|
12
|
+
def env(args)
|
9
13
|
# Packwerk needs to run in a test environment, which has a set of autoload paths that are
|
10
14
|
# often a superset of the dev/prod paths (for example, test/support/helpers)
|
11
15
|
"test"
|
12
16
|
end
|
13
17
|
|
18
|
+
sig { returns(String) }
|
14
19
|
def exec_name
|
15
20
|
"packwerk"
|
16
21
|
end
|
17
22
|
|
23
|
+
sig { returns(String) }
|
18
24
|
def gem_name
|
19
25
|
"packwerk"
|
20
26
|
end
|
21
27
|
|
28
|
+
sig { returns(T::Boolean) }
|
22
29
|
def call
|
23
30
|
load(Gem.bin_path(gem_name, exec_name))
|
24
31
|
end
|
@@ -26,3 +33,5 @@ module Packwerk
|
|
26
33
|
|
27
34
|
Spring.register_command("packwerk", SpringCommand.new)
|
28
35
|
end
|
36
|
+
|
37
|
+
require "packwerk/disable_sorbet"
|
@@ -6,5 +6,13 @@ module Packwerk
|
|
6
6
|
# Unresolved means that we know how it's referred to in the file,
|
7
7
|
# and we have enough context on that reference to figure out the fully qualified reference such that we
|
8
8
|
# can produce a Reference in a separate pass. However, we have not yet resolved it to its fully qualified version.
|
9
|
-
UnresolvedReference = Struct.new(
|
9
|
+
UnresolvedReference = Struct.new(
|
10
|
+
:constant_name,
|
11
|
+
:namespace_path,
|
12
|
+
:relative_path,
|
13
|
+
:source_location,
|
14
|
+
keyword_init: true,
|
15
|
+
)
|
16
|
+
|
17
|
+
private_constant :UnresolvedReference
|
10
18
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Validator
|
6
|
+
class Result < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
const :ok, T::Boolean
|
10
|
+
const :error_value, T.nilable(String)
|
11
|
+
|
12
|
+
sig { returns(T::Boolean) }
|
13
|
+
def ok?
|
14
|
+
ok
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|