packwerk 1.1.0 → 1.3.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 +17 -8
- data/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +129 -111
- data/README.md +10 -3
- data/TROUBLESHOOT.md +2 -2
- data/USAGE.md +30 -30
- data/bin/m +29 -0
- data/bin/rake +29 -0
- data/bin/rubocop +29 -0
- data/bin/srb +29 -0
- data/bin/tapioca +29 -0
- data/dev.yml +7 -7
- data/exe/packwerk +1 -1
- data/gemfiles/Gemfile-rails-6-0 +22 -0
- data/lib/packwerk.rb +72 -34
- data/lib/packwerk/application_load_paths.rb +21 -10
- data/lib/packwerk/application_validator.rb +104 -84
- data/lib/packwerk/association_inspector.rb +23 -11
- data/lib/packwerk/checker.rb +4 -7
- data/lib/packwerk/cli.rb +36 -129
- data/lib/packwerk/configuration.rb +10 -2
- data/lib/packwerk/const_node_inspector.rb +13 -14
- data/lib/packwerk/constant_discovery.rb +2 -0
- data/lib/packwerk/constant_name_inspector.rb +0 -1
- data/lib/packwerk/dependency_checker.rb +12 -17
- data/lib/packwerk/deprecated_references.rb +8 -10
- data/lib/packwerk/file_processor.rb +0 -4
- data/lib/packwerk/formatters/offenses_formatter.rb +52 -0
- data/lib/packwerk/formatters/progress_formatter.rb +9 -4
- data/lib/packwerk/generators/configuration_file.rb +0 -1
- data/lib/packwerk/inflector.rb +0 -2
- data/lib/packwerk/node.rb +9 -2
- data/lib/packwerk/node_processor.rb +15 -32
- data/lib/packwerk/node_processor_factory.rb +0 -5
- data/lib/packwerk/node_visitor.rb +1 -4
- data/lib/packwerk/offense.rb +2 -8
- data/lib/packwerk/offense_collection.rb +84 -0
- data/lib/packwerk/offenses_formatter.rb +19 -0
- data/lib/packwerk/output_style.rb +20 -0
- data/lib/packwerk/output_styles/coloured.rb +29 -0
- data/lib/packwerk/output_styles/plain.rb +26 -0
- data/lib/packwerk/package.rb +17 -1
- data/lib/packwerk/package_set.rb +2 -3
- data/lib/packwerk/parse_run.rb +106 -0
- data/lib/packwerk/parsed_constant_definitions.rb +2 -4
- data/lib/packwerk/parsers.rb +0 -2
- data/lib/packwerk/parsers/erb.rb +0 -2
- data/lib/packwerk/parsers/factory.rb +1 -3
- data/lib/packwerk/privacy_checker.rb +22 -17
- data/lib/packwerk/reference_extractor.rb +0 -8
- data/lib/packwerk/reference_offense.rb +49 -0
- data/lib/packwerk/result.rb +9 -0
- data/lib/packwerk/run_context.rb +4 -21
- data/lib/packwerk/sanity_checker.rb +1 -3
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk/violation_type.rb +0 -2
- data/library.yml +1 -1
- data/packwerk.gemspec +1 -0
- data/service.yml +1 -4
- data/shipit.rubygems.yml +5 -1
- data/sorbet/rbi/gems/{actioncable@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actioncable@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +56 -36
- data/sorbet/rbi/gems/{actionmailbox@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionmailbox@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +25 -28
- data/sorbet/rbi/gems/{actionmailer@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionmailer@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +43 -24
- data/sorbet/rbi/gems/{actionpack@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionpack@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +382 -284
- data/sorbet/rbi/gems/{actiontext@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actiontext@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +76 -40
- data/sorbet/rbi/gems/{actionview@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → actionview@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +206 -195
- data/sorbet/rbi/gems/{activejob@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activejob@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +64 -75
- data/sorbet/rbi/gems/{activemodel@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activemodel@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +103 -56
- data/sorbet/rbi/gems/{activerecord@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activerecord@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +1250 -898
- data/sorbet/rbi/gems/{activestorage@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activestorage@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +92 -120
- data/sorbet/rbi/gems/{activesupport@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → activesupport@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +292 -193
- data/sorbet/rbi/gems/{ast@2.4.1.rbi → ast@2.4.2.rbi} +2 -1
- data/sorbet/rbi/gems/{better_html@1.0.15.rbi → better_html@1.0.16.rbi} +2 -2
- data/sorbet/rbi/gems/{concurrent-ruby@1.1.6.rbi → concurrent-ruby@1.1.8.rbi} +12 -9
- data/sorbet/rbi/gems/{erubi@1.9.0.rbi → erubi@1.10.0.rbi} +3 -1
- data/sorbet/rbi/gems/{i18n@1.8.2.rbi → i18n@1.8.10.rbi} +19 -52
- data/sorbet/rbi/gems/{loofah@2.5.0.rbi → loofah@2.9.0.rbi} +3 -1
- data/sorbet/rbi/gems/marcel@1.0.0.rbi +70 -0
- data/sorbet/rbi/gems/{mini_mime@1.0.2.rbi → mini_mime@1.0.3.rbi} +6 -6
- data/sorbet/rbi/gems/{mini_portile2@2.4.0.rbi → minitest-focus@1.2.1.rbi} +2 -2
- data/sorbet/rbi/gems/{minitest@5.14.0.rbi → minitest@5.14.4.rbi} +31 -29
- data/sorbet/rbi/gems/{mocha@1.11.2.rbi → mocha@1.12.0.rbi} +25 -36
- data/sorbet/rbi/gems/{nio4r@2.5.2.rbi → nio4r@2.5.7.rbi} +21 -20
- data/sorbet/rbi/gems/{nokogiri@1.10.9.rbi → nokogiri@1.11.2.rbi} +193 -154
- data/sorbet/rbi/gems/parallel@1.20.1.rbi +117 -0
- data/sorbet/rbi/gems/parlour@6.0.0.rbi +1272 -0
- data/sorbet/rbi/gems/{parser@2.7.1.4.rbi → parser@3.0.0.0.rbi} +287 -174
- data/sorbet/rbi/gems/{pry@0.13.1.rbi → pry@0.14.0.rbi} +1 -1
- data/sorbet/rbi/gems/racc@1.5.2.rbi +57 -0
- data/sorbet/rbi/gems/{rack@2.2.2.rbi → rack@2.2.3.rbi} +23 -35
- data/sorbet/rbi/gems/{rails@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → rails@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +1 -1
- data/sorbet/rbi/gems/{railties@6.1.0.alpha-d80c18a391e33552ae2d943e68af56946f883f65.rbi → railties@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi} +132 -121
- data/sorbet/rbi/gems/{rake@13.0.1.rbi → rake@13.0.3.rbi} +16 -20
- data/sorbet/rbi/gems/{parallel@1.19.1.rbi → regexp_parser@2.1.1.rbi} +2 -2
- data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +8 -0
- data/sorbet/rbi/gems/{rubocop-performance@1.5.2.rbi → rubocop-performance@1.10.2.rbi} +1 -1
- data/sorbet/rbi/gems/{rubocop-shopify@1.0.2.rbi → rubocop-shopify@2.0.1.rbi} +1 -1
- data/sorbet/rbi/gems/{rubocop-sorbet@0.3.7.rbi → rubocop-sorbet@0.6.1.rbi} +1 -1
- data/sorbet/rbi/gems/{rubocop@0.82.0.rbi → rubocop@1.12.0.rbi} +1 -1
- data/sorbet/rbi/gems/{ruby-progressbar@1.10.1.rbi → ruby-progressbar@1.11.0.rbi} +1 -1
- data/sorbet/rbi/gems/spoom@1.1.0.rbi +1061 -0
- data/sorbet/rbi/gems/{spring@2.1.0.rbi → spring@2.1.1.rbi} +7 -7
- data/sorbet/rbi/gems/{sprockets-rails@3.2.1.rbi → sprockets-rails@3.2.2.rbi} +88 -68
- data/sorbet/rbi/gems/{sprockets@4.0.0.rbi → sprockets@4.0.2.rbi} +8 -7
- data/sorbet/rbi/gems/{tapioca@0.4.5.rbi → tapioca@0.4.19.rbi} +109 -24
- data/sorbet/rbi/gems/{thor@1.0.1.rbi → thor@1.1.0.rbi} +16 -15
- data/sorbet/rbi/gems/{tzinfo@2.0.2.rbi → tzinfo@2.0.4.rbi} +21 -2
- data/sorbet/rbi/gems/{unicode-display_width@1.7.0.rbi → unicode-display_width@2.0.0.rbi} +1 -1
- data/sorbet/rbi/gems/{websocket-driver@0.7.1.rbi → websocket-driver@0.7.3.rbi} +29 -29
- data/sorbet/rbi/gems/{websocket-extensions@0.1.4.rbi → websocket-extensions@0.1.5.rbi} +2 -2
- data/sorbet/rbi/gems/zeitwerk@2.4.2.rbi +177 -0
- data/sorbet/tapioca/require.rb +1 -0
- metadata +83 -65
- data/lib/packwerk/cache_deprecated_references.rb +0 -47
- data/lib/packwerk/checking_deprecated_references.rb +0 -40
- data/lib/packwerk/commands/detect_stale_violations_command.rb +0 -63
- data/lib/packwerk/commands/offense_progress_marker.rb +0 -24
- data/lib/packwerk/detect_stale_deprecated_references.rb +0 -14
- data/lib/packwerk/generators/application_validation.rb +0 -62
- data/lib/packwerk/generators/templates/packwerk +0 -23
- data/lib/packwerk/generators/templates/packwerk_validator_test.rb +0 -11
- data/lib/packwerk/output_styles.rb +0 -41
- data/lib/packwerk/reference_lister.rb +0 -23
- data/lib/packwerk/spring_command.rb +0 -28
- data/lib/packwerk/updating_deprecated_references.rb +0 -14
- data/sorbet/rbi/gems/jaro_winkler@1.5.4.rbi +0 -8
- data/sorbet/rbi/gems/marcel@0.3.3.rbi +0 -30
- data/sorbet/rbi/gems/mimemagic@0.3.5.rbi +0 -47
- data/sorbet/rbi/gems/parlour@4.0.1.rbi +0 -561
- data/sorbet/rbi/gems/spoom@1.0.4.rbi +0 -418
- data/sorbet/rbi/gems/zeitwerk@2.3.0.rbi +0 -8
@@ -22,7 +22,10 @@ module Packwerk
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def from_packwerk_config(path)
|
25
|
-
new(
|
25
|
+
new(
|
26
|
+
YAML.load_file(path) || {},
|
27
|
+
config_path: path
|
28
|
+
)
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
@@ -42,10 +45,15 @@ module Packwerk
|
|
42
45
|
@root_path = File.expand_path(root)
|
43
46
|
@package_paths = configs["package_paths"] || "**/"
|
44
47
|
@custom_associations = configs["custom_associations"] || []
|
45
|
-
@load_paths = configs["load_paths"]
|
48
|
+
@load_paths = configs["load_paths"] || []
|
46
49
|
@inflections_file = File.expand_path(configs["inflections_file"] || "config/inflections.yml", @root_path)
|
50
|
+
@parallel = configs.key?("parallel") ? configs["parallel"] : true
|
47
51
|
|
48
52
|
@config_path = config_path
|
49
53
|
end
|
54
|
+
|
55
|
+
def parallel?
|
56
|
+
@parallel
|
57
|
+
end
|
50
58
|
end
|
51
59
|
end
|
@@ -1,20 +1,24 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "packwerk/constant_name_inspector"
|
5
|
-
|
6
4
|
module Packwerk
|
7
5
|
# Extracts a constant name from an AST node of type :const
|
8
6
|
class ConstNodeInspector
|
7
|
+
extend T::Sig
|
9
8
|
include ConstantNameInspector
|
10
9
|
|
10
|
+
sig do
|
11
|
+
override
|
12
|
+
.params(node: AST::Node, ancestors: T::Array[AST::Node])
|
13
|
+
.returns(T.nilable(String))
|
14
|
+
end
|
11
15
|
def constant_name_from_node(node, ancestors:)
|
12
16
|
return nil unless Node.constant?(node)
|
13
17
|
parent = ancestors.first
|
14
18
|
return nil unless root_constant?(parent)
|
15
19
|
|
16
20
|
if parent && constant_in_module_or_class_definition?(node, parent: parent)
|
17
|
-
fully_qualify_constant(
|
21
|
+
fully_qualify_constant(ancestors)
|
18
22
|
else
|
19
23
|
begin
|
20
24
|
Node.constant_name(node)
|
@@ -28,27 +32,22 @@ module Packwerk
|
|
28
32
|
|
29
33
|
# Only process the root `const` node for namespaced constant references. For example, in the
|
30
34
|
# reference `Spam::Eggs::Thing`, we only process the const node associated with `Spam`.
|
35
|
+
sig { params(parent: T.nilable(AST::Node)).returns(T::Boolean) }
|
31
36
|
def root_constant?(parent)
|
32
37
|
!(parent && Node.constant?(parent))
|
33
38
|
end
|
34
39
|
|
40
|
+
sig { params(node: AST::Node, parent: AST::Node).returns(T.nilable(T::Boolean)) }
|
35
41
|
def constant_in_module_or_class_definition?(node, parent:)
|
36
42
|
parent_name = Node.module_name_from_definition(parent)
|
37
43
|
parent_name && parent_name == Node.constant_name(node)
|
38
44
|
end
|
39
45
|
|
40
|
-
|
46
|
+
sig { params(ancestors: T::Array[AST::Node]).returns(String) }
|
47
|
+
def fully_qualify_constant(ancestors)
|
41
48
|
# We're defining a class with this name, in which case the constant is implicitly fully qualified by its
|
42
49
|
# enclosing namespace
|
43
|
-
|
44
|
-
name ||= generate_qualified_constant(node, ancestors)
|
45
|
-
"::" + name
|
46
|
-
end
|
47
|
-
|
48
|
-
def generate_qualified_constant(node, ancestors:)
|
49
|
-
namespace_path = Node.enclosing_namespace_path(node, ancestors: ancestors)
|
50
|
-
constant_name = Node.constant_name(node)
|
51
|
-
namespace_path.push(constant_name).join("::")
|
50
|
+
"::" + Node.parent_module_name(ancestors: ancestors)
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "constant_resolver"
|
5
|
+
|
4
6
|
module Packwerk
|
5
7
|
# Get information about (partially qualified) constants without loading the application code.
|
6
8
|
# Information gathered: Fully qualified name, path to file containing the definition, package,
|
@@ -1,31 +1,26 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "packwerk/violation_type"
|
5
|
-
require "packwerk/checker"
|
6
|
-
|
7
4
|
module Packwerk
|
8
5
|
class DependencyChecker
|
6
|
+
extend T::Sig
|
9
7
|
include Checker
|
10
8
|
|
9
|
+
sig { override.returns(ViolationType) }
|
11
10
|
def violation_type
|
12
11
|
ViolationType::Dependency
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
return if reference_lister.listed?(reference, violation_type: violation_type)
|
20
|
-
true
|
14
|
+
sig do
|
15
|
+
override
|
16
|
+
.params(reference: Packwerk::Reference)
|
17
|
+
.returns(T::Boolean)
|
21
18
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
"Are we missing an abstraction?\n" \
|
28
|
-
"Is the code making the reference, and the referenced constant, in the right packages?\n"
|
19
|
+
def invalid_reference?(reference)
|
20
|
+
return false unless reference.source_package
|
21
|
+
return false unless reference.source_package.enforce_dependencies?
|
22
|
+
return false if reference.source_package.dependency?(reference.constant.package)
|
23
|
+
true
|
29
24
|
end
|
30
25
|
end
|
31
26
|
end
|
@@ -1,19 +1,13 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "sorbet-runtime"
|
5
4
|
require "yaml"
|
6
|
-
require "sorbet-runtime"
|
7
|
-
|
8
|
-
require "packwerk/reference"
|
9
|
-
require "packwerk/reference_lister"
|
10
|
-
require "packwerk/violation_type"
|
11
5
|
|
12
6
|
module Packwerk
|
13
7
|
class DeprecatedReferences
|
14
8
|
extend T::Sig
|
15
|
-
include ReferenceLister
|
16
9
|
|
10
|
+
sig { params(package: Packwerk::Package, filepath: String).void }
|
17
11
|
def initialize(package, filepath)
|
18
12
|
@package = package
|
19
13
|
@filepath = filepath
|
@@ -23,7 +17,6 @@ module Packwerk
|
|
23
17
|
sig do
|
24
18
|
params(reference: Packwerk::Reference, violation_type: ViolationType)
|
25
19
|
.returns(T::Boolean)
|
26
|
-
.override
|
27
20
|
end
|
28
21
|
def listed?(reference, violation_type:)
|
29
22
|
violated_constants_found = deprecated_references.dig(reference.constant.package.name, reference.constant.name)
|
@@ -35,17 +28,19 @@ module Packwerk
|
|
35
28
|
violated_constants_found.fetch("violations", []).include?(violation_type.serialize)
|
36
29
|
end
|
37
30
|
|
31
|
+
sig { params(reference: Packwerk::Reference, violation_type: Packwerk::ViolationType).returns(T::Boolean) }
|
38
32
|
def add_entries(reference, violation_type)
|
39
33
|
package_violations = @new_entries.fetch(reference.constant.package.name, {})
|
40
34
|
entries_for_file = package_violations[reference.constant.name] ||= {}
|
41
35
|
|
42
36
|
entries_for_file["violations"] ||= []
|
43
|
-
entries_for_file["violations"] << violation_type
|
37
|
+
entries_for_file["violations"] << violation_type.serialize
|
44
38
|
|
45
39
|
entries_for_file["files"] ||= []
|
46
40
|
entries_for_file["files"] << reference.relative_path.to_s
|
47
41
|
|
48
42
|
@new_entries[reference.constant.package.name] = package_violations
|
43
|
+
listed?(reference, violation_type: violation_type)
|
49
44
|
end
|
50
45
|
|
51
46
|
sig { returns(T::Boolean) }
|
@@ -66,6 +61,7 @@ module Packwerk
|
|
66
61
|
end
|
67
62
|
end
|
68
63
|
|
64
|
+
sig { void }
|
69
65
|
def dump
|
70
66
|
if @new_entries.empty?
|
71
67
|
File.delete(@filepath) if File.exist?(@filepath)
|
@@ -77,7 +73,7 @@ module Packwerk
|
|
77
73
|
#
|
78
74
|
# You can regenerate this file using the following command:
|
79
75
|
#
|
80
|
-
#
|
76
|
+
# packwerk update-deprecations #{@package.name}
|
81
77
|
MESSAGE
|
82
78
|
File.open(@filepath, "w") do |f|
|
83
79
|
f.write(message)
|
@@ -88,6 +84,7 @@ module Packwerk
|
|
88
84
|
|
89
85
|
private
|
90
86
|
|
87
|
+
sig { returns(Hash) }
|
91
88
|
def prepare_entries_for_dump
|
92
89
|
@new_entries.each do |package_name, package_violations|
|
93
90
|
package_violations.each do |_, entries_for_file|
|
@@ -100,6 +97,7 @@ module Packwerk
|
|
100
97
|
@new_entries = @new_entries.sort.to_h
|
101
98
|
end
|
102
99
|
|
100
|
+
sig { returns(Hash) }
|
103
101
|
def deprecated_references
|
104
102
|
@deprecated_references ||= if File.exist?(@filepath)
|
105
103
|
YAML.load_file(@filepath) || {}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Formatters
|
6
|
+
class OffensesFormatter
|
7
|
+
include Packwerk::OffensesFormatter
|
8
|
+
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(style: OutputStyle).void }
|
12
|
+
def initialize(style: OutputStyles::Plain.new)
|
13
|
+
@style = style
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { override.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
17
|
+
def show_offenses(offenses)
|
18
|
+
return "No offenses detected" if offenses.empty?
|
19
|
+
|
20
|
+
<<~EOS
|
21
|
+
#{offenses_list(offenses)}
|
22
|
+
#{offenses_summary(offenses)}
|
23
|
+
EOS
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.params(offense_collection: Packwerk::OffenseCollection).returns(String) }
|
27
|
+
def show_stale_violations(offense_collection)
|
28
|
+
if offense_collection.stale_violations?
|
29
|
+
"There were stale violations found, please run `packwerk update-deprecations`"
|
30
|
+
else
|
31
|
+
"No stale violations detected"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
sig { params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
38
|
+
def offenses_list(offenses)
|
39
|
+
offenses
|
40
|
+
.compact
|
41
|
+
.map { |offense| offense.to_s(@style) }
|
42
|
+
.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
46
|
+
def offenses_summary(offenses)
|
47
|
+
offenses_string = Inflector.default.pluralize("offense", offenses.length)
|
48
|
+
"#{offenses.length} #{offenses_string} detected"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
|
4
4
|
require "benchmark"
|
5
5
|
|
6
|
-
require "packwerk/inflector"
|
7
|
-
require "packwerk/output_styles"
|
8
|
-
|
9
6
|
module Packwerk
|
10
7
|
module Formatters
|
11
8
|
class ProgressFormatter
|
12
|
-
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(out: T.any(StringIO, IO), style: OutputStyle).void }
|
12
|
+
def initialize(out, style: OutputStyles::Plain.new)
|
13
13
|
@out = out
|
14
14
|
@style = style
|
15
15
|
end
|
@@ -41,6 +41,11 @@ module Packwerk
|
|
41
41
|
@out.puts
|
42
42
|
@out.puts("📦 Finished in #{execution_time.round(2)} seconds")
|
43
43
|
end
|
44
|
+
|
45
|
+
def interrupted
|
46
|
+
@out.puts
|
47
|
+
@out.puts("Manually interrupted. Violations caught so far are listed below:")
|
48
|
+
end
|
44
49
|
end
|
45
50
|
end
|
46
51
|
end
|
data/lib/packwerk/inflector.rb
CHANGED
data/lib/packwerk/node.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "parser"
|
4
5
|
require "parser/ast/node"
|
5
6
|
|
6
7
|
module Packwerk
|
@@ -9,6 +10,8 @@ module Packwerk
|
|
9
10
|
Location = Struct.new(:line, :column)
|
10
11
|
|
11
12
|
class << self
|
13
|
+
extend T::Sig
|
14
|
+
|
12
15
|
def class_or_module_name(class_or_module_node)
|
13
16
|
case type_of(class_or_module_node)
|
14
17
|
when CLASS, MODULE
|
@@ -27,7 +30,7 @@ module Packwerk
|
|
27
30
|
case type_of(constant_node)
|
28
31
|
when CONSTANT_ROOT_NAMESPACE
|
29
32
|
""
|
30
|
-
when CONSTANT, CONSTANT_ASSIGNMENT
|
33
|
+
when CONSTANT, CONSTANT_ASSIGNMENT, SELF
|
31
34
|
# (const nil :Foo)
|
32
35
|
# "Foo"
|
33
36
|
# (const (cbase) :Foo)
|
@@ -40,6 +43,8 @@ module Packwerk
|
|
40
43
|
# "::Foo = 1"
|
41
44
|
# (casgn (lvar :a) :Foo (int 1))
|
42
45
|
# "a::Foo = 1"
|
46
|
+
# (casgn (self) :Foo (int 1))
|
47
|
+
# "self::Foo = 1"
|
43
48
|
namespace, name = constant_node.children
|
44
49
|
if namespace
|
45
50
|
[constant_name(namespace), name].join("::")
|
@@ -176,6 +181,7 @@ module Packwerk
|
|
176
181
|
class_node.children[1]
|
177
182
|
end
|
178
183
|
|
184
|
+
sig { params(ancestors: T::Array[AST::Node]).returns(String) }
|
179
185
|
def parent_module_name(ancestors:)
|
180
186
|
definitions = ancestors
|
181
187
|
.select { |n| [CLASS, MODULE, CONSTANT_ASSIGNMENT, BLOCK].include?(type_of(n)) }
|
@@ -204,12 +210,13 @@ module Packwerk
|
|
204
210
|
HASH_PAIR = :pair
|
205
211
|
METHOD_CALL = :send
|
206
212
|
MODULE = :module
|
213
|
+
SELF = :self
|
207
214
|
STRING = :str
|
208
215
|
SYMBOL = :sym
|
209
216
|
|
210
217
|
private_constant(
|
211
218
|
:BLOCK, :CLASS, :CONSTANT, :CONSTANT_ASSIGNMENT, :CONSTANT_ROOT_NAMESPACE, :HASH, :HASH_PAIR, :METHOD_CALL,
|
212
|
-
:MODULE, :STRING, :SYMBOL,
|
219
|
+
:MODULE, :SELF, :STRING, :SYMBOL,
|
213
220
|
)
|
214
221
|
|
215
222
|
def type_of(node)
|
@@ -1,11 +1,6 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "packwerk/node"
|
5
|
-
require "packwerk/offense"
|
6
|
-
require "packwerk/checker"
|
7
|
-
require "packwerk/reference_lister"
|
8
|
-
|
9
4
|
module Packwerk
|
10
5
|
class NodeProcessor
|
11
6
|
extend T::Sig
|
@@ -13,48 +8,36 @@ module Packwerk
|
|
13
8
|
sig do
|
14
9
|
params(
|
15
10
|
reference_extractor: ReferenceExtractor,
|
16
|
-
reference_lister: ReferenceLister,
|
17
11
|
filename: String,
|
18
12
|
checkers: T::Array[Checker]
|
19
13
|
).void
|
20
14
|
end
|
21
|
-
def initialize(reference_extractor:,
|
15
|
+
def initialize(reference_extractor:, filename:, checkers:)
|
22
16
|
@reference_extractor = reference_extractor
|
23
|
-
@reference_lister = reference_lister
|
24
17
|
@filename = filename
|
25
18
|
@checkers = checkers
|
26
19
|
end
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
21
|
+
sig { params(node: Parser::AST::Node, ancestors: T::Array[Parser::AST::Node]).returns(T::Array[Offense]) }
|
22
|
+
def call(node, ancestors)
|
23
|
+
return [] unless Node.method_call?(node) || Node.constant?(node)
|
24
|
+
reference = @reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
|
25
|
+
check_reference(reference, node)
|
33
26
|
end
|
34
27
|
|
35
28
|
private
|
36
29
|
|
37
30
|
def check_reference(reference, node)
|
38
|
-
return
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
Inference details: this is a reference to #{constant.name} which seems to be defined in #{constant.location}.
|
48
|
-
To receive help interpreting or resolving this error message, see: https://github.com/Shopify/packwerk/blob/main/TROUBLESHOOT.md#Troubleshooting-violations
|
49
|
-
EOS
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
def failed_check(reference)
|
54
|
-
failing_checker = @checkers.find do |checker|
|
55
|
-
checker.invalid_reference?(reference, @reference_lister)
|
31
|
+
return [] unless reference
|
32
|
+
@checkers.each_with_object([]) do |checker, violations|
|
33
|
+
next unless checker.invalid_reference?(reference)
|
34
|
+
offense = Packwerk::ReferenceOffense.new(
|
35
|
+
location: Node.location(node),
|
36
|
+
reference: reference,
|
37
|
+
violation_type: checker.violation_type
|
38
|
+
)
|
39
|
+
violations << offense
|
56
40
|
end
|
57
|
-
failing_checker&.message_for(reference)
|
58
41
|
end
|
59
42
|
end
|
60
43
|
end
|