packwerk 2.2.0 → 2.2.2
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 +29 -20
- data/.github/workflows/cla.yml +22 -0
- data/.rubocop.yml +48 -19
- data/Gemfile +7 -2
- data/Gemfile.lock +202 -175
- data/README.md +1 -1
- data/RESOLVING_VIOLATIONS.md +81 -0
- data/Rakefile +1 -1
- data/USAGE.md +14 -5
- data/bin/m +1 -1
- data/bin/rake +1 -1
- data/bin/rubocop +1 -1
- data/bin/srb +1 -1
- data/bin/tapioca +1 -1
- data/gemfiles/Gemfile-rails-6-0 +1 -1
- data/gemfiles/Gemfile-rails-6-1 +22 -0
- data/lib/packwerk/application_load_paths.rb +1 -1
- data/lib/packwerk/application_validator.rb +7 -6
- data/lib/packwerk/association_inspector.rb +17 -15
- data/lib/packwerk/cache.rb +36 -29
- data/lib/packwerk/cli.rb +24 -20
- data/lib/packwerk/const_node_inspector.rb +8 -7
- data/lib/packwerk/constant_name_inspector.rb +2 -2
- data/lib/packwerk/deprecated_references.rb +40 -20
- data/lib/packwerk/file_processor.rb +14 -14
- data/lib/packwerk/files_for_processing.rb +27 -31
- data/lib/packwerk/formatters/offenses_formatter.rb +3 -3
- data/lib/packwerk/formatters/progress_formatter.rb +2 -2
- data/lib/packwerk/node.rb +1 -294
- data/lib/packwerk/node_helpers.rb +335 -0
- data/lib/packwerk/node_processor.rb +6 -5
- data/lib/packwerk/node_processor_factory.rb +3 -3
- data/lib/packwerk/node_visitor.rb +1 -1
- data/lib/packwerk/offense_collection.rb +27 -8
- data/lib/packwerk/offenses_formatter.rb +2 -2
- data/lib/packwerk/package.rb +3 -0
- data/lib/packwerk/package_set.rb +2 -0
- data/lib/packwerk/parse_run.rb +29 -20
- data/lib/packwerk/parsed_constant_definitions.rb +23 -20
- data/lib/packwerk/parsers/erb.rb +3 -3
- data/lib/packwerk/reference_checking/checkers/checker.rb +16 -3
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +16 -0
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +18 -0
- data/lib/packwerk/reference_checking/reference_checker.rb +3 -1
- data/lib/packwerk/reference_extractor.rb +51 -48
- data/lib/packwerk/reference_offense.rb +3 -27
- data/lib/packwerk/run_context.rb +9 -8
- data/lib/packwerk/spring_command.rb +1 -1
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +1 -0
- data/packwerk.gemspec +5 -12
- data/sorbet/rbi/gems/actioncable@7.0.3.1.rbi +2754 -0
- data/sorbet/rbi/gems/actionmailbox@7.0.3.1.rbi +1496 -0
- data/sorbet/rbi/gems/actionmailer@7.0.3.1.rbi +2362 -0
- data/sorbet/rbi/gems/actionpack@7.0.3.1.rbi +19397 -0
- data/sorbet/rbi/gems/actiontext@7.0.3.1.rbi +1569 -0
- data/sorbet/rbi/gems/actionview@7.0.3.1.rbi +14907 -0
- data/sorbet/rbi/gems/activejob@7.0.3.1.rbi +2553 -0
- data/sorbet/rbi/gems/activemodel@7.0.3.1.rbi +5999 -0
- data/sorbet/rbi/gems/activerecord@7.0.3.1.rbi +37832 -0
- data/sorbet/rbi/gems/activestorage@7.0.3.1.rbi +2321 -0
- data/sorbet/rbi/gems/activesupport@7.0.3.1.rbi +18818 -0
- data/sorbet/rbi/gems/concurrent-ruby@1.1.10.rbi +11722 -0
- data/sorbet/rbi/gems/constant_resolver@0.2.0.rbi +90 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1079 -0
- data/sorbet/rbi/gems/digest@3.1.0.rbi +189 -0
- data/sorbet/rbi/gems/erubi@1.11.0.rbi +140 -0
- data/sorbet/rbi/gems/globalid@1.0.0.rbi +572 -0
- data/sorbet/rbi/gems/i18n@1.12.0.rbi +2296 -0
- data/sorbet/rbi/gems/json@2.6.2.rbi +1548 -0
- data/sorbet/rbi/gems/language_server-protocol@3.16.0.3.rbi +8 -0
- data/sorbet/rbi/gems/loofah@2.18.0.rbi +877 -0
- data/sorbet/rbi/gems/m@1.6.0.rbi +257 -0
- data/sorbet/rbi/gems/marcel@1.0.2.rbi +220 -0
- data/sorbet/rbi/gems/mini_mime@1.1.2.rbi +170 -0
- data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +8 -0
- data/sorbet/rbi/gems/minitest-focus@1.3.1.rbi +104 -0
- data/sorbet/rbi/gems/minitest@5.16.2.rbi +2136 -0
- data/sorbet/rbi/gems/mocha@1.14.0.rbi +4177 -0
- data/sorbet/rbi/gems/net-imap@0.2.3.rbi +2147 -0
- data/sorbet/rbi/gems/net-pop@0.1.1.rbi +926 -0
- data/sorbet/rbi/gems/net-protocol@0.1.3.rbi +11 -0
- data/sorbet/rbi/gems/net-smtp@0.3.1.rbi +1108 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +153 -0
- data/sorbet/rbi/gems/nio4r@2.5.8.rbi +292 -0
- data/sorbet/rbi/gems/nokogiri@1.13.8.rbi +6478 -0
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
- data/sorbet/rbi/gems/parser@3.1.2.1.rbi +9029 -0
- data/sorbet/rbi/gems/prettier_print@0.1.0.rbi +8 -0
- data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
- data/sorbet/rbi/gems/racc@1.6.0.rbi +152 -0
- data/sorbet/rbi/gems/rack-test@2.0.2.rbi +953 -0
- data/sorbet/rbi/gems/rack@2.2.4.rbi +5636 -0
- data/sorbet/rbi/gems/rails-html-sanitizer@1.4.3.rbi +688 -0
- data/sorbet/rbi/gems/rails@7.0.3.1.rbi +8 -0
- data/sorbet/rbi/gems/railties@7.0.3.1.rbi +3507 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +392 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +2924 -0
- data/sorbet/rbi/gems/rbi@0.0.15.rbi +3007 -0
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +3383 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +4714 -0
- data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +6961 -0
- data/sorbet/rbi/gems/rubocop-performance@1.14.3.rbi +2986 -0
- data/sorbet/rbi/gems/{rubocop-shopify@2.0.1.rbi → rubocop-shopify@2.9.0.rbi} +4 -4
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.11.rbi +992 -0
- data/sorbet/rbi/gems/rubocop@1.34.1.rbi +51820 -0
- data/sorbet/rbi/gems/ruby-lsp@0.2.1.rbi +11 -0
- data/sorbet/rbi/gems/smart_properties@1.17.0.rbi +474 -0
- data/sorbet/rbi/gems/spoom@1.1.11.rbi +2181 -0
- data/sorbet/rbi/gems/spring@4.0.0.rbi +411 -0
- data/sorbet/rbi/gems/strscan@3.0.4.rbi +8 -0
- data/sorbet/rbi/gems/syntax_tree@3.3.0.rbi +8 -0
- data/sorbet/rbi/gems/tapioca@0.9.2.rbi +3181 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
- data/sorbet/rbi/gems/timeout@0.3.0.rbi +142 -0
- data/sorbet/rbi/gems/tzinfo@2.0.5.rbi +5896 -0
- data/sorbet/rbi/gems/unicode-display_width@2.2.0.rbi +48 -0
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +4529 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +2582 -0
- data/sorbet/rbi/gems/websocket-driver@0.7.5.rbi +993 -0
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +388 -0
- data/sorbet/rbi/gems/yard@0.9.28.rbi +18242 -0
- data/sorbet/rbi/gems/zeitwerk@2.6.0.rbi +867 -0
- data/sorbet/rbi/shims/psych.rbi +5 -0
- data/sorbet/tapioca/require.rb +2 -3
- metadata +91 -146
- data/.github/probots.yml +0 -2
- data/library.yml +0 -6
- data/service.yml +0 -1
- data/sorbet/rbi/gems/actioncable@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -860
- data/sorbet/rbi/gems/actionmailbox@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -568
- data/sorbet/rbi/gems/actionmailer@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -587
- data/sorbet/rbi/gems/actionpack@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -5314
- data/sorbet/rbi/gems/actiontext@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -699
- data/sorbet/rbi/gems/actionview@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -2515
- data/sorbet/rbi/gems/activejob@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -624
- data/sorbet/rbi/gems/activemodel@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -1248
- data/sorbet/rbi/gems/activerecord@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -8363
- data/sorbet/rbi/gems/activestorage@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -876
- data/sorbet/rbi/gems/activesupport@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -3987
- data/sorbet/rbi/gems/colorize@0.8.1.rbi +0 -40
- data/sorbet/rbi/gems/commander@4.5.2.rbi +0 -8
- data/sorbet/rbi/gems/concurrent-ruby@1.1.8.rbi +0 -1969
- data/sorbet/rbi/gems/constant_resolver@0.1.5.rbi +0 -26
- data/sorbet/rbi/gems/erubi@1.10.0.rbi +0 -41
- data/sorbet/rbi/gems/globalid@0.4.2.rbi +0 -178
- data/sorbet/rbi/gems/highline@2.0.3.rbi +0 -8
- data/sorbet/rbi/gems/i18n@1.8.10.rbi +0 -600
- data/sorbet/rbi/gems/loofah@2.9.0.rbi +0 -274
- data/sorbet/rbi/gems/m@1.5.1.rbi +0 -108
- data/sorbet/rbi/gems/marcel@1.0.0.rbi +0 -70
- data/sorbet/rbi/gems/mini_mime@1.0.3.rbi +0 -71
- data/sorbet/rbi/gems/minitest-focus@1.2.1.rbi +0 -8
- data/sorbet/rbi/gems/minitest@5.14.4.rbi +0 -544
- data/sorbet/rbi/gems/mocha@1.12.0.rbi +0 -953
- data/sorbet/rbi/gems/nio4r@2.5.7.rbi +0 -90
- data/sorbet/rbi/gems/nokogiri@1.11.2.rbi +0 -1647
- data/sorbet/rbi/gems/parallel@1.20.1.rbi +0 -117
- data/sorbet/rbi/gems/parlour@6.0.0.rbi +0 -1272
- data/sorbet/rbi/gems/parser@3.0.0.0.rbi +0 -1745
- data/sorbet/rbi/gems/pry@0.14.0.rbi +0 -8
- data/sorbet/rbi/gems/psych@3.3.2.rbi +0 -24
- data/sorbet/rbi/gems/racc@1.5.2.rbi +0 -57
- data/sorbet/rbi/gems/rack-test@1.1.0.rbi +0 -335
- data/sorbet/rbi/gems/rack@2.2.3.rbi +0 -1718
- data/sorbet/rbi/gems/rails-html-sanitizer@1.3.0.rbi +0 -213
- data/sorbet/rbi/gems/rails@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -8
- data/sorbet/rbi/gems/railties@7.0.0.alpha-d612542336d9a61381311c95a27d801bb4094779.rbi +0 -880
- data/sorbet/rbi/gems/rainbow@3.0.0.rbi +0 -155
- data/sorbet/rbi/gems/rake@13.0.3.rbi +0 -837
- data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +0 -8
- data/sorbet/rbi/gems/rexml@3.2.4.rbi +0 -8
- data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +0 -8
- data/sorbet/rbi/gems/rubocop-performance@1.10.2.rbi +0 -8
- data/sorbet/rbi/gems/rubocop-sorbet@0.6.1.rbi +0 -8
- data/sorbet/rbi/gems/rubocop@1.12.0.rbi +0 -8
- data/sorbet/rbi/gems/smart_properties@1.15.0.rbi +0 -168
- data/sorbet/rbi/gems/spoom@1.1.0.rbi +0 -1061
- data/sorbet/rbi/gems/spring@2.1.1.rbi +0 -160
- data/sorbet/rbi/gems/sprockets-rails@3.2.2.rbi +0 -451
- data/sorbet/rbi/gems/sprockets@4.0.2.rbi +0 -1133
- data/sorbet/rbi/gems/tapioca@0.4.19.rbi +0 -603
- data/sorbet/rbi/gems/thor@1.1.0.rbi +0 -893
- data/sorbet/rbi/gems/tzinfo@2.0.4.rbi +0 -566
- data/sorbet/rbi/gems/unicode-display_width@2.0.0.rbi +0 -8
- data/sorbet/rbi/gems/websocket-driver@0.7.3.rbi +0 -438
- data/sorbet/rbi/gems/zeitwerk@2.4.2.rbi +0 -177
|
@@ -7,7 +7,7 @@ module Packwerk
|
|
|
7
7
|
class DeprecatedReferences
|
|
8
8
|
extend T::Sig
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
EntriesType = T.type_alias do
|
|
11
11
|
T::Hash[String, T.untyped]
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -15,8 +15,8 @@ module Packwerk
|
|
|
15
15
|
def initialize(package, filepath)
|
|
16
16
|
@package = package
|
|
17
17
|
@filepath = filepath
|
|
18
|
-
@new_entries = T.let({},
|
|
19
|
-
@deprecated_references = T.let(nil, T.nilable(
|
|
18
|
+
@new_entries = T.let({}, EntriesType)
|
|
19
|
+
@deprecated_references = T.let(nil, T.nilable(EntriesType))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
sig do
|
|
@@ -36,28 +36,43 @@ module Packwerk
|
|
|
36
36
|
sig { params(reference: Packwerk::Reference, violation_type: Packwerk::ViolationType).returns(T::Boolean) }
|
|
37
37
|
def add_entries(reference, violation_type)
|
|
38
38
|
package_violations = @new_entries.fetch(reference.constant.package.name, {})
|
|
39
|
-
|
|
39
|
+
entries_for_constant = package_violations[reference.constant.name] ||= {}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
entries_for_constant["violations"] ||= []
|
|
42
|
+
entries_for_constant["violations"] << violation_type.serialize
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
entries_for_constant["files"] ||= []
|
|
45
|
+
entries_for_constant["files"] << reference.relative_path.to_s
|
|
46
46
|
|
|
47
47
|
@new_entries[reference.constant.package.name] = package_violations
|
|
48
48
|
listed?(reference, violation_type: violation_type)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
sig { returns(T::Boolean) }
|
|
52
|
-
def stale_violations?
|
|
51
|
+
sig { params(for_files: T::Set[String]).returns(T::Boolean) }
|
|
52
|
+
def stale_violations?(for_files)
|
|
53
53
|
prepare_entries_for_dump
|
|
54
|
+
|
|
54
55
|
deprecated_references.any? do |package, package_violations|
|
|
55
|
-
|
|
56
|
+
package_violations_for_files = {}
|
|
57
|
+
package_violations.each do |constant_name, entries_for_constant|
|
|
58
|
+
entries_for_files = for_files & entries_for_constant["files"]
|
|
59
|
+
next if entries_for_files.none?
|
|
60
|
+
|
|
61
|
+
package_violations_for_files[constant_name] = {
|
|
62
|
+
"violations" => entries_for_constant["violations"],
|
|
63
|
+
"files" => entries_for_files.to_a,
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
return true if package_violations_for_files.empty?
|
|
68
|
+
|
|
69
|
+
package_violations_for_files.any? do |constant_name, entries_for_constant|
|
|
56
70
|
new_entries_violation_types = @new_entries.dig(package, constant_name, "violations")
|
|
57
71
|
return true if new_entries_violation_types.nil?
|
|
58
|
-
|
|
72
|
+
|
|
73
|
+
if entries_for_constant["violations"].all? { |type| new_entries_violation_types.include?(type) }
|
|
59
74
|
stale_violations =
|
|
60
|
-
|
|
75
|
+
entries_for_constant["files"] - Array(@new_entries.dig(package, constant_name, "files"))
|
|
61
76
|
stale_violations.any?
|
|
62
77
|
else
|
|
63
78
|
return true
|
|
@@ -69,7 +84,7 @@ module Packwerk
|
|
|
69
84
|
sig { void }
|
|
70
85
|
def dump
|
|
71
86
|
if @new_entries.empty?
|
|
72
|
-
|
|
87
|
+
delete_if_exists
|
|
73
88
|
else
|
|
74
89
|
prepare_entries_for_dump
|
|
75
90
|
message = <<~MESSAGE
|
|
@@ -87,14 +102,19 @@ module Packwerk
|
|
|
87
102
|
end
|
|
88
103
|
end
|
|
89
104
|
|
|
105
|
+
sig { void }
|
|
106
|
+
def delete_if_exists
|
|
107
|
+
File.delete(@filepath) if File.exist?(@filepath)
|
|
108
|
+
end
|
|
109
|
+
|
|
90
110
|
private
|
|
91
111
|
|
|
92
|
-
sig { returns(
|
|
112
|
+
sig { returns(EntriesType) }
|
|
93
113
|
def prepare_entries_for_dump
|
|
94
114
|
@new_entries.each do |package_name, package_violations|
|
|
95
|
-
package_violations.each do |_,
|
|
96
|
-
|
|
97
|
-
|
|
115
|
+
package_violations.each do |_, entries_for_constant|
|
|
116
|
+
entries_for_constant["violations"].sort!.uniq!
|
|
117
|
+
entries_for_constant["files"].sort!.uniq!
|
|
98
118
|
end
|
|
99
119
|
@new_entries[package_name] = package_violations.sort.to_h
|
|
100
120
|
end
|
|
@@ -102,7 +122,7 @@ module Packwerk
|
|
|
102
122
|
@new_entries = @new_entries.sort.to_h
|
|
103
123
|
end
|
|
104
124
|
|
|
105
|
-
sig { returns(
|
|
125
|
+
sig { returns(EntriesType) }
|
|
106
126
|
def deprecated_references
|
|
107
127
|
@deprecated_references ||= if File.exist?(@filepath)
|
|
108
128
|
load_yaml(@filepath)
|
|
@@ -111,7 +131,7 @@ module Packwerk
|
|
|
111
131
|
end
|
|
112
132
|
end
|
|
113
133
|
|
|
114
|
-
sig { params(filepath: String).returns(
|
|
134
|
+
sig { params(filepath: String).returns(EntriesType) }
|
|
115
135
|
def load_yaml(filepath)
|
|
116
136
|
YAML.load_file(filepath) || {}
|
|
117
137
|
rescue Psych::Exception
|
|
@@ -35,19 +35,19 @@ module Packwerk
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
sig do
|
|
38
|
-
params(
|
|
38
|
+
params(relative_file: String).returns(ProcessedFile)
|
|
39
39
|
end
|
|
40
|
-
def call(
|
|
41
|
-
parser = parser_for(
|
|
40
|
+
def call(relative_file)
|
|
41
|
+
parser = parser_for(relative_file)
|
|
42
42
|
if parser.nil?
|
|
43
|
-
return ProcessedFile.new(offenses: [UnknownFileTypeResult.new(file:
|
|
43
|
+
return ProcessedFile.new(offenses: [UnknownFileTypeResult.new(file: relative_file)])
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
unresolved_references = @cache.with_cache(
|
|
47
|
-
node = parse_into_ast(
|
|
46
|
+
unresolved_references = @cache.with_cache(relative_file) do
|
|
47
|
+
node = parse_into_ast(relative_file, parser)
|
|
48
48
|
return ProcessedFile.new unless node
|
|
49
49
|
|
|
50
|
-
references_from_ast(node,
|
|
50
|
+
references_from_ast(node, relative_file)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
ProcessedFile.new(unresolved_references: unresolved_references)
|
|
@@ -58,22 +58,22 @@ module Packwerk
|
|
|
58
58
|
private
|
|
59
59
|
|
|
60
60
|
sig do
|
|
61
|
-
params(node: Parser::AST::Node,
|
|
61
|
+
params(node: Parser::AST::Node, relative_file: String).returns(T::Array[UnresolvedReference])
|
|
62
62
|
end
|
|
63
|
-
def references_from_ast(node,
|
|
63
|
+
def references_from_ast(node, relative_file)
|
|
64
64
|
references = []
|
|
65
65
|
|
|
66
|
-
node_processor = @node_processor_factory.for(
|
|
66
|
+
node_processor = @node_processor_factory.for(relative_file: relative_file, node: node)
|
|
67
67
|
node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
|
|
68
68
|
node_visitor.visit(node, ancestors: [], result: references)
|
|
69
69
|
|
|
70
70
|
references
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
sig { params(
|
|
74
|
-
def parse_into_ast(
|
|
75
|
-
File.open(
|
|
76
|
-
parser.call(io: file, file_path:
|
|
73
|
+
sig { params(relative_file: String, parser: Parsers::ParserInterface).returns(T.untyped) }
|
|
74
|
+
def parse_into_ast(relative_file, parser)
|
|
75
|
+
File.open(relative_file, "r", nil, external_encoding: Encoding::UTF_8) do |file|
|
|
76
|
+
parser.call(io: file, file_path: relative_file)
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
@@ -5,7 +5,7 @@ module Packwerk
|
|
|
5
5
|
class FilesForProcessing
|
|
6
6
|
extend T::Sig
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
RelativeFileSet = T.type_alias { T::Set[String] }
|
|
9
9
|
|
|
10
10
|
class << self
|
|
11
11
|
extend T::Sig
|
|
@@ -15,7 +15,7 @@ module Packwerk
|
|
|
15
15
|
relative_file_paths: T::Array[String],
|
|
16
16
|
configuration: Configuration,
|
|
17
17
|
ignore_nested_packages: T::Boolean
|
|
18
|
-
).returns(
|
|
18
|
+
).returns(RelativeFileSet)
|
|
19
19
|
end
|
|
20
20
|
def fetch(relative_file_paths:, configuration:, ignore_nested_packages: false)
|
|
21
21
|
new(relative_file_paths, configuration, ignore_nested_packages).files
|
|
@@ -33,15 +33,15 @@ module Packwerk
|
|
|
33
33
|
@relative_file_paths = relative_file_paths
|
|
34
34
|
@configuration = configuration
|
|
35
35
|
@ignore_nested_packages = ignore_nested_packages
|
|
36
|
-
@custom_files = T.let(nil, T.nilable(
|
|
36
|
+
@custom_files = T.let(nil, T.nilable(RelativeFileSet))
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
sig { returns(
|
|
39
|
+
sig { returns(RelativeFileSet) }
|
|
40
40
|
def files
|
|
41
41
|
include_files = if custom_files.empty?
|
|
42
42
|
configured_included_files
|
|
43
43
|
else
|
|
44
|
-
custom_files
|
|
44
|
+
configured_included_files & custom_files
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
include_files - configured_excluded_files
|
|
@@ -49,59 +49,55 @@ module Packwerk
|
|
|
49
49
|
|
|
50
50
|
private
|
|
51
51
|
|
|
52
|
-
sig { returns(
|
|
52
|
+
sig { returns(RelativeFileSet) }
|
|
53
53
|
def custom_files
|
|
54
54
|
@custom_files ||= Set.new(
|
|
55
55
|
@relative_file_paths.map do |relative_file_path|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
absolute_file_path
|
|
56
|
+
if File.file?(relative_file_path)
|
|
57
|
+
relative_file_path
|
|
59
58
|
else
|
|
60
|
-
custom_included_files(
|
|
59
|
+
custom_included_files(relative_file_path)
|
|
61
60
|
end
|
|
62
61
|
end
|
|
63
62
|
).flatten
|
|
64
63
|
end
|
|
65
64
|
|
|
66
|
-
sig { params(
|
|
67
|
-
def custom_included_files(
|
|
65
|
+
sig { params(relative_file_path: String).returns(RelativeFileSet) }
|
|
66
|
+
def custom_included_files(relative_file_path)
|
|
68
67
|
# Note, assuming include globs are always relative paths
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
absolute_files = Dir.glob([File.join(absolute_file_path, "**", "*")]).select do |absolute_path|
|
|
74
|
-
absolute_includes.any? do |pattern|
|
|
75
|
-
File.fnmatch?(pattern, absolute_path, File::FNM_EXTGLOB)
|
|
68
|
+
relative_includes = @configuration.include
|
|
69
|
+
relative_files = Dir.glob([File.join(relative_file_path, "**", "*")]).select do |relative_path|
|
|
70
|
+
relative_includes.any? do |pattern|
|
|
71
|
+
File.fnmatch?(pattern, relative_path, File::FNM_EXTGLOB)
|
|
76
72
|
end
|
|
77
73
|
end
|
|
78
74
|
|
|
79
75
|
if @ignore_nested_packages
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
nested_packages_relative_file_paths = Dir.glob(File.join(relative_file_path, "*", "**", "package.yml"))
|
|
77
|
+
nested_packages_relative_globs = nested_packages_relative_file_paths.map do |npp|
|
|
82
78
|
npp.gsub("package.yml", "**/*")
|
|
83
79
|
end
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
nested_packages_relative_globs.each do |relative_glob|
|
|
81
|
+
relative_files -= Dir.glob(relative_glob)
|
|
86
82
|
end
|
|
87
83
|
end
|
|
88
84
|
|
|
89
|
-
Set.new(
|
|
85
|
+
Set.new(relative_files)
|
|
90
86
|
end
|
|
91
87
|
|
|
92
|
-
sig { returns(
|
|
88
|
+
sig { returns(RelativeFileSet) }
|
|
93
89
|
def configured_included_files
|
|
94
|
-
|
|
90
|
+
relative_files_for_globs(@configuration.include)
|
|
95
91
|
end
|
|
96
92
|
|
|
97
|
-
sig { returns(
|
|
93
|
+
sig { returns(RelativeFileSet) }
|
|
98
94
|
def configured_excluded_files
|
|
99
|
-
|
|
95
|
+
relative_files_for_globs(@configuration.exclude)
|
|
100
96
|
end
|
|
101
97
|
|
|
102
|
-
sig { params(relative_globs: T::Array[String]).returns(
|
|
103
|
-
def
|
|
104
|
-
Set.new(relative_globs.flat_map { |glob| Dir[
|
|
98
|
+
sig { params(relative_globs: T::Array[String]).returns(RelativeFileSet) }
|
|
99
|
+
def relative_files_for_globs(relative_globs)
|
|
100
|
+
Set.new(relative_globs.flat_map { |glob| Dir[glob] })
|
|
105
101
|
end
|
|
106
102
|
end
|
|
107
103
|
end
|
|
@@ -23,9 +23,9 @@ module Packwerk
|
|
|
23
23
|
EOS
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
sig { override.params(offense_collection: Packwerk::OffenseCollection).returns(String) }
|
|
27
|
-
def show_stale_violations(offense_collection)
|
|
28
|
-
if offense_collection.stale_violations?
|
|
26
|
+
sig { override.params(offense_collection: Packwerk::OffenseCollection, fileset: T::Set[String]).returns(String) }
|
|
27
|
+
def show_stale_violations(offense_collection, fileset)
|
|
28
|
+
if offense_collection.stale_violations?(fileset)
|
|
29
29
|
"There were stale violations found, please run `packwerk update-deprecations`"
|
|
30
30
|
else
|
|
31
31
|
"No stale violations detected"
|
|
@@ -20,10 +20,10 @@ module Packwerk
|
|
|
20
20
|
@out.puts("📦 Packwerk is inspecting #{files_size} #{files_string}")
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def started_validation
|
|
23
|
+
def started_validation(&block)
|
|
24
24
|
@out.puts("📦 Packwerk is running validation...")
|
|
25
25
|
|
|
26
|
-
execution_time = Benchmark.realtime
|
|
26
|
+
execution_time = Benchmark.realtime(&block)
|
|
27
27
|
finished(execution_time)
|
|
28
28
|
|
|
29
29
|
@out.puts("✅ Packages are valid. Use `packwerk check` to run static checks.")
|
data/lib/packwerk/node.rb
CHANGED
|
@@ -1,301 +1,8 @@
|
|
|
1
1
|
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require "parser"
|
|
5
|
-
require "parser/ast/node"
|
|
6
|
-
|
|
7
4
|
module Packwerk
|
|
8
|
-
|
|
9
|
-
module Node
|
|
10
|
-
class TypeError < ArgumentError; end
|
|
5
|
+
class Node
|
|
11
6
|
Location = Struct.new(:line, :column)
|
|
12
|
-
|
|
13
|
-
class << self
|
|
14
|
-
extend T::Sig
|
|
15
|
-
|
|
16
|
-
def class_or_module_name(class_or_module_node)
|
|
17
|
-
case type_of(class_or_module_node)
|
|
18
|
-
when CLASS, MODULE
|
|
19
|
-
# (class (const nil :Foo) (const nil :Bar) (nil))
|
|
20
|
-
# "class Foo < Bar; end"
|
|
21
|
-
# (module (const nil :Foo) (nil))
|
|
22
|
-
# "module Foo; end"
|
|
23
|
-
identifier = class_or_module_node.children[0]
|
|
24
|
-
constant_name(identifier)
|
|
25
|
-
else
|
|
26
|
-
raise TypeError
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def constant_name(constant_node)
|
|
31
|
-
case type_of(constant_node)
|
|
32
|
-
when CONSTANT_ROOT_NAMESPACE
|
|
33
|
-
""
|
|
34
|
-
when CONSTANT, CONSTANT_ASSIGNMENT, SELF
|
|
35
|
-
# (const nil :Foo)
|
|
36
|
-
# "Foo"
|
|
37
|
-
# (const (cbase) :Foo)
|
|
38
|
-
# "::Foo"
|
|
39
|
-
# (const (lvar :a) :Foo)
|
|
40
|
-
# "a::Foo"
|
|
41
|
-
# (casgn nil :Foo (int 1))
|
|
42
|
-
# "Foo = 1"
|
|
43
|
-
# (casgn (cbase) :Foo (int 1))
|
|
44
|
-
# "::Foo = 1"
|
|
45
|
-
# (casgn (lvar :a) :Foo (int 1))
|
|
46
|
-
# "a::Foo = 1"
|
|
47
|
-
# (casgn (self) :Foo (int 1))
|
|
48
|
-
# "self::Foo = 1"
|
|
49
|
-
namespace, name = constant_node.children
|
|
50
|
-
if namespace
|
|
51
|
-
[constant_name(namespace), name].join("::")
|
|
52
|
-
else
|
|
53
|
-
name.to_s
|
|
54
|
-
end
|
|
55
|
-
else
|
|
56
|
-
raise TypeError
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def each_child(node)
|
|
61
|
-
if block_given?
|
|
62
|
-
node.children.each do |child|
|
|
63
|
-
yield child if child.is_a?(Parser::AST::Node)
|
|
64
|
-
end
|
|
65
|
-
else
|
|
66
|
-
enum_for(:each_child, node)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def enclosing_namespace_path(starting_node, ancestors:)
|
|
71
|
-
ancestors.select { |n| [CLASS, MODULE].include?(type_of(n)) }
|
|
72
|
-
.each_with_object([]) do |node, namespace|
|
|
73
|
-
# when evaluating `class Child < Parent`, the const node for `Parent` is a child of the class
|
|
74
|
-
# node, so it'll be an ancestor, but `Parent` is not evaluated in the namespace of `Child`, so
|
|
75
|
-
# we need to skip it here
|
|
76
|
-
next if type_of(node) == CLASS && parent_class(node) == starting_node
|
|
77
|
-
|
|
78
|
-
namespace.prepend(class_or_module_name(node))
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def literal_value(string_or_symbol_node)
|
|
83
|
-
case type_of(string_or_symbol_node)
|
|
84
|
-
when STRING, SYMBOL
|
|
85
|
-
# (str "foo")
|
|
86
|
-
# "'foo'"
|
|
87
|
-
# (sym :foo)
|
|
88
|
-
# ":foo"
|
|
89
|
-
string_or_symbol_node.children[0]
|
|
90
|
-
else
|
|
91
|
-
raise TypeError
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def location(node)
|
|
96
|
-
location = node.location
|
|
97
|
-
Location.new(location.line, location.column)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def constant?(node)
|
|
101
|
-
type_of(node) == CONSTANT
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def constant_assignment?(node)
|
|
105
|
-
type_of(node) == CONSTANT_ASSIGNMENT
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def class?(node)
|
|
109
|
-
type_of(node) == CLASS
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def method_call?(node)
|
|
113
|
-
type_of(node) == METHOD_CALL
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def hash?(node)
|
|
117
|
-
type_of(node) == HASH
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def string?(node)
|
|
121
|
-
type_of(node) == STRING
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def symbol?(node)
|
|
125
|
-
type_of(node) == SYMBOL
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def method_arguments(method_call_node)
|
|
129
|
-
raise TypeError unless method_call?(method_call_node)
|
|
130
|
-
|
|
131
|
-
# (send (lvar :foo) :bar (int 1))
|
|
132
|
-
# "foo.bar(1)"
|
|
133
|
-
method_call_node.children.slice(2..-1)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def method_name(method_call_node)
|
|
137
|
-
raise TypeError unless method_call?(method_call_node)
|
|
138
|
-
|
|
139
|
-
# (send (lvar :foo) :bar (int 1))
|
|
140
|
-
# "foo.bar(1)"
|
|
141
|
-
method_call_node.children[1]
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def module_name_from_definition(node)
|
|
145
|
-
case type_of(node)
|
|
146
|
-
when CLASS, MODULE
|
|
147
|
-
# "class My::Class; end"
|
|
148
|
-
# "module My::Module; end"
|
|
149
|
-
class_or_module_name(node)
|
|
150
|
-
when CONSTANT_ASSIGNMENT
|
|
151
|
-
# "My::Class = ..."
|
|
152
|
-
# "My::Module = ..."
|
|
153
|
-
rvalue = node.children.last
|
|
154
|
-
|
|
155
|
-
case type_of(rvalue)
|
|
156
|
-
when METHOD_CALL
|
|
157
|
-
# "Class.new"
|
|
158
|
-
# "Module.new"
|
|
159
|
-
constant_name(node) if module_creation?(rvalue)
|
|
160
|
-
when BLOCK
|
|
161
|
-
# "Class.new do end"
|
|
162
|
-
# "Module.new do end"
|
|
163
|
-
constant_name(node) if module_creation?(method_call_node(rvalue))
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def name_location(node)
|
|
169
|
-
location = node.location
|
|
170
|
-
|
|
171
|
-
if location.respond_to?(:name)
|
|
172
|
-
name = location.name
|
|
173
|
-
Location.new(name.line, name.column)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def parent_class(class_node)
|
|
178
|
-
raise TypeError unless type_of(class_node) == CLASS
|
|
179
|
-
|
|
180
|
-
# (class (const nil :Foo) (const nil :Bar) (nil))
|
|
181
|
-
# "class Foo < Bar; end"
|
|
182
|
-
class_node.children[1]
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
sig { params(ancestors: T::Array[AST::Node]).returns(String) }
|
|
186
|
-
def parent_module_name(ancestors:)
|
|
187
|
-
definitions = ancestors
|
|
188
|
-
.select { |n| [CLASS, MODULE, CONSTANT_ASSIGNMENT, BLOCK].include?(type_of(n)) }
|
|
189
|
-
|
|
190
|
-
names = definitions.map do |definition|
|
|
191
|
-
name_part_from_definition(definition)
|
|
192
|
-
end.compact
|
|
193
|
-
|
|
194
|
-
names.empty? ? "Object" : names.reverse.join("::")
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def value_from_hash(hash_node, key)
|
|
198
|
-
raise TypeError unless hash?(hash_node)
|
|
199
|
-
pair = hash_pairs(hash_node).detect { |pair_node| literal_value(hash_pair_key(pair_node)) == key }
|
|
200
|
-
hash_pair_value(pair) if pair
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
private
|
|
204
|
-
|
|
205
|
-
BLOCK = :block
|
|
206
|
-
CLASS = :class
|
|
207
|
-
CONSTANT = :const
|
|
208
|
-
CONSTANT_ASSIGNMENT = :casgn
|
|
209
|
-
CONSTANT_ROOT_NAMESPACE = :cbase
|
|
210
|
-
HASH = :hash
|
|
211
|
-
HASH_PAIR = :pair
|
|
212
|
-
METHOD_CALL = :send
|
|
213
|
-
MODULE = :module
|
|
214
|
-
SELF = :self
|
|
215
|
-
STRING = :str
|
|
216
|
-
SYMBOL = :sym
|
|
217
|
-
|
|
218
|
-
private_constant(
|
|
219
|
-
:BLOCK, :CLASS, :CONSTANT, :CONSTANT_ASSIGNMENT, :CONSTANT_ROOT_NAMESPACE, :HASH, :HASH_PAIR, :METHOD_CALL,
|
|
220
|
-
:MODULE, :SELF, :STRING, :SYMBOL,
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
def type_of(node)
|
|
224
|
-
node.type
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def hash_pair_key(hash_pair_node)
|
|
228
|
-
raise TypeError unless type_of(hash_pair_node) == HASH_PAIR
|
|
229
|
-
|
|
230
|
-
# (pair (int 1) (int 2))
|
|
231
|
-
# "1 => 2"
|
|
232
|
-
# (pair (sym :answer) (int 42))
|
|
233
|
-
# "answer: 42"
|
|
234
|
-
hash_pair_node.children[0]
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def hash_pair_value(hash_pair_node)
|
|
238
|
-
raise TypeError unless type_of(hash_pair_node) == HASH_PAIR
|
|
239
|
-
|
|
240
|
-
# (pair (int 1) (int 2))
|
|
241
|
-
# "1 => 2"
|
|
242
|
-
# (pair (sym :answer) (int 42))
|
|
243
|
-
# "answer: 42"
|
|
244
|
-
hash_pair_node.children[1]
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def hash_pairs(hash_node)
|
|
248
|
-
raise TypeError unless hash?(hash_node)
|
|
249
|
-
|
|
250
|
-
# (hash (pair (int 1) (int 2)) (pair (int 3) (int 4)))
|
|
251
|
-
# "{1 => 2, 3 => 4}"
|
|
252
|
-
hash_node.children.select { |n| type_of(n) == HASH_PAIR }
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
def method_call_node(block_node)
|
|
256
|
-
raise TypeError unless type_of(block_node) == BLOCK
|
|
257
|
-
|
|
258
|
-
# (block (send (lvar :foo) :bar) (args) (int 42))
|
|
259
|
-
# "foo.bar do 42 end"
|
|
260
|
-
block_node.children[0]
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def module_creation?(node)
|
|
264
|
-
# "Class.new"
|
|
265
|
-
# "Module.new"
|
|
266
|
-
method_call?(node) &&
|
|
267
|
-
receiver(node) &&
|
|
268
|
-
constant?(receiver(node)) &&
|
|
269
|
-
["Class", "Module"].include?(constant_name(receiver(node))) &&
|
|
270
|
-
method_name(node) == :new
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
def name_from_block_definition(node)
|
|
274
|
-
if method_name(method_call_node(node)) == :class_eval
|
|
275
|
-
receiver = receiver(node)
|
|
276
|
-
constant_name(receiver) if receiver && constant?(receiver)
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def name_part_from_definition(node)
|
|
281
|
-
case type_of(node)
|
|
282
|
-
when CLASS, MODULE, CONSTANT_ASSIGNMENT
|
|
283
|
-
module_name_from_definition(node)
|
|
284
|
-
when BLOCK
|
|
285
|
-
name_from_block_definition(node)
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def receiver(method_call_or_block_node)
|
|
290
|
-
case type_of(method_call_or_block_node)
|
|
291
|
-
when METHOD_CALL
|
|
292
|
-
method_call_or_block_node.children[0]
|
|
293
|
-
when BLOCK
|
|
294
|
-
receiver(method_call_node(method_call_or_block_node))
|
|
295
|
-
else
|
|
296
|
-
raise TypeError
|
|
297
|
-
end
|
|
298
|
-
end
|
|
299
|
-
end
|
|
300
7
|
end
|
|
301
8
|
end
|