packwerk 2.3.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -5
- data/.ruby-version +1 -1
- data/Gemfile +0 -1
- data/Gemfile.lock +5 -95
- data/README.md +2 -7
- data/RESOLVING_VIOLATIONS.md +3 -8
- data/TROUBLESHOOT.md +2 -25
- data/UPGRADING.md +12 -0
- data/USAGE.md +136 -54
- data/dev.yml +1 -1
- data/exe/packwerk +4 -0
- data/gemfiles/Gemfile-rails-6-1 +1 -1
- data/lib/packwerk/application_validator.rb +54 -285
- data/lib/packwerk/association_inspector.rb +2 -0
- data/lib/packwerk/cache.rb +6 -5
- data/lib/packwerk/checker.rb +54 -0
- data/lib/packwerk/cli/result.rb +11 -0
- data/lib/packwerk/cli.rb +55 -40
- data/lib/packwerk/configuration.rb +61 -40
- data/lib/packwerk/const_node_inspector.rb +2 -0
- data/lib/packwerk/constant_context.rb +8 -0
- data/lib/packwerk/constant_discovery.rb +5 -6
- data/lib/packwerk/constant_name_inspector.rb +2 -0
- data/lib/packwerk/disable_sorbet.rb +41 -0
- data/lib/packwerk/extension_loader.rb +24 -0
- data/lib/packwerk/file_processor.rb +3 -1
- data/lib/packwerk/files_for_processing.rb +25 -12
- data/lib/packwerk/formatters/default_offenses_formatter.rb +77 -0
- data/lib/packwerk/formatters/progress_formatter.rb +31 -12
- data/lib/packwerk/generators/configuration_file.rb +7 -2
- data/lib/packwerk/generators/root_package.rb +5 -1
- data/lib/packwerk/generators/templates/package.yml +0 -10
- data/lib/packwerk/graph.rb +10 -2
- data/lib/packwerk/node.rb +1 -1
- data/lib/packwerk/node_helpers.rb +14 -7
- data/lib/packwerk/node_processor.rb +2 -0
- data/lib/packwerk/node_processor_factory.rb +6 -4
- data/lib/packwerk/node_visitor.rb +10 -1
- data/lib/packwerk/offense_collection.rb +26 -18
- data/lib/packwerk/offenses_formatter.rb +59 -2
- data/lib/packwerk/package.rb +7 -35
- data/lib/packwerk/package_set.rb +1 -1
- data/lib/packwerk/package_todo.rb +19 -20
- data/lib/packwerk/parse_run.rb +27 -34
- data/lib/packwerk/parsed_constant_definitions.rb +28 -5
- data/lib/packwerk/parsers/erb.rb +23 -4
- data/lib/packwerk/parsers/factory.rb +11 -2
- data/lib/packwerk/parsers/parser_interface.rb +1 -1
- data/lib/packwerk/parsers/ruby.rb +13 -3
- data/lib/packwerk/parsers.rb +6 -2
- data/lib/packwerk/{application_load_paths.rb → rails_load_paths.rb} +6 -4
- data/lib/packwerk/reference.rb +7 -1
- data/lib/packwerk/reference_checking/checkers/dependency_checker.rb +29 -6
- data/lib/packwerk/reference_checking/reference_checker.rb +1 -1
- data/lib/packwerk/reference_extractor.rb +24 -12
- data/lib/packwerk/reference_offense.rb +2 -2
- data/lib/packwerk/run_context.rb +7 -10
- data/lib/packwerk/spring_command.rb +9 -2
- data/lib/packwerk/unresolved_reference.rb +9 -1
- data/lib/packwerk/validator/result.rb +18 -0
- data/lib/packwerk/validator.rb +90 -0
- data/lib/packwerk/validators/dependency_validator.rb +154 -0
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +64 -26
- data/packwerk.gemspec +4 -2
- data/sorbet/rbi/gems/{zeitwerk@2.6.0.rbi → zeitwerk@2.6.4.rbi} +291 -228
- data/sorbet/rbi/shims/minitest/test.rb +8 -0
- data/sorbet/rbi/shims/packwerk/reference.rbi +33 -0
- data/sorbet/rbi/shims/packwerk/unresolved_reference.rbi +33 -0
- data/sorbet/rbi/shims/parser.rbi +13 -0
- metadata +34 -15
- data/lib/packwerk/formatters/offenses_formatter.rb +0 -52
- data/lib/packwerk/reference_checking/checkers/checker.rb +0 -34
- data/lib/packwerk/reference_checking/checkers/privacy_checker.rb +0 -76
- data/lib/packwerk/result.rb +0 -9
- data/lib/packwerk/sanity_checker.rb +0 -8
- data/lib/packwerk/violation_type.rb +0 -11
- data/sorbet/rbi/gems/html_tokenizer@0.0.7.rbi +0 -46
- data/sorbet/rbi/gems/mini_portile2@2.8.0.rbi +0 -8
@@ -9,234 +9,107 @@ module Packwerk
|
|
9
9
|
# Checks the structure of the application and its packwerk configuration to make sure we can run a check and deliver
|
10
10
|
# correct results.
|
11
11
|
class ApplicationValidator
|
12
|
+
include Validator
|
12
13
|
extend T::Sig
|
13
14
|
|
14
|
-
sig
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
environment: String
|
19
|
-
).void
|
20
|
-
end
|
21
|
-
def initialize(config_file_path:, configuration:, environment:)
|
22
|
-
@config_file_path = config_file_path
|
23
|
-
@configuration = configuration
|
24
|
-
@environment = environment
|
25
|
-
@package_set = T.let(PackageSet.load_all_from(@configuration.root_path, package_pathspec: package_glob),
|
26
|
-
PackageSet)
|
27
|
-
end
|
28
|
-
|
29
|
-
class Result < T::Struct
|
30
|
-
extend T::Sig
|
31
|
-
|
32
|
-
const :ok, T::Boolean
|
33
|
-
const :error_value, T.nilable(String)
|
34
|
-
|
35
|
-
sig { returns(T::Boolean) }
|
36
|
-
def ok?
|
37
|
-
ok
|
38
|
-
end
|
15
|
+
sig { params(package_set: PackageSet, configuration: Configuration).returns(Validator::Result) }
|
16
|
+
def check_all(package_set, configuration)
|
17
|
+
results = Validator.all.flat_map { |validator| validator.call(package_set, configuration) }
|
18
|
+
merge_results(results)
|
39
19
|
end
|
40
20
|
|
41
|
-
sig { returns(Result) }
|
42
|
-
def
|
21
|
+
sig { override.params(package_set: PackageSet, configuration: Configuration).returns(Validator::Result) }
|
22
|
+
def call(package_set, configuration)
|
43
23
|
results = [
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
check_package_manifest_paths,
|
49
|
-
check_valid_package_dependencies,
|
50
|
-
check_root_package_exists,
|
24
|
+
check_package_manifest_syntax(configuration),
|
25
|
+
check_application_structure(configuration),
|
26
|
+
check_package_manifest_paths(configuration),
|
27
|
+
check_root_package_exists(configuration),
|
51
28
|
]
|
52
29
|
|
53
|
-
merge_results(results)
|
30
|
+
merge_results(results, separator: "\n❓ ")
|
54
31
|
end
|
55
32
|
|
56
|
-
sig { returns(
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
root_path: @configuration.root_path,
|
62
|
-
load_paths: @configuration.load_paths
|
63
|
-
)
|
64
|
-
|
65
|
-
results = T.let([], T::Array[Result])
|
66
|
-
|
67
|
-
privacy_settings.each do |config_file_path, setting|
|
68
|
-
next unless setting.is_a?(Array)
|
69
|
-
|
70
|
-
constants = setting
|
71
|
-
|
72
|
-
results += assert_constants_can_be_loaded(constants, config_file_path)
|
73
|
-
|
74
|
-
constant_locations = constants.map { |c| [c, resolver.resolve(c)&.location] }
|
75
|
-
|
76
|
-
constant_locations.each do |name, location|
|
77
|
-
results << if location
|
78
|
-
check_private_constant_location(name, location, config_file_path)
|
79
|
-
else
|
80
|
-
private_constant_unresolvable(name, config_file_path)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
merge_results(results, separator: "\n---\n")
|
33
|
+
sig { override.returns(T::Array[String]) }
|
34
|
+
def permitted_keys
|
35
|
+
[
|
36
|
+
"metadata",
|
37
|
+
]
|
86
38
|
end
|
87
39
|
|
88
|
-
sig { returns(Result) }
|
89
|
-
def check_package_manifest_syntax
|
40
|
+
sig { params(configuration: Configuration).returns(Validator::Result) }
|
41
|
+
def check_package_manifest_syntax(configuration)
|
90
42
|
errors = []
|
91
43
|
|
92
|
-
package_manifests.each do |
|
93
|
-
hash = YAML.load_file(
|
44
|
+
package_manifests(configuration).each do |manifest|
|
45
|
+
hash = YAML.load_file(manifest)
|
94
46
|
next unless hash
|
95
47
|
|
96
|
-
known_keys =
|
48
|
+
known_keys = Validator.all.flat_map(&:permitted_keys)
|
97
49
|
unknown_keys = hash.keys - known_keys
|
98
50
|
|
99
51
|
unless unknown_keys.empty?
|
100
|
-
errors << "
|
101
|
-
"If you think a key should be included in your package.yml, please "\
|
102
|
-
"open an issue in https://github.com/Shopify/packwerk"
|
103
|
-
end
|
104
|
-
|
105
|
-
if hash.key?("enforce_privacy")
|
106
|
-
unless [TrueClass, FalseClass, Array].include?(hash["enforce_privacy"].class)
|
107
|
-
errors << "Invalid 'enforce_privacy' option in #{f.inspect}: #{hash["enforce_privacy"].inspect}"
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
if hash.key?("enforce_dependencies")
|
112
|
-
unless [TrueClass, FalseClass].include?(hash["enforce_dependencies"].class)
|
113
|
-
errors << "Invalid 'enforce_dependencies' option in #{f.inspect}: #{hash["enforce_dependencies"].inspect}"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
if hash.key?("public_path")
|
118
|
-
unless hash["public_path"].is_a?(String)
|
119
|
-
errors << "'public_path' option must be a string in #{f.inspect}: #{hash["public_path"].inspect}"
|
120
|
-
end
|
52
|
+
errors << "\tUnknown keys: #{unknown_keys.inspect} in #{manifest.inspect}"
|
121
53
|
end
|
122
|
-
|
123
|
-
next unless hash.key?("dependencies")
|
124
|
-
next if hash["dependencies"].is_a?(Array)
|
125
|
-
|
126
|
-
errors << "Invalid 'dependencies' option in #{f.inspect}: #{hash["dependencies"].inspect}"
|
127
54
|
end
|
128
55
|
|
129
56
|
if errors.empty?
|
130
|
-
Result.new(ok: true)
|
57
|
+
Validator::Result.new(ok: true)
|
131
58
|
else
|
132
|
-
|
59
|
+
merge_results(
|
60
|
+
errors.map { |error| Validator::Result.new(ok: false, error_value: error) },
|
61
|
+
separator: "\n",
|
62
|
+
before_errors: "Malformed syntax in the following manifests:\n\n",
|
63
|
+
after_errors: "\n",
|
64
|
+
)
|
133
65
|
end
|
134
66
|
end
|
135
67
|
|
136
|
-
sig { returns(Result) }
|
137
|
-
def check_application_structure
|
68
|
+
sig { params(configuration: Configuration).returns(Validator::Result) }
|
69
|
+
def check_application_structure(configuration)
|
138
70
|
resolver = ConstantResolver.new(
|
139
|
-
root_path:
|
140
|
-
load_paths:
|
71
|
+
root_path: configuration.root_path.to_s,
|
72
|
+
load_paths: configuration.load_paths
|
141
73
|
)
|
142
74
|
|
143
75
|
begin
|
144
76
|
resolver.file_map
|
145
|
-
Result.new(ok: true)
|
77
|
+
Validator::Result.new(ok: true)
|
146
78
|
rescue => e
|
147
|
-
Result.new(ok: false, error_value: e.message)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
sig { returns(Result) }
|
152
|
-
def check_acyclic_graph
|
153
|
-
edges = @package_set.flat_map do |package|
|
154
|
-
package.dependencies.map { |dependency| [package, @package_set.fetch(dependency)] }
|
155
|
-
end
|
156
|
-
dependency_graph = Graph.new(*T.unsafe(edges))
|
157
|
-
|
158
|
-
cycle_strings = build_cycle_strings(dependency_graph.cycles)
|
159
|
-
|
160
|
-
if dependency_graph.acyclic?
|
161
|
-
Result.new(ok: true)
|
162
|
-
else
|
163
|
-
Result.new(
|
164
|
-
ok: false,
|
165
|
-
error_value: <<~EOS
|
166
|
-
Expected the package dependency graph to be acyclic, but it contains the following cycles:
|
167
|
-
|
168
|
-
#{cycle_strings.join("\n")}
|
169
|
-
EOS
|
170
|
-
)
|
79
|
+
Validator::Result.new(ok: false, error_value: e.message)
|
171
80
|
end
|
172
81
|
end
|
173
82
|
|
174
|
-
sig { returns(Result) }
|
175
|
-
def check_package_manifest_paths
|
176
|
-
all_package_manifests = package_manifests("**/")
|
177
|
-
package_paths_package_manifests = package_manifests(package_glob)
|
83
|
+
sig { params(configuration: Configuration).returns(Validator::Result) }
|
84
|
+
def check_package_manifest_paths(configuration)
|
85
|
+
all_package_manifests = package_manifests(configuration, "**/")
|
86
|
+
package_paths_package_manifests = package_manifests(configuration, package_glob(configuration))
|
178
87
|
|
179
88
|
difference = all_package_manifests - package_paths_package_manifests
|
180
89
|
|
181
90
|
if difference.empty?
|
182
|
-
Result.new(ok: true)
|
91
|
+
Validator::Result.new(ok: true)
|
183
92
|
else
|
184
|
-
Result.new(
|
93
|
+
Validator::Result.new(
|
185
94
|
ok: false,
|
186
95
|
error_value: <<~EOS
|
187
96
|
Expected package paths for all package.ymls to be specified, but paths were missing for the following manifests:
|
188
97
|
|
189
|
-
#{relative_paths(difference).join("\n")}
|
190
|
-
EOS
|
191
|
-
)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
sig { returns(Result) }
|
196
|
-
def check_valid_package_dependencies
|
197
|
-
packages_dependencies = package_manifests_settings_for("dependencies")
|
198
|
-
.delete_if { |_, deps| deps.nil? }
|
199
|
-
|
200
|
-
packages_with_invalid_dependencies =
|
201
|
-
packages_dependencies.each_with_object([]) do |(package, dependencies), invalid_packages|
|
202
|
-
invalid_dependencies = dependencies.filter { |path| invalid_package_path?(path) }
|
203
|
-
invalid_packages << [package, invalid_dependencies] if invalid_dependencies.any?
|
204
|
-
end
|
205
|
-
|
206
|
-
if packages_with_invalid_dependencies.empty?
|
207
|
-
Result.new(ok: true)
|
208
|
-
else
|
209
|
-
error_locations = packages_with_invalid_dependencies.map do |package, invalid_dependencies|
|
210
|
-
package ||= @configuration.root_path
|
211
|
-
package_path = Pathname.new(package).relative_path_from(@configuration.root_path)
|
212
|
-
all_invalid_dependencies = invalid_dependencies.map { |d| " - #{d}" }
|
213
|
-
|
214
|
-
<<~EOS
|
215
|
-
#{package_path}:
|
216
|
-
#{all_invalid_dependencies.join("\n")}
|
217
|
-
EOS
|
218
|
-
end
|
219
|
-
|
220
|
-
Result.new(
|
221
|
-
ok: false,
|
222
|
-
error_value: <<~EOS
|
223
|
-
These dependencies do not point to valid packages:
|
224
|
-
|
225
|
-
#{error_locations.join("\n")}
|
98
|
+
#{relative_paths(configuration, difference).join("\n")}
|
226
99
|
EOS
|
227
100
|
)
|
228
101
|
end
|
229
102
|
end
|
230
103
|
|
231
|
-
sig { returns(Result) }
|
232
|
-
def check_root_package_exists
|
233
|
-
root_package_path = File.join(
|
234
|
-
all_packages_manifests = package_manifests(package_glob)
|
104
|
+
sig { params(configuration: Configuration).returns(Validator::Result) }
|
105
|
+
def check_root_package_exists(configuration)
|
106
|
+
root_package_path = File.join(configuration.root_path, "package.yml")
|
107
|
+
all_packages_manifests = package_manifests(configuration, package_glob(configuration))
|
235
108
|
|
236
109
|
if all_packages_manifests.include?(root_package_path)
|
237
|
-
Result.new(ok: true)
|
110
|
+
Validator::Result.new(ok: true)
|
238
111
|
else
|
239
|
-
Result.new(
|
112
|
+
Validator::Result.new(
|
240
113
|
ok: false,
|
241
114
|
error_value: <<~EOS
|
242
115
|
A root package does not exist. Create an empty `package.yml` at the root directory.
|
@@ -247,120 +120,16 @@ module Packwerk
|
|
247
120
|
|
248
121
|
private
|
249
122
|
|
250
|
-
# Convert the cycles:
|
251
|
-
#
|
252
|
-
# [[a, b, c], [b, c]]
|
253
|
-
#
|
254
|
-
# to the string:
|
255
|
-
#
|
256
|
-
# ["a -> b -> c -> a", "b -> c -> b"]
|
257
|
-
sig { params(cycles: T.untyped).returns(T::Array[String]) }
|
258
|
-
def build_cycle_strings(cycles)
|
259
|
-
cycles.map do |cycle|
|
260
|
-
cycle_strings = cycle.map(&:to_s)
|
261
|
-
cycle_strings << cycle.first.to_s
|
262
|
-
"\t- #{cycle_strings.join(" → ")}"
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
sig { params(setting: T.untyped).returns(T.untyped) }
|
267
|
-
def package_manifests_settings_for(setting)
|
268
|
-
package_manifests.map { |f| [f, (YAML.load_file(File.join(f)) || {})[setting]] }
|
269
|
-
end
|
270
|
-
|
271
123
|
sig { params(list: T.untyped).returns(T.untyped) }
|
272
124
|
def format_yaml_strings(list)
|
273
125
|
list.sort.map { |p| "- \"#{p}\"" }.join("\n")
|
274
126
|
end
|
275
127
|
|
276
|
-
sig {
|
277
|
-
def
|
278
|
-
|
279
|
-
end
|
280
|
-
|
281
|
-
sig { params(glob_pattern: T.any(T::Array[String], String)).returns(T::Array[String]) }
|
282
|
-
def package_manifests(glob_pattern = package_glob)
|
283
|
-
PackageSet.package_paths(@configuration.root_path, glob_pattern, @configuration.exclude)
|
284
|
-
.map { |f| File.realpath(f) }
|
285
|
-
end
|
286
|
-
|
287
|
-
sig { params(paths: T::Array[String]).returns(T::Array[Pathname]) }
|
288
|
-
def relative_paths(paths)
|
289
|
-
paths.map { |path| relative_path(path) }
|
290
|
-
end
|
291
|
-
|
292
|
-
sig { params(path: String).returns(Pathname) }
|
293
|
-
def relative_path(path)
|
294
|
-
Pathname.new(path).relative_path_from(@configuration.root_path)
|
295
|
-
end
|
296
|
-
|
297
|
-
sig { params(path: T.untyped).returns(T::Boolean) }
|
298
|
-
def invalid_package_path?(path)
|
299
|
-
# Packages at the root can be implicitly specified as "."
|
300
|
-
return false if path == "."
|
301
|
-
|
302
|
-
package_path = File.join(@configuration.root_path, path, PackageSet::PACKAGE_CONFIG_FILENAME)
|
303
|
-
!File.file?(package_path)
|
304
|
-
end
|
305
|
-
|
306
|
-
sig { params(constants: T.untyped, config_file_path: String).returns(T::Array[Result]) }
|
307
|
-
def assert_constants_can_be_loaded(constants, config_file_path)
|
308
|
-
constants.map do |constant|
|
309
|
-
if !constant.start_with?("::")
|
310
|
-
Result.new(
|
311
|
-
ok: false,
|
312
|
-
error_value: "'#{constant}', listed in the 'enforce_privacy' option in #{config_file_path}, is invalid.\n"\
|
313
|
-
"Private constants need to be prefixed with the top-level namespace operator `::`."
|
314
|
-
)
|
315
|
-
else
|
316
|
-
constant.try(&:constantize) && Result.new(ok: true)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
sig { params(name: T.untyped, config_file_path: T.untyped).returns(Result) }
|
322
|
-
def private_constant_unresolvable(name, config_file_path)
|
323
|
-
explicit_filepath = (name.start_with?("::") ? name[2..-1] : name).underscore + ".rb"
|
324
|
-
|
325
|
-
Result.new(
|
326
|
-
ok: false,
|
327
|
-
error_value: "'#{name}', listed in #{config_file_path}, could not be resolved.\n"\
|
328
|
-
"This is probably because it is an autovivified namespace - a namespace module that doesn't have a\n"\
|
329
|
-
"file explicitly defining it. Packwerk currently doesn't support declaring autovivified namespaces as\n"\
|
330
|
-
"private. Add a #{explicit_filepath} file to explicitly define the constant."
|
331
|
-
)
|
332
|
-
end
|
333
|
-
|
334
|
-
sig { params(name: T.untyped, location: T.untyped, config_file_path: T.untyped).returns(Result) }
|
335
|
-
def check_private_constant_location(name, location, config_file_path)
|
336
|
-
declared_package = @package_set.package_from_path(relative_path(config_file_path))
|
337
|
-
constant_package = @package_set.package_from_path(location)
|
338
|
-
|
339
|
-
if constant_package == declared_package
|
340
|
-
Result.new(ok: true)
|
341
|
-
else
|
342
|
-
Result.new(
|
343
|
-
ok: false,
|
344
|
-
error_value: "'#{name}' is declared as private in the '#{declared_package}' package but appears to be "\
|
345
|
-
"defined\nin the '#{constant_package}' package. Packwerk resolved it to #{location}."
|
346
|
-
)
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
sig do
|
351
|
-
params(results: T::Array[Result], separator: String, errors_headline: String).returns(Result)
|
352
|
-
end
|
353
|
-
def merge_results(results, separator: "\n===\n", errors_headline: "")
|
354
|
-
results.reject!(&:ok?)
|
355
|
-
|
356
|
-
if results.empty?
|
357
|
-
Result.new(ok: true)
|
358
|
-
else
|
359
|
-
Result.new(
|
360
|
-
ok: false,
|
361
|
-
error_value: errors_headline + results.map(&:error_value).join(separator)
|
362
|
-
)
|
363
|
-
end
|
128
|
+
sig { params(configuration: Configuration, paths: T::Array[String]).returns(T::Array[Pathname]) }
|
129
|
+
def relative_paths(configuration, paths)
|
130
|
+
paths.map { |path| relative_path(configuration, path) }
|
364
131
|
end
|
365
132
|
end
|
133
|
+
|
134
|
+
private_constant :ApplicationValidator
|
366
135
|
end
|
data/lib/packwerk/cache.rb
CHANGED
@@ -21,10 +21,10 @@ module Packwerk
|
|
21
21
|
cache_contents_json = JSON.parse(serialized_cache_contents)
|
22
22
|
unresolved_references = cache_contents_json["unresolved_references"].map do |json|
|
23
23
|
UnresolvedReference.new(
|
24
|
-
json["constant_name"],
|
25
|
-
json["namespace_path"],
|
26
|
-
json["relative_path"],
|
27
|
-
Node::Location.new(json["source_location"]["line"], json["source_location"]["column"],)
|
24
|
+
constant_name: json["constant_name"],
|
25
|
+
namespace_path: json["namespace_path"],
|
26
|
+
relative_path: json["relative_path"],
|
27
|
+
source_location: Node::Location.new(json["source_location"]["line"], json["source_location"]["column"],)
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
@@ -169,8 +169,9 @@ module Packwerk
|
|
169
169
|
puts(out)
|
170
170
|
end
|
171
171
|
end
|
172
|
-
|
172
|
+
end
|
173
173
|
end
|
174
174
|
|
175
|
+
private_constant :Cache
|
175
176
|
private_constant :Debug
|
176
177
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Packwerk
|
5
|
+
module Checker
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
abstract!
|
10
|
+
|
11
|
+
class << self
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { params(base: Class).void }
|
15
|
+
def included(base)
|
16
|
+
@checkers ||= T.let(@checkers, T.nilable(T::Array[Class]))
|
17
|
+
@checkers ||= []
|
18
|
+
@checkers << base
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { returns(T::Array[Checker]) }
|
22
|
+
def all
|
23
|
+
T.unsafe(@checkers).map(&:new)
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { params(violation_type: String).returns(Checker) }
|
27
|
+
def find(violation_type)
|
28
|
+
checker_by_violation_type(violation_type)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
sig { params(name: String).returns(Checker) }
|
34
|
+
def checker_by_violation_type(name)
|
35
|
+
@checker_by_violation_type ||= T.let(Checker.all.to_h do |checker|
|
36
|
+
[checker.violation_type, checker]
|
37
|
+
end, T.nilable(T::Hash[String, Checker]))
|
38
|
+
@checker_by_violation_type.fetch(name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
sig { abstract.returns(String) }
|
43
|
+
def violation_type; end
|
44
|
+
|
45
|
+
sig { abstract.params(listed_offense: ReferenceOffense).returns(T::Boolean) }
|
46
|
+
def strict_mode_violation?(listed_offense); end
|
47
|
+
|
48
|
+
sig { abstract.params(reference: Reference).returns(T::Boolean) }
|
49
|
+
def invalid_reference?(reference); end
|
50
|
+
|
51
|
+
sig { abstract.params(reference: Reference).returns(String) }
|
52
|
+
def message(reference); end
|
53
|
+
end
|
54
|
+
end
|