packwerk 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66332315c1155336fbe79f8aeb6e44a0893d4100a1d4b6b5f08c81574c940fe0
4
- data.tar.gz: 88da350ce07daaf7556d23edbc318a19ca33b74af71ee624d547a8a8598bc16b
3
+ metadata.gz: e0270de859808f51bbfa45a7140813744da0555c57d9d60432ba4e166ab63c4a
4
+ data.tar.gz: 90465398a3e04f83ba4c0e3b290dca72677b166003640504c7470b181e556d73
5
5
  SHA512:
6
- metadata.gz: b54eeeac5e68285e03c32916d72f2afdc291d9d6d4993de0d2875d7dbb4a9cdf8cde7ca2677107f35288a20a20c1f8fc2f9ddcc330c9a7fe0dfaf8e245c07b3c
7
- data.tar.gz: d8597878958e00a069cb5e51c609e04da9526b3c1933c4824399e4bad1173d718703c88ec582411a9c73fb5c88899707b04ebdb7b296a22b70e25598e11b57d6
6
+ metadata.gz: 86e386674412f99d605013799ebd09bbff4d9e0dd2718de534db33e71c148fd0b8aa2ec905ca96c34c48ce85e31e922eae41352c05676de8f4af6baea749c03e
7
+ data.tar.gz: 6406b7ddbcac8c91f3e51660d5c5e111145b6f8f01ef6ca2bfc1797cdec39b13cb0db1e3d8d04026bc59835bb67e9e8acb41a5edbd0cd207f609bbb532ffa067
data/Gemfile.lock CHANGED
@@ -87,7 +87,7 @@ GIT
87
87
  PATH
88
88
  remote: .
89
89
  specs:
90
- packwerk (2.1.0)
90
+ packwerk (2.1.1)
91
91
  activesupport (>= 5.2)
92
92
  ast
93
93
  better_html
@@ -138,14 +138,16 @@ GEM
138
138
  marcel (1.0.0)
139
139
  method_source (1.0.0)
140
140
  mini_mime (1.0.3)
141
- mini_portile2 (2.6.1)
141
+ mini_portile2 (2.8.0)
142
142
  minitest (5.14.4)
143
143
  minitest-focus (1.2.1)
144
144
  minitest (>= 4, < 6)
145
145
  mocha (1.12.0)
146
146
  nio4r (2.5.7)
147
- nokogiri (1.12.5)
148
- mini_portile2 (~> 2.6.1)
147
+ nokogiri (1.13.3)
148
+ mini_portile2 (~> 2.8.0)
149
+ racc (~> 1.4)
150
+ nokogiri (1.13.3-x86_64-darwin)
149
151
  racc (~> 1.4)
150
152
  parallel (1.20.1)
151
153
  parlour (6.0.0)
@@ -159,7 +161,7 @@ GEM
159
161
  coderay (~> 1.1)
160
162
  method_source (~> 1.0)
161
163
  psych (3.3.2)
162
- racc (1.5.2)
164
+ racc (1.6.0)
163
165
  rack (2.2.3)
164
166
  rack-test (1.1.0)
165
167
  rack (>= 1.0, < 3)
@@ -191,18 +193,19 @@ GEM
191
193
  rubocop-sorbet (0.6.1)
192
194
  rubocop
193
195
  ruby-progressbar (1.11.0)
194
- smart_properties (1.16.3)
195
- sorbet (0.5.6360)
196
- sorbet-static (= 0.5.6360)
197
- sorbet-runtime (0.5.6360)
198
- sorbet-static (0.5.6360-universal-darwin-14)
199
- sorbet-static (0.5.6360-universal-darwin-15)
200
- sorbet-static (0.5.6360-universal-darwin-16)
201
- sorbet-static (0.5.6360-universal-darwin-17)
202
- sorbet-static (0.5.6360-universal-darwin-18)
203
- sorbet-static (0.5.6360-universal-darwin-19)
204
- sorbet-static (0.5.6360-universal-darwin-20)
205
- sorbet-static (0.5.6360-x86_64-linux)
196
+ smart_properties (1.17.0)
197
+ sorbet (0.5.9538)
198
+ sorbet-static (= 0.5.9538)
199
+ sorbet-runtime (0.5.9538)
200
+ sorbet-static (0.5.9538-universal-darwin-14)
201
+ sorbet-static (0.5.9538-universal-darwin-15)
202
+ sorbet-static (0.5.9538-universal-darwin-16)
203
+ sorbet-static (0.5.9538-universal-darwin-17)
204
+ sorbet-static (0.5.9538-universal-darwin-18)
205
+ sorbet-static (0.5.9538-universal-darwin-19)
206
+ sorbet-static (0.5.9538-universal-darwin-20)
207
+ sorbet-static (0.5.9538-universal-darwin-21)
208
+ sorbet-static (0.5.9538-x86_64-linux)
206
209
  spoom (1.1.0)
207
210
  colorize
208
211
  sorbet (>= 0.5.6347)
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "constant_resolver"
@@ -9,14 +9,36 @@ 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
+ extend T::Sig
13
+
14
+ sig do
15
+ params(
16
+ config_file_path: String,
17
+ configuration: Configuration,
18
+ environment: String
19
+ ).void
20
+ end
12
21
  def initialize(config_file_path:, configuration:, environment:)
13
22
  @config_file_path = config_file_path
14
23
  @configuration = configuration
15
24
  @environment = environment
25
+ @package_set = T.let(PackageSet.load_all_from(@configuration.root_path, package_pathspec: package_glob),
26
+ PackageSet)
16
27
  end
17
28
 
18
- Result = Struct.new(:ok?, :error_value)
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
39
+ end
19
40
 
41
+ sig { returns(Result) }
20
42
  def check_all
21
43
  results = [
22
44
  check_package_manifests_for_privacy,
@@ -31,6 +53,7 @@ module Packwerk
31
53
  merge_results(results)
32
54
  end
33
55
 
56
+ sig { returns(Result) }
34
57
  def check_package_manifests_for_privacy
35
58
  privacy_settings = package_manifests_settings_for("enforce_privacy")
36
59
 
@@ -39,7 +62,7 @@ module Packwerk
39
62
  load_paths: @configuration.load_paths
40
63
  )
41
64
 
42
- results = T.let([], T::Array[Packwerk::Result])
65
+ results = T.let([], T::Array[Result])
43
66
 
44
67
  privacy_settings.each do |config_file_path, setting|
45
68
  next unless setting.is_a?(Array)
@@ -61,6 +84,7 @@ module Packwerk
61
84
  merge_results(results, separator: "\n---\n")
62
85
  end
63
86
 
87
+ sig { returns(Result) }
64
88
  def check_package_manifest_syntax
65
89
  errors = []
66
90
 
@@ -102,12 +126,13 @@ module Packwerk
102
126
  end
103
127
 
104
128
  if errors.empty?
105
- Result.new(true)
129
+ Result.new(ok: true)
106
130
  else
107
- Result.new(false, errors.join("\n---\n"))
131
+ Result.new(ok: false, error_value: errors.join("\n---\n"))
108
132
  end
109
133
  end
110
134
 
135
+ sig { returns(Result) }
111
136
  def check_application_structure
112
137
  resolver = ConstantResolver.new(
113
138
  root_path: @configuration.root_path.to_s,
@@ -116,26 +141,27 @@ module Packwerk
116
141
 
117
142
  begin
118
143
  resolver.file_map
119
- Result.new(true)
144
+ Result.new(ok: true)
120
145
  rescue => e
121
- Result.new(false, e.message)
146
+ Result.new(ok: false, error_value: e.message)
122
147
  end
123
148
  end
124
149
 
150
+ sig { returns(Result) }
125
151
  def check_acyclic_graph
126
- edges = package_set.flat_map do |package|
127
- package.dependencies.map { |dependency| [package, package_set.fetch(dependency)] }
152
+ edges = @package_set.flat_map do |package|
153
+ package.dependencies.map { |dependency| [package, @package_set.fetch(dependency)] }
128
154
  end
129
- dependency_graph = Packwerk::Graph.new(*T.unsafe(edges))
155
+ dependency_graph = Graph.new(*T.unsafe(edges))
130
156
 
131
157
  cycle_strings = build_cycle_strings(dependency_graph.cycles)
132
158
 
133
159
  if dependency_graph.acyclic?
134
- Result.new(true)
160
+ Result.new(ok: true)
135
161
  else
136
162
  Result.new(
137
- false,
138
- <<~EOS
163
+ ok: false,
164
+ error_value: <<~EOS
139
165
  Expected the package dependency graph to be acyclic, but it contains the following cycles:
140
166
 
141
167
  #{cycle_strings.join("\n")}
@@ -144,6 +170,7 @@ module Packwerk
144
170
  end
145
171
  end
146
172
 
173
+ sig { returns(Result) }
147
174
  def check_package_manifest_paths
148
175
  all_package_manifests = package_manifests("**/")
149
176
  package_paths_package_manifests = package_manifests(package_glob)
@@ -151,11 +178,11 @@ module Packwerk
151
178
  difference = all_package_manifests - package_paths_package_manifests
152
179
 
153
180
  if difference.empty?
154
- Result.new(true)
181
+ Result.new(ok: true)
155
182
  else
156
183
  Result.new(
157
- false,
158
- <<~EOS
184
+ ok: false,
185
+ error_value: <<~EOS
159
186
  Expected package paths for all package.ymls to be specified, but paths were missing for the following manifests:
160
187
 
161
188
  #{relative_paths(difference).join("\n")}
@@ -164,6 +191,7 @@ module Packwerk
164
191
  end
165
192
  end
166
193
 
194
+ sig { returns(Result) }
167
195
  def check_valid_package_dependencies
168
196
  packages_dependencies = package_manifests_settings_for("dependencies")
169
197
  .delete_if { |_, deps| deps.nil? }
@@ -175,7 +203,7 @@ module Packwerk
175
203
  end
176
204
 
177
205
  if packages_with_invalid_dependencies.empty?
178
- Result.new(true)
206
+ Result.new(ok: true)
179
207
  else
180
208
  error_locations = packages_with_invalid_dependencies.map do |package, invalid_dependencies|
181
209
  package ||= @configuration.root_path
@@ -189,8 +217,8 @@ module Packwerk
189
217
  end
190
218
 
191
219
  Result.new(
192
- false,
193
- <<~EOS
220
+ ok: false,
221
+ error_value: <<~EOS
194
222
  These dependencies do not point to valid packages:
195
223
 
196
224
  #{error_locations.join("\n")}
@@ -199,16 +227,17 @@ module Packwerk
199
227
  end
200
228
  end
201
229
 
230
+ sig { returns(Result) }
202
231
  def check_root_package_exists
203
232
  root_package_path = File.join(@configuration.root_path, "package.yml")
204
233
  all_packages_manifests = package_manifests(package_glob)
205
234
 
206
235
  if all_packages_manifests.include?(root_package_path)
207
- Result.new(true)
236
+ Result.new(ok: true)
208
237
  else
209
238
  Result.new(
210
- false,
211
- <<~EOS
239
+ ok: false,
240
+ error_value: <<~EOS
212
241
  A root package does not exist. Create an empty `package.yml` at the root directory.
213
242
  EOS
214
243
  )
@@ -224,6 +253,7 @@ module Packwerk
224
253
  # to the string:
225
254
  #
226
255
  # ["a -> b -> c -> a", "b -> c -> b"]
256
+ sig { params(cycles: T.untyped).returns(T::Array[String]) }
227
257
  def build_cycle_strings(cycles)
228
258
  cycles.map do |cycle|
229
259
  cycle_strings = cycle.map(&:to_s)
@@ -232,93 +262,102 @@ module Packwerk
232
262
  end
233
263
  end
234
264
 
265
+ sig { params(setting: T.untyped).returns(T.untyped) }
235
266
  def package_manifests_settings_for(setting)
236
267
  package_manifests.map { |f| [f, (YAML.load_file(File.join(f)) || {})[setting]] }
237
268
  end
238
269
 
270
+ sig { params(list: T.untyped).returns(T.untyped) }
239
271
  def format_yaml_strings(list)
240
272
  list.sort.map { |p| "- \"#{p}\"" }.join("\n")
241
273
  end
242
274
 
275
+ sig { returns(T.any(T::Array[String], String)) }
243
276
  def package_glob
244
277
  @configuration.package_paths || "**"
245
278
  end
246
279
 
280
+ sig { params(glob_pattern: T.any(T::Array[String], String)).returns(T::Array[String]) }
247
281
  def package_manifests(glob_pattern = package_glob)
248
282
  PackageSet.package_paths(@configuration.root_path, glob_pattern, @configuration.exclude)
249
283
  .map { |f| File.realpath(f) }
250
284
  end
251
285
 
286
+ sig { params(paths: T::Array[String]).returns(T::Array[Pathname]) }
252
287
  def relative_paths(paths)
253
288
  paths.map { |path| relative_path(path) }
254
289
  end
255
290
 
291
+ sig { params(path: String).returns(Pathname) }
256
292
  def relative_path(path)
257
293
  Pathname.new(path).relative_path_from(@configuration.root_path)
258
294
  end
259
295
 
296
+ sig { params(path: T.untyped).returns(T::Boolean) }
260
297
  def invalid_package_path?(path)
261
298
  # Packages at the root can be implicitly specified as "."
262
299
  return false if path == "."
263
300
 
264
- package_path = File.join(@configuration.root_path, path, Packwerk::PackageSet::PACKAGE_CONFIG_FILENAME)
301
+ package_path = File.join(@configuration.root_path, path, PackageSet::PACKAGE_CONFIG_FILENAME)
265
302
  !File.file?(package_path)
266
303
  end
267
304
 
305
+ sig { params(constants: T.untyped, config_file_path: String).returns(T::Array[Result]) }
268
306
  def assert_constants_can_be_loaded(constants, config_file_path)
269
307
  constants.map do |constant|
270
308
  if !constant.start_with?("::")
271
309
  Result.new(
272
- false,
273
- "'#{constant}', listed in the 'enforce_privacy' option in #{config_file_path}, is invalid.\n"\
310
+ ok: false,
311
+ error_value: "'#{constant}', listed in the 'enforce_privacy' option in #{config_file_path}, is invalid.\n"\
274
312
  "Private constants need to be prefixed with the top-level namespace operator `::`."
275
313
  )
276
314
  else
277
- constant.try(&:constantize) && Result.new(true)
315
+ constant.try(&:constantize) && Result.new(ok: true)
278
316
  end
279
317
  end
280
318
  end
281
319
 
320
+ sig { params(name: T.untyped, config_file_path: T.untyped).returns(Result) }
282
321
  def private_constant_unresolvable(name, config_file_path)
283
322
  explicit_filepath = (name.start_with?("::") ? name[2..-1] : name).underscore + ".rb"
284
323
 
285
324
  Result.new(
286
- false,
287
- "'#{name}', listed in #{config_file_path}, could not be resolved.\n"\
325
+ ok: false,
326
+ error_value: "'#{name}', listed in #{config_file_path}, could not be resolved.\n"\
288
327
  "This is probably because it is an autovivified namespace - a namespace module that doesn't have a\n"\
289
328
  "file explicitly defining it. Packwerk currently doesn't support declaring autovivified namespaces as\n"\
290
329
  "private. Add a #{explicit_filepath} file to explicitly define the constant."
291
330
  )
292
331
  end
293
332
 
333
+ sig { params(name: T.untyped, location: T.untyped, config_file_path: T.untyped).returns(Result) }
294
334
  def check_private_constant_location(name, location, config_file_path)
295
- declared_package = package_set.package_from_path(relative_path(config_file_path))
296
- constant_package = package_set.package_from_path(location)
335
+ declared_package = @package_set.package_from_path(relative_path(config_file_path))
336
+ constant_package = @package_set.package_from_path(location)
297
337
 
298
338
  if constant_package == declared_package
299
- Result.new(true)
339
+ Result.new(ok: true)
300
340
  else
301
341
  Result.new(
302
- false,
303
- "'#{name}' is declared as private in the '#{declared_package}' package but appears to be "\
342
+ ok: false,
343
+ error_value: "'#{name}' is declared as private in the '#{declared_package}' package but appears to be "\
304
344
  "defined\nin the '#{constant_package}' package. Packwerk resolved it to #{location}."
305
345
  )
306
346
  end
307
347
  end
308
348
 
309
- def package_set
310
- @package_set ||= Packwerk::PackageSet.load_all_from(@configuration.root_path, package_pathspec: package_glob)
349
+ sig do
350
+ params(results: T::Array[Result], separator: String, errors_headline: String).returns(Result)
311
351
  end
312
-
313
352
  def merge_results(results, separator: "\n===\n", errors_headline: "")
314
353
  results.reject!(&:ok?)
315
354
 
316
355
  if results.empty?
317
- Result.new(true)
356
+ Result.new(ok: true)
318
357
  else
319
358
  Result.new(
320
- false,
321
- errors_headline + results.map(&:error_value).join(separator)
359
+ ok: false,
360
+ error_value: errors_headline + results.map(&:error_value).join(separator)
322
361
  )
323
362
  end
324
363
  end
@@ -44,7 +44,7 @@ module Packwerk
44
44
  ]
45
45
  end
46
46
 
47
- sig { params(enable_cache: T::Boolean, cache_directory: Pathname, config_path: String).void }
47
+ sig { params(enable_cache: T::Boolean, cache_directory: Pathname, config_path: T.nilable(String)).void }
48
48
  def initialize(enable_cache:, cache_directory:, config_path:)
49
49
  @enable_cache = enable_cache
50
50
  @cache = T.let({}, CACHE_SHAPE)
@@ -115,6 +115,7 @@ module Packwerk
115
115
 
116
116
  sig { void }
117
117
  def bust_cache_if_packwerk_yml_has_changed!
118
+ return nil if @config_path.nil?
118
119
  bust_cache_if_contents_have_changed(File.read(@config_path), :packwerk_yml)
119
120
  end
120
121
 
data/lib/packwerk/cli.rb CHANGED
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "optparse"
@@ -30,9 +30,10 @@ module Packwerk
30
30
  @err_out = err_out
31
31
  @environment = environment
32
32
  @style = style
33
- @configuration = configuration || Configuration.from_path
34
- @progress_formatter = Formatters::ProgressFormatter.new(@out, style: style)
35
- @offenses_formatter = offenses_formatter || Formatters::OffensesFormatter.new(style: @style)
33
+ @configuration = T.let(configuration || Configuration.from_path, Configuration)
34
+ @progress_formatter = T.let(Formatters::ProgressFormatter.new(@out, style: style), Formatters::ProgressFormatter)
35
+ @offenses_formatter = T.let(offenses_formatter || Formatters::OffensesFormatter.new(style: @style),
36
+ OffensesFormatter)
36
37
  end
37
38
 
38
39
  sig { params(args: T::Array[String]).returns(T.noreturn) }
@@ -78,12 +79,14 @@ module Packwerk
78
79
 
79
80
  private
80
81
 
82
+ sig { returns(T::Boolean) }
81
83
  def init
82
84
  @out.puts("📦 Initializing Packwerk...")
83
85
 
84
86
  generate_configs
85
87
  end
86
88
 
89
+ sig { returns(T::Boolean) }
87
90
  def generate_configs
88
91
  configuration_file = Packwerk::Generators::ConfigurationFile.generate(
89
92
  root: @configuration.root_path,
@@ -112,23 +115,26 @@ module Packwerk
112
115
  success
113
116
  end
114
117
 
118
+ sig { params(result: Result).returns(T::Boolean) }
115
119
  def output_result(result)
116
120
  @out.puts
117
121
  @out.puts(result.message)
118
122
  result.status
119
123
  end
120
124
 
121
- def fetch_files_to_process(paths, ignore_nested_packages)
122
- files = FilesForProcessing.fetch(
123
- paths: paths,
125
+ sig { params(relative_file_paths: T::Array[String], ignore_nested_packages: T::Boolean).returns(T::Array[String]) }
126
+ def fetch_files_to_process(relative_file_paths, ignore_nested_packages)
127
+ absolute_files = FilesForProcessing.fetch(
128
+ relative_file_paths: relative_file_paths,
124
129
  ignore_nested_packages: ignore_nested_packages,
125
130
  configuration: @configuration
126
131
  )
127
132
  abort("No files found or given. "\
128
- "Specify files or check the include and exclude glob in the config file.") if files.empty?
129
- files
133
+ "Specify files or check the include and exclude glob in the config file.") if absolute_files.empty?
134
+ absolute_files
130
135
  end
131
136
 
137
+ sig { params(_paths: T::Array[String]).returns(T::Boolean) }
132
138
  def validate(_paths)
133
139
  @progress_formatter.started_validation do
134
140
  result = checker.check_all
@@ -139,6 +145,7 @@ module Packwerk
139
145
  end
140
146
  end
141
147
 
148
+ sig { returns(ApplicationValidator) }
142
149
  def checker
143
150
  Packwerk::ApplicationValidator.new(
144
151
  config_file_path: @configuration.config_path,
@@ -147,6 +154,7 @@ module Packwerk
147
154
  )
148
155
  end
149
156
 
157
+ sig { params(result: ApplicationValidator::Result).void }
150
158
  def list_validation_errors(result)
151
159
  @out.puts
152
160
  if result.ok?
@@ -157,24 +165,25 @@ module Packwerk
157
165
  end
158
166
  end
159
167
 
168
+ sig { params(params: T.untyped).returns(ParseRun) }
160
169
  def parse_run(params)
161
- paths = T.let([], T::Array[String])
170
+ relative_file_paths = T.let([], T::Array[String])
162
171
  ignore_nested_packages = nil
163
172
 
164
173
  if params.any? { |p| p.include?("--packages") }
165
174
  OptionParser.new do |parser|
166
175
  parser.on("--packages=PACKAGESLIST", Array, "package names, comma separated") do |p|
167
- paths = p
176
+ relative_file_paths = p
168
177
  end
169
178
  end.parse!(params)
170
179
  ignore_nested_packages = true
171
180
  else
172
- paths = params
181
+ relative_file_paths = params
173
182
  ignore_nested_packages = false
174
183
  end
175
184
 
176
185
  ParseRun.new(
177
- files: fetch_files_to_process(paths, ignore_nested_packages),
186
+ absolute_files: fetch_files_to_process(relative_file_paths, ignore_nested_packages),
178
187
  configuration: @configuration,
179
188
  progress_formatter: @progress_formatter,
180
189
  offenses_formatter: @offenses_formatter
@@ -15,10 +15,15 @@ module Packwerk
15
15
  # have no way of inferring the file it is defined in. You could argue though that inheritance means that another
16
16
  # constant with the same name exists in the inheriting class, and this view is sufficient for all our use cases.
17
17
  class ConstantDiscovery
18
+ extend T::Sig
19
+
18
20
  ConstantContext = Struct.new(:name, :location, :package, :public?)
19
21
 
20
22
  # @param constant_resolver [ConstantResolver]
21
23
  # @param packages [Packwerk::PackageSet]
24
+ sig do
25
+ params(constant_resolver: ConstantResolver, packages: Packwerk::PackageSet).void
26
+ end
22
27
  def initialize(constant_resolver:, packages:)
23
28
  @packages = packages
24
29
  @resolver = constant_resolver
@@ -30,6 +35,11 @@ module Packwerk
30
35
  #
31
36
  # @return [Packwerk::Package] the package that contains the given file,
32
37
  # or nil if the path is not owned by any component
38
+ sig do
39
+ params(
40
+ path: String,
41
+ ).returns(Packwerk::Package)
42
+ end
33
43
  def package_from_path(path)
34
44
  @packages.package_from_path(path)
35
45
  end
@@ -41,6 +51,12 @@ module Packwerk
41
51
  # @param current_namespace_path [Array<String>] (optional) The namespace of the context in which the constant is
42
52
  # used, e.g. ["Apps", "Models"] for `Apps::Models`. Defaults to [] which means top level.
43
53
  # @return [Packwerk::ConstantDiscovery::ConstantContext]
54
+ sig do
55
+ params(
56
+ const_name: String,
57
+ current_namespace_path: T.nilable(T::Array[String]),
58
+ ).returns(T.nilable(ConstantDiscovery::ConstantContext))
59
+ end
44
60
  def context_for(const_name, current_namespace_path: [])
45
61
  begin
46
62
  constant = @resolver.resolve(const_name, current_namespace_path: current_namespace_path)
@@ -55,7 +71,7 @@ module Packwerk
55
71
  constant.name,
56
72
  constant.location,
57
73
  package,
58
- package&.public_path?(constant.location),
74
+ package.public_path?(constant.location),
59
75
  )
60
76
  end
61
77
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ast"
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ast/node"
@@ -8,6 +8,9 @@ module Packwerk
8
8
  extend T::Sig
9
9
 
10
10
  class UnknownFileTypeResult < Offense
11
+ extend T::Sig
12
+
13
+ sig { params(file: String).void }
11
14
  def initialize(file:)
12
15
  super(file: file, message: "unknown file type")
13
16
  end
@@ -23,11 +26,11 @@ module Packwerk
23
26
  def initialize(node_processor_factory:, cache:, parser_factory: nil)
24
27
  @node_processor_factory = node_processor_factory
25
28
  @cache = cache
26
- @parser_factory = parser_factory || Packwerk::Parsers::Factory.instance
29
+ @parser_factory = T.let(parser_factory || Packwerk::Parsers::Factory.instance, Parsers::Factory)
27
30
  end
28
31
 
29
32
  sig do
30
- params(file_path: String).returns(
33
+ params(absolute_file: String).returns(
31
34
  T::Array[
32
35
  T.any(
33
36
  Packwerk::UnresolvedReference,
@@ -36,15 +39,15 @@ module Packwerk
36
39
  ]
37
40
  )
38
41
  end
39
- def call(file_path)
40
- return [UnknownFileTypeResult.new(file: file_path)] if parser_for(file_path).nil?
41
-
42
- @cache.with_cache(file_path) do
43
- node = parse_into_ast(file_path)
42
+ def call(absolute_file)
43
+ parser = parser_for(absolute_file)
44
+ return [UnknownFileTypeResult.new(file: absolute_file)] if T.unsafe(parser).nil?
44
45
 
46
+ @cache.with_cache(absolute_file) do
47
+ node = parse_into_ast(absolute_file, T.must(parser))
45
48
  return [] unless node
46
49
 
47
- references_from_ast(node, file_path)
50
+ references_from_ast(node, absolute_file)
48
51
  end
49
52
  rescue Parsers::ParseError => e
50
53
  [e.result]
@@ -53,24 +56,26 @@ module Packwerk
53
56
  private
54
57
 
55
58
  sig do
56
- params(node: Parser::AST::Node, file_path: String).returns(T::Array[UnresolvedReference])
59
+ params(node: Parser::AST::Node, absolute_file: String).returns(T::Array[UnresolvedReference])
57
60
  end
58
- def references_from_ast(node, file_path)
61
+ def references_from_ast(node, absolute_file)
59
62
  references = []
60
63
 
61
- node_processor = @node_processor_factory.for(filename: file_path, node: node)
64
+ node_processor = @node_processor_factory.for(absolute_file: absolute_file, node: node)
62
65
  node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
63
66
  node_visitor.visit(node, ancestors: [], result: references)
64
67
 
65
68
  references
66
69
  end
67
70
 
68
- def parse_into_ast(file_path)
69
- File.open(file_path, "r", nil, external_encoding: Encoding::UTF_8) do |file|
70
- parser_for(file_path).call(io: file, file_path: file_path)
71
+ sig { params(absolute_file: String, parser: Parsers::ParserInterface).returns(T.untyped) }
72
+ def parse_into_ast(absolute_file, parser)
73
+ File.open(absolute_file, "r", nil, external_encoding: Encoding::UTF_8) do |file|
74
+ parser.call(io: file, file_path: absolute_file)
71
75
  end
72
76
  end
73
77
 
78
+ sig { params(file_path: String).returns(T.nilable(Parsers::ParserInterface)) }
74
79
  def parser_for(file_path)
75
80
  @parser_factory.for_path(file_path)
76
81
  end