packwerk 2.2.0 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|