packwerk 1.0.1 → 1.1.3
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/pull_request_template.md +8 -7
- data/.github/workflows/ci.yml +14 -5
- data/.ruby-version +1 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +130 -110
- data/README.md +8 -1
- data/USAGE.md +23 -2
- data/dev.yml +1 -1
- data/exe/packwerk +1 -1
- data/gemfiles/Gemfile-rails-6-0 +22 -0
- data/lib/packwerk.rb +4 -2
- data/lib/packwerk/application_load_paths.rb +68 -0
- data/lib/packwerk/application_validator.rb +94 -74
- data/lib/packwerk/association_inspector.rb +24 -9
- data/lib/packwerk/cache_deprecated_references.rb +55 -0
- data/lib/packwerk/checker.rb +3 -0
- data/lib/packwerk/checking_deprecated_references.rb +5 -2
- data/lib/packwerk/cli.rb +56 -55
- data/lib/packwerk/commands/detect_stale_violations_command.rb +60 -0
- data/lib/packwerk/commands/offense_progress_marker.rb +24 -0
- data/lib/packwerk/commands/result.rb +13 -0
- data/lib/packwerk/commands/update_deprecations_command.rb +81 -0
- data/lib/packwerk/configuration.rb +5 -37
- data/lib/packwerk/const_node_inspector.rb +28 -17
- data/lib/packwerk/dependency_checker.rb +13 -5
- data/lib/packwerk/deprecated_references.rb +23 -0
- data/lib/packwerk/detect_stale_deprecated_references.rb +14 -0
- data/lib/packwerk/file_processor.rb +4 -4
- data/lib/packwerk/formatters/offenses_formatter.rb +48 -0
- data/lib/packwerk/formatters/progress_formatter.rb +6 -2
- data/lib/packwerk/inflector.rb +17 -8
- data/lib/packwerk/node.rb +61 -38
- data/lib/packwerk/node_processor.rb +4 -4
- data/lib/packwerk/node_processor_factory.rb +39 -0
- data/lib/packwerk/node_visitor.rb +1 -1
- data/lib/packwerk/offense.rb +4 -6
- 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_set.rb +9 -3
- data/lib/packwerk/parsed_constant_definitions.rb +4 -4
- data/lib/packwerk/parsers/erb.rb +4 -0
- data/lib/packwerk/parsers/factory.rb +10 -1
- data/lib/packwerk/privacy_checker.rb +23 -5
- data/lib/packwerk/run_context.rb +69 -46
- data/lib/packwerk/sanity_checker.rb +1 -1
- data/lib/packwerk/spring_command.rb +1 -1
- data/lib/packwerk/updating_deprecated_references.rb +2 -39
- data/lib/packwerk/version.rb +1 -1
- data/library.yml +1 -1
- data/packwerk.gemspec +1 -1
- data/service.yml +2 -2
- 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.19.1.rbi → parallel@1.20.1.rbi} +1 -1
- 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/regexp_parser@2.1.1.rbi +8 -0
- 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
- metadata +66 -58
- data/lib/packwerk/output_styles.rb +0 -41
- 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
- data/static/packwerk-check-demo.png +0 -0
- data/static/packwerk_check.gif +0 -0
- data/static/packwerk_check_violation.gif +0 -0
- data/static/packwerk_update.gif +0 -0
- data/static/packwerk_validate.gif +0 -0
data/lib/packwerk/node.rb
CHANGED
@@ -5,24 +5,14 @@ require "parser/ast/node"
|
|
5
5
|
|
6
6
|
module Packwerk
|
7
7
|
module Node
|
8
|
-
BLOCK = :block
|
9
|
-
CLASS = :class
|
10
|
-
CONSTANT = :const
|
11
|
-
CONSTANT_ASSIGNMENT = :casgn
|
12
|
-
CONSTANT_ROOT_NAMESPACE = :cbase
|
13
|
-
HASH = :hash
|
14
|
-
HASH_PAIR = :pair
|
15
|
-
METHOD_CALL = :send
|
16
|
-
MODULE = :module
|
17
|
-
STRING = :str
|
18
|
-
SYMBOL = :sym
|
19
|
-
|
20
8
|
class TypeError < ArgumentError; end
|
21
9
|
Location = Struct.new(:line, :column)
|
22
10
|
|
23
11
|
class << self
|
12
|
+
extend T::Sig
|
13
|
+
|
24
14
|
def class_or_module_name(class_or_module_node)
|
25
|
-
case
|
15
|
+
case type_of(class_or_module_node)
|
26
16
|
when CLASS, MODULE
|
27
17
|
# (class (const nil :Foo) (const nil :Bar) (nil))
|
28
18
|
# "class Foo < Bar; end"
|
@@ -36,10 +26,10 @@ module Packwerk
|
|
36
26
|
end
|
37
27
|
|
38
28
|
def constant_name(constant_node)
|
39
|
-
case
|
29
|
+
case type_of(constant_node)
|
40
30
|
when CONSTANT_ROOT_NAMESPACE
|
41
31
|
""
|
42
|
-
when CONSTANT, CONSTANT_ASSIGNMENT
|
32
|
+
when CONSTANT, CONSTANT_ASSIGNMENT, SELF
|
43
33
|
# (const nil :Foo)
|
44
34
|
# "Foo"
|
45
35
|
# (const (cbase) :Foo)
|
@@ -52,6 +42,8 @@ module Packwerk
|
|
52
42
|
# "::Foo = 1"
|
53
43
|
# (casgn (lvar :a) :Foo (int 1))
|
54
44
|
# "a::Foo = 1"
|
45
|
+
# (casgn (self) :Foo (int 1))
|
46
|
+
# "self::Foo = 1"
|
55
47
|
namespace, name = constant_node.children
|
56
48
|
if namespace
|
57
49
|
[constant_name(namespace), name].join("::")
|
@@ -74,19 +66,19 @@ module Packwerk
|
|
74
66
|
end
|
75
67
|
|
76
68
|
def enclosing_namespace_path(starting_node, ancestors:)
|
77
|
-
ancestors.select { |n| [CLASS, MODULE].include?(
|
69
|
+
ancestors.select { |n| [CLASS, MODULE].include?(type_of(n)) }
|
78
70
|
.each_with_object([]) do |node, namespace|
|
79
71
|
# when evaluating `class Child < Parent`, the const node for `Parent` is a child of the class
|
80
72
|
# node, so it'll be an ancestor, but `Parent` is not evaluated in the namespace of `Child`, so
|
81
73
|
# we need to skip it here
|
82
|
-
next if
|
74
|
+
next if type_of(node) == CLASS && parent_class(node) == starting_node
|
83
75
|
|
84
76
|
namespace.prepend(class_or_module_name(node))
|
85
77
|
end
|
86
78
|
end
|
87
79
|
|
88
80
|
def literal_value(string_or_symbol_node)
|
89
|
-
case
|
81
|
+
case type_of(string_or_symbol_node)
|
90
82
|
when STRING, SYMBOL
|
91
83
|
# (str "foo")
|
92
84
|
# "'foo'"
|
@@ -103,20 +95,32 @@ module Packwerk
|
|
103
95
|
Location.new(location.line, location.column)
|
104
96
|
end
|
105
97
|
|
98
|
+
def constant?(node)
|
99
|
+
type_of(node) == CONSTANT
|
100
|
+
end
|
101
|
+
|
102
|
+
def constant_assignment?(node)
|
103
|
+
type_of(node) == CONSTANT_ASSIGNMENT
|
104
|
+
end
|
105
|
+
|
106
|
+
def class?(node)
|
107
|
+
type_of(node) == CLASS
|
108
|
+
end
|
109
|
+
|
106
110
|
def method_call?(node)
|
107
|
-
|
111
|
+
type_of(node) == METHOD_CALL
|
108
112
|
end
|
109
113
|
|
110
114
|
def hash?(node)
|
111
|
-
|
115
|
+
type_of(node) == HASH
|
112
116
|
end
|
113
117
|
|
114
118
|
def string?(node)
|
115
|
-
|
119
|
+
type_of(node) == STRING
|
116
120
|
end
|
117
121
|
|
118
122
|
def symbol?(node)
|
119
|
-
|
123
|
+
type_of(node) == SYMBOL
|
120
124
|
end
|
121
125
|
|
122
126
|
def method_arguments(method_call_node)
|
@@ -136,7 +140,7 @@ module Packwerk
|
|
136
140
|
end
|
137
141
|
|
138
142
|
def module_name_from_definition(node)
|
139
|
-
case
|
143
|
+
case type_of(node)
|
140
144
|
when CLASS, MODULE
|
141
145
|
# "class My::Class; end"
|
142
146
|
# "module My::Module; end"
|
@@ -146,7 +150,7 @@ module Packwerk
|
|
146
150
|
# "My::Module = ..."
|
147
151
|
rvalue = node.children.last
|
148
152
|
|
149
|
-
case
|
153
|
+
case type_of(rvalue)
|
150
154
|
when METHOD_CALL
|
151
155
|
# "Class.new"
|
152
156
|
# "Module.new"
|
@@ -169,16 +173,17 @@ module Packwerk
|
|
169
173
|
end
|
170
174
|
|
171
175
|
def parent_class(class_node)
|
172
|
-
raise TypeError unless
|
176
|
+
raise TypeError unless type_of(class_node) == CLASS
|
173
177
|
|
174
178
|
# (class (const nil :Foo) (const nil :Bar) (nil))
|
175
179
|
# "class Foo < Bar; end"
|
176
180
|
class_node.children[1]
|
177
181
|
end
|
178
182
|
|
183
|
+
sig { params(ancestors: T::Array[AST::Node]).returns(String) }
|
179
184
|
def parent_module_name(ancestors:)
|
180
185
|
definitions = ancestors
|
181
|
-
.select { |n| [CLASS, MODULE, CONSTANT_ASSIGNMENT, BLOCK].include?(
|
186
|
+
.select { |n| [CLASS, MODULE, CONSTANT_ASSIGNMENT, BLOCK].include?(type_of(n)) }
|
182
187
|
|
183
188
|
names = definitions.map do |definition|
|
184
189
|
name_part_from_definition(definition)
|
@@ -187,10 +192,6 @@ module Packwerk
|
|
187
192
|
names.empty? ? "Object" : names.reverse.join("::")
|
188
193
|
end
|
189
194
|
|
190
|
-
def type(node)
|
191
|
-
node.type
|
192
|
-
end
|
193
|
-
|
194
195
|
def value_from_hash(hash_node, key)
|
195
196
|
raise TypeError unless hash?(hash_node)
|
196
197
|
pair = hash_pairs(hash_node).detect { |pair_node| literal_value(hash_pair_key(pair_node)) == key }
|
@@ -199,8 +200,30 @@ module Packwerk
|
|
199
200
|
|
200
201
|
private
|
201
202
|
|
203
|
+
BLOCK = :block
|
204
|
+
CLASS = :class
|
205
|
+
CONSTANT = :const
|
206
|
+
CONSTANT_ASSIGNMENT = :casgn
|
207
|
+
CONSTANT_ROOT_NAMESPACE = :cbase
|
208
|
+
HASH = :hash
|
209
|
+
HASH_PAIR = :pair
|
210
|
+
METHOD_CALL = :send
|
211
|
+
MODULE = :module
|
212
|
+
SELF = :self
|
213
|
+
STRING = :str
|
214
|
+
SYMBOL = :sym
|
215
|
+
|
216
|
+
private_constant(
|
217
|
+
:BLOCK, :CLASS, :CONSTANT, :CONSTANT_ASSIGNMENT, :CONSTANT_ROOT_NAMESPACE, :HASH, :HASH_PAIR, :METHOD_CALL,
|
218
|
+
:MODULE, :SELF, :STRING, :SYMBOL,
|
219
|
+
)
|
220
|
+
|
221
|
+
def type_of(node)
|
222
|
+
node.type
|
223
|
+
end
|
224
|
+
|
202
225
|
def hash_pair_key(hash_pair_node)
|
203
|
-
raise TypeError unless
|
226
|
+
raise TypeError unless type_of(hash_pair_node) == HASH_PAIR
|
204
227
|
|
205
228
|
# (pair (int 1) (int 2))
|
206
229
|
# "1 => 2"
|
@@ -210,7 +233,7 @@ module Packwerk
|
|
210
233
|
end
|
211
234
|
|
212
235
|
def hash_pair_value(hash_pair_node)
|
213
|
-
raise TypeError unless
|
236
|
+
raise TypeError unless type_of(hash_pair_node) == HASH_PAIR
|
214
237
|
|
215
238
|
# (pair (int 1) (int 2))
|
216
239
|
# "1 => 2"
|
@@ -224,11 +247,11 @@ module Packwerk
|
|
224
247
|
|
225
248
|
# (hash (pair (int 1) (int 2)) (pair (int 3) (int 4)))
|
226
249
|
# "{1 => 2, 3 => 4}"
|
227
|
-
hash_node.children.select { |n|
|
250
|
+
hash_node.children.select { |n| type_of(n) == HASH_PAIR }
|
228
251
|
end
|
229
252
|
|
230
253
|
def method_call_node(block_node)
|
231
|
-
raise TypeError unless
|
254
|
+
raise TypeError unless type_of(block_node) == BLOCK
|
232
255
|
|
233
256
|
# (block (send (lvar :foo) :bar) (args) (int 42))
|
234
257
|
# "foo.bar do 42 end"
|
@@ -239,7 +262,7 @@ module Packwerk
|
|
239
262
|
# "Class.new"
|
240
263
|
# "Module.new"
|
241
264
|
method_call?(node) &&
|
242
|
-
|
265
|
+
constant?(receiver(node)) &&
|
243
266
|
["Class", "Module"].include?(constant_name(receiver(node))) &&
|
244
267
|
method_name(node) == :new
|
245
268
|
end
|
@@ -247,12 +270,12 @@ module Packwerk
|
|
247
270
|
def name_from_block_definition(node)
|
248
271
|
if method_name(method_call_node(node)) == :class_eval
|
249
272
|
receiver = receiver(node)
|
250
|
-
constant_name(receiver) if receiver &&
|
273
|
+
constant_name(receiver) if receiver && constant?(receiver)
|
251
274
|
end
|
252
275
|
end
|
253
276
|
|
254
277
|
def name_part_from_definition(node)
|
255
|
-
case
|
278
|
+
case type_of(node)
|
256
279
|
when CLASS, MODULE, CONSTANT_ASSIGNMENT
|
257
280
|
module_name_from_definition(node)
|
258
281
|
when BLOCK
|
@@ -261,7 +284,7 @@ module Packwerk
|
|
261
284
|
end
|
262
285
|
|
263
286
|
def receiver(method_call_or_block_node)
|
264
|
-
case
|
287
|
+
case type_of(method_call_or_block_node)
|
265
288
|
when METHOD_CALL
|
266
289
|
method_call_or_block_node.children[0]
|
267
290
|
when BLOCK
|
@@ -25,9 +25,9 @@ module Packwerk
|
|
25
25
|
@checkers = checkers
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
sig { params(node: Parser::AST::Node, ancestors: T::Array[Parser::AST::Node]).returns(T.nilable(Offense)) }
|
29
|
+
def call(node, ancestors)
|
30
|
+
if Node.method_call?(node) || Node.constant?(node)
|
31
31
|
reference = @reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
|
32
32
|
check_reference(reference, node) if reference
|
33
33
|
end
|
@@ -42,7 +42,7 @@ module Packwerk
|
|
42
42
|
|
43
43
|
Packwerk::Offense.new(
|
44
44
|
location: Node.location(node),
|
45
|
-
file:
|
45
|
+
file: reference.relative_path,
|
46
46
|
message: <<~EOS
|
47
47
|
#{message}
|
48
48
|
Inference details: this is a reference to #{constant.name} which seems to be defined in #{constant.location}.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "packwerk/constant_name_inspector"
|
5
|
+
require "packwerk/checker"
|
6
|
+
|
7
|
+
module Packwerk
|
8
|
+
class NodeProcessorFactory < T::Struct
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
const :root_path, String
|
12
|
+
const :reference_lister, ReferenceLister
|
13
|
+
const :context_provider, Packwerk::ConstantDiscovery
|
14
|
+
const :constant_name_inspectors, T::Array[ConstantNameInspector]
|
15
|
+
const :checkers, T::Array[Checker]
|
16
|
+
|
17
|
+
sig { params(filename: String, node: AST::Node).returns(NodeProcessor) }
|
18
|
+
def for(filename:, node:)
|
19
|
+
::Packwerk::NodeProcessor.new(
|
20
|
+
reference_extractor: reference_extractor(node: node),
|
21
|
+
reference_lister: reference_lister,
|
22
|
+
filename: filename,
|
23
|
+
checkers: checkers,
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
sig { params(node: AST::Node).returns(::Packwerk::ReferenceExtractor) }
|
30
|
+
def reference_extractor(node:)
|
31
|
+
::Packwerk::ReferenceExtractor.new(
|
32
|
+
context_provider: context_provider,
|
33
|
+
constant_name_inspectors: constant_name_inspectors,
|
34
|
+
root_node: node,
|
35
|
+
root_path: root_path,
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/packwerk/offense.rb
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
require "parser/source/map"
|
5
5
|
require "sorbet-runtime"
|
6
6
|
|
7
|
-
require "packwerk/
|
7
|
+
require "packwerk/output_style"
|
8
|
+
require "packwerk/output_styles/plain"
|
8
9
|
|
9
10
|
module Packwerk
|
10
11
|
class Offense
|
@@ -23,11 +24,8 @@ module Packwerk
|
|
23
24
|
@message = message
|
24
25
|
end
|
25
26
|
|
26
|
-
sig
|
27
|
-
|
28
|
-
.returns(String)
|
29
|
-
end
|
30
|
-
def to_s(style = OutputStyles::Plain)
|
27
|
+
sig { params(style: OutputStyle).returns(String) }
|
28
|
+
def to_s(style = OutputStyles::Plain.new)
|
31
29
|
if location
|
32
30
|
<<~EOS
|
33
31
|
#{style.filename}#{file}#{style.reset}:#{location.line}:#{location.column}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module OutputStyle
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
interface!
|
10
|
+
|
11
|
+
sig { abstract.returns(String) }
|
12
|
+
def reset; end
|
13
|
+
|
14
|
+
sig { abstract.returns(String) }
|
15
|
+
def filename; end
|
16
|
+
|
17
|
+
sig { abstract.returns(String) }
|
18
|
+
def error; end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module OutputStyles
|
6
|
+
# See https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit for ANSI escape colour codes
|
7
|
+
class Coloured
|
8
|
+
extend T::Sig
|
9
|
+
include OutputStyle
|
10
|
+
|
11
|
+
sig { override.returns(String) }
|
12
|
+
def reset
|
13
|
+
"\033[m"
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { override.returns(String) }
|
17
|
+
def filename
|
18
|
+
# 36 is foreground cyan
|
19
|
+
"\033[36m"
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { override.returns(String) }
|
23
|
+
def error
|
24
|
+
# 31 is foreground red
|
25
|
+
"\033[31m"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module OutputStyles
|
6
|
+
class Plain
|
7
|
+
extend T::Sig
|
8
|
+
include OutputStyle
|
9
|
+
|
10
|
+
sig { override.returns(String) }
|
11
|
+
def reset
|
12
|
+
""
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { override.returns(String) }
|
16
|
+
def filename
|
17
|
+
""
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.returns(String) }
|
21
|
+
def error
|
22
|
+
""
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/packwerk/package_set.rb
CHANGED
@@ -28,8 +28,13 @@ module Packwerk
|
|
28
28
|
def package_paths(root_path, package_pathspec)
|
29
29
|
bundle_path_match = Bundler.bundle_path.join("**").to_s
|
30
30
|
|
31
|
-
|
32
|
-
.
|
31
|
+
glob_patterns = Array(package_pathspec).map do |pathspec|
|
32
|
+
File.join(root_path, pathspec, PACKAGE_CONFIG_FILENAME)
|
33
|
+
end
|
34
|
+
|
35
|
+
Dir.glob(glob_patterns)
|
36
|
+
.map { |path| Pathname.new(path).cleanpath }
|
37
|
+
.reject { |path| path.realpath.fnmatch(bundle_path_match) }
|
33
38
|
end
|
34
39
|
|
35
40
|
private
|
@@ -55,7 +60,8 @@ module Packwerk
|
|
55
60
|
end
|
56
61
|
|
57
62
|
def package_from_path(file_path)
|
58
|
-
|
63
|
+
path_string = file_path.to_s
|
64
|
+
@packages.values.find { |package| package.package_path?(path_string) }
|
59
65
|
end
|
60
66
|
end
|
61
67
|
end
|
@@ -28,8 +28,8 @@ module Packwerk
|
|
28
28
|
|
29
29
|
fully_qualified_constant_name = "::#{constant_name}"
|
30
30
|
|
31
|
-
possible_namespaces = namespace_path.
|
32
|
-
acc << acc.last
|
31
|
+
possible_namespaces = namespace_path.each_with_object([""]) do |current, acc|
|
32
|
+
acc << "#{acc.last}::#{current}" if acc.last && current
|
33
33
|
end
|
34
34
|
|
35
35
|
possible_namespaces.map { |namespace| namespace + fully_qualified_constant_name }
|
@@ -38,12 +38,12 @@ module Packwerk
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def collect_local_definitions_from_root(node, current_namespace_path = [])
|
41
|
-
if Node.
|
41
|
+
if Node.constant_assignment?(node)
|
42
42
|
add_definition(Node.constant_name(node), current_namespace_path, Node.name_location(node))
|
43
43
|
elsif Node.module_name_from_definition(node)
|
44
44
|
# handle compact constant nesting (e.g. "module Sales::Order")
|
45
45
|
tempnode = node
|
46
|
-
while (tempnode = Node.each_child(tempnode).find { |n| Node.
|
46
|
+
while (tempnode = Node.each_child(tempnode).find { |n| Node.constant?(n) })
|
47
47
|
add_definition(Node.constant_name(tempnode), current_namespace_path, Node.name_location(tempnode))
|
48
48
|
end
|
49
49
|
|