packwerk 2.1.0 → 2.1.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 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