sord 0.10.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  3. data/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
  4. data/.gitignore +0 -0
  5. data/.parlour +6 -2
  6. data/.rspec +0 -0
  7. data/.travis.yml +0 -0
  8. data/CHANGELOG.md +68 -0
  9. data/CODE_OF_CONDUCT.md +0 -0
  10. data/Gemfile +0 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +72 -44
  13. data/Rakefile +26 -7
  14. data/exe/sord +46 -6
  15. data/lib/sord.rb +1 -1
  16. data/lib/sord/generator.rb +592 -0
  17. data/lib/sord/logging.rb +0 -0
  18. data/lib/sord/parlour_plugin.rb +23 -2
  19. data/lib/sord/resolver.rb +0 -0
  20. data/lib/sord/type_converter.rb +71 -63
  21. data/lib/sord/version.rb +1 -1
  22. data/rbi/sord.rbi +211 -30
  23. data/sord.gemspec +3 -3
  24. metadata +11 -30
  25. data/lib/sord/rbi_generator.rb +0 -391
  26. data/sorbet/config +0 -0
  27. data/sorbet/rbi/gems/docile.rbi +0 -31
  28. data/sorbet/rbi/gems/parlour.rbi +0 -214
  29. data/sorbet/rbi/gems/rainbow.rbi +0 -117
  30. data/sorbet/rbi/gems/rake.rbi +0 -643
  31. data/sorbet/rbi/gems/rspec-core.rbi +0 -1658
  32. data/sorbet/rbi/gems/rspec-expectations.rbi +0 -389
  33. data/sorbet/rbi/gems/rspec-mocks.rbi +0 -823
  34. data/sorbet/rbi/gems/rspec-support.rbi +0 -268
  35. data/sorbet/rbi/gems/rspec.rbi +0 -14
  36. data/sorbet/rbi/gems/simplecov-html.rbi +0 -30
  37. data/sorbet/rbi/gems/simplecov.rbi +0 -225
  38. data/sorbet/rbi/gems/sorbet-runtime.rbi +0 -670
  39. data/sorbet/rbi/gems/yard.rbi +0 -310
  40. data/sorbet/rbi/hidden-definitions/errors.txt +0 -9285
  41. data/sorbet/rbi/hidden-definitions/hidden.rbi +0 -26604
  42. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +0 -8575
  43. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +0 -111
  44. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +0 -543
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  # Specify which files should be added to the gem when it is released.
17
17
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
18
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|sorbet)/}) }
20
20
  end
21
21
  spec.bindir = "exe"
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -24,8 +24,8 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency 'yard'
26
26
  spec.add_dependency 'sorbet-runtime'
27
- spec.add_dependency 'commander', '~> 4.4'
28
- spec.add_dependency 'parlour', '~> 0.8.0'
27
+ spec.add_dependency 'commander', '~> 4.5'
28
+ spec.add_dependency 'parlour', '~> 5.0'
29
29
 
30
30
  spec.add_development_dependency "bundler", "~> 2.0"
31
31
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Christiansen
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-14 00:00:00.000000000 Z
11
+ date: 2020-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yard
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '4.4'
47
+ version: '4.5'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '4.4'
54
+ version: '4.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: parlour
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.8.0
61
+ version: '5.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.8.0
68
+ version: '5.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -136,7 +136,7 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
- description:
139
+ description:
140
140
  email:
141
141
  - aaronc20000@gmail.com
142
142
  executables:
@@ -158,38 +158,19 @@ files:
158
158
  - Rakefile
159
159
  - exe/sord
160
160
  - lib/sord.rb
161
+ - lib/sord/generator.rb
161
162
  - lib/sord/logging.rb
162
163
  - lib/sord/parlour_plugin.rb
163
- - lib/sord/rbi_generator.rb
164
164
  - lib/sord/resolver.rb
165
165
  - lib/sord/type_converter.rb
166
166
  - lib/sord/version.rb
167
167
  - rbi/sord.rbi
168
- - sorbet/config
169
- - sorbet/rbi/gems/docile.rbi
170
- - sorbet/rbi/gems/parlour.rbi
171
- - sorbet/rbi/gems/rainbow.rbi
172
- - sorbet/rbi/gems/rake.rbi
173
- - sorbet/rbi/gems/rspec-core.rbi
174
- - sorbet/rbi/gems/rspec-expectations.rbi
175
- - sorbet/rbi/gems/rspec-mocks.rbi
176
- - sorbet/rbi/gems/rspec-support.rbi
177
- - sorbet/rbi/gems/rspec.rbi
178
- - sorbet/rbi/gems/simplecov-html.rbi
179
- - sorbet/rbi/gems/simplecov.rbi
180
- - sorbet/rbi/gems/sorbet-runtime.rbi
181
- - sorbet/rbi/gems/yard.rbi
182
- - sorbet/rbi/hidden-definitions/errors.txt
183
- - sorbet/rbi/hidden-definitions/hidden.rbi
184
- - sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi
185
- - sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi
186
- - sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi
187
168
  - sord.gemspec
188
169
  homepage: https://github.com/AaronC81/sord
189
170
  licenses:
190
171
  - MIT
191
172
  metadata: {}
192
- post_install_message:
173
+ post_install_message:
193
174
  rdoc_options: []
194
175
  require_paths:
195
176
  - lib
@@ -205,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
186
  version: '0'
206
187
  requirements: []
207
188
  rubygems_version: 3.0.3
208
- signing_key:
189
+ signing_key:
209
190
  specification_version: 4
210
191
  summary: Generate Sorbet RBI files from YARD documentation
211
192
  test_files: []
@@ -1,391 +0,0 @@
1
- # typed: true
2
- require 'yard'
3
- require 'sord/type_converter'
4
- require 'sord/logging'
5
- require 'parlour'
6
- require 'rainbow'
7
-
8
- module Sord
9
- # Converts the current working directory's YARD registry into an RBI file.
10
- class RbiGenerator
11
- # @return [Integer] The number of objects this generator has processed so
12
- # far.
13
- def object_count
14
- @namespace_count + @method_count
15
- end
16
-
17
- # @return [Array<Array(String, YARD::CodeObjects::Base, Integer)>] The
18
- # errors encountered by by the generator. Each element is of the form
19
- # [message, item, line].
20
- attr_reader :warnings
21
-
22
- # Create a new RBI generator.
23
- # @param [Hash] options
24
- # @option options [Integer] break_params
25
- # @option options [Boolean] replace_errors_with_untyped
26
- # @option options [Boolean] replace_unresolved_with_untyped
27
- # @option options [Boolean] comments
28
- # @option options [Parlour::RbiGenerator] generator
29
- # @option options [Parlour::RbiGenerator::Namespace] root
30
- # @return [void]
31
- def initialize(options)
32
- @parlour = options[:parlour] || Parlour::RbiGenerator.new
33
- @current_object = options[:root] || @parlour.root
34
-
35
- @namespace_count = 0
36
- @method_count = 0
37
- @warnings = []
38
-
39
- @replace_errors_with_untyped = options[:replace_errors_with_untyped]
40
- @replace_unresolved_with_untyped = options[:replace_unresolved_with_untyped]
41
- @keep_original_comments = options[:keep_original_comments]
42
-
43
- # Hook the logger so that messages are added as comments to the RBI file
44
- Logging.add_hook do |type, msg, item|
45
- @current_object.add_comment_to_next_child("sord #{type} - #{msg}")
46
- end if options[:sord_comments]
47
-
48
- # Hook the logger so that warnings are collected
49
- Logging.add_hook do |type, msg, item|
50
- # TODO: is it possible to get line numbers here?
51
- warnings << [msg, item, 0] if type == :warn
52
- end
53
- end
54
-
55
- # Increment the namespace counter.
56
- # @return [void]
57
- def count_namespace
58
- @namespace_count += 1
59
- end
60
-
61
- # Increment the method counter.
62
- # @return [void]
63
- def count_method
64
- @method_count += 1
65
- end
66
-
67
- # Given a YARD CodeObject, add lines defining its mixins (that is, extends
68
- # and includes) to the current RBI file. Returns the number of mixins.
69
- # @param [YARD::CodeObjects::Base] item
70
- # @return [Integer]
71
- def add_mixins(item)
72
- item.instance_mixins.reverse_each do |i|
73
- @current_object.create_include(i.path.to_s)
74
- end
75
- item.class_mixins.reverse_each do |e|
76
- @current_object.create_extend(e.path.to_s)
77
- end
78
-
79
- item.instance_mixins.length + item.class_mixins.length
80
- end
81
-
82
- # Given a YARD NamespaceObject, add lines defining constants.
83
- # @param [YARD::CodeObjects::NamespaceObject] item
84
- # @return [void]
85
- def add_constants(item)
86
- item.constants.each do |constant|
87
- # Take a constant (like "A::B::CONSTANT"), split it on each '::', and
88
- # set the constant name to the last string in the array.
89
- constant_name = constant.to_s.split('::').last
90
-
91
- # Add the constant to the current object being generated.
92
- @current_object.create_constant(constant_name, value: "T.let(#{constant.value}, T.untyped)") do |c|
93
- c.add_comments(constant.docstring.all.split("\n"))
94
- end
95
- end
96
- end
97
-
98
- # Given a YARD NamespaceObject, add lines defining its methods and their
99
- # signatures to the current RBI file.
100
- # @param [YARD::CodeObjects::NamespaceObject] item
101
- # @return [void]
102
- def add_methods(item)
103
- item.meths(inherited: false).each do |meth|
104
- count_method
105
-
106
- # If the method is an alias, skip it so we don't define it as a
107
- # separate method. Sorbet will handle it automatically.
108
- if meth.is_alias?
109
- next
110
- end
111
-
112
- # This is better than iterating over YARD's "@param" tags directly
113
- # because it includes parameters without documentation
114
- # (The gsubs allow for better splat-argument compatibility)
115
- parameter_names_and_defaults_to_tags = meth.parameters.map do |name, default|
116
- [[name, default], meth.tags('param')
117
- .find { |p| p.name&.gsub('*', '')&.gsub(':', '') == name.gsub('*', '').gsub(':', '') }]
118
- end.to_h
119
-
120
- parameter_types = parameter_names_and_defaults_to_tags.map do |name_and_default, tag|
121
- name = name_and_default.first
122
-
123
- if tag
124
- TypeConverter.yard_to_sorbet(tag.types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
125
- elsif name.start_with? '&'
126
- # Find yieldparams and yieldreturn
127
- yieldparams = meth.tags('yieldparam')
128
- yieldreturn = meth.tag('yieldreturn')&.types
129
- yieldreturn = nil if yieldreturn&.length == 1 &&
130
- yieldreturn&.first&.downcase == 'void'
131
-
132
- # Create strings
133
- params_string = yieldparams.map do |param|
134
- "#{param.name.gsub('*', '')}: #{TypeConverter.yard_to_sorbet(param.types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)}" unless param.name.nil?
135
- end.join(', ')
136
- return_string = TypeConverter.yard_to_sorbet(yieldreturn, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
137
-
138
- # Create proc types, if possible
139
- if yieldparams.empty? && yieldreturn.nil?
140
- 'T.untyped'
141
- elsif yieldreturn.nil?
142
- "T.proc#{params_string.empty? ? '' : ".params(#{params_string})"}.void"
143
- else
144
- "T.proc#{params_string.empty? ? '' : ".params(#{params_string})"}.returns(#{return_string})"
145
- end
146
- elsif meth.path.end_with? '='
147
- # Look for the matching getter method
148
- getter_path = meth.path[0...-1]
149
- getter = item.meths.find { |m| m.path == getter_path }
150
-
151
- unless getter
152
- if parameter_names_and_defaults_to_tags.length == 1 \
153
- && meth.tags('param').length == 1 \
154
- && meth.tag('param').types
155
-
156
- Logging.infer("argument name in single @param inferred as #{parameter_names_and_defaults_to_tags.first.first.first.inspect}", meth)
157
- next TypeConverter.yard_to_sorbet(meth.tag('param').types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
158
- else
159
- Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth)
160
- next 'T.untyped'
161
- end
162
- end
163
-
164
- return_types = getter.tags('return').flat_map(&:types)
165
- unless return_types.any?
166
- Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth)
167
- next 'T.untyped'
168
- end
169
- inferred_type = TypeConverter.yard_to_sorbet(
170
- return_types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
171
-
172
- Logging.infer("inferred type of parameter #{name.inspect} as #{inferred_type} using getter's return type", meth)
173
- inferred_type
174
- else
175
- # Is this the only argument, and was a @param specified without an
176
- # argument name? If so, infer it
177
- if parameter_names_and_defaults_to_tags.length == 1 \
178
- && meth.tags('param').length == 1 \
179
- && meth.tag('param').types
180
-
181
- Logging.infer("argument name in single @param inferred as #{parameter_names_and_defaults_to_tags.first.first.first.inspect}", meth)
182
- TypeConverter.yard_to_sorbet(meth.tag('param').types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
183
- else
184
- Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth)
185
- 'T.untyped'
186
- end
187
- end
188
- end
189
-
190
- return_tags = meth.tags('return')
191
- returns = if return_tags.length == 0
192
- Logging.omit("no YARD return type given, using T.untyped", meth)
193
- 'T.untyped'
194
- elsif return_tags.length == 1 && return_tags&.first&.types&.first&.downcase == "void"
195
- nil
196
- else
197
- TypeConverter.yard_to_sorbet(meth.tag('return').types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped)
198
- end
199
-
200
- parlour_params = parameter_names_and_defaults_to_tags
201
- .zip(parameter_types)
202
- .map do |((name, default), _), type|
203
- # If the default is "nil" but the type is not nilable, then it
204
- # should become nilable
205
- # (T.untyped can include nil, so don't alter that)
206
- type = "T.nilable(#{type})" \
207
- if default == 'nil' && !type.start_with?('T.nilable') && type != 'T.untyped'
208
- Parlour::RbiGenerator::Parameter.new(
209
- name.to_s,
210
- type: type,
211
- default: default
212
- )
213
- end
214
-
215
- @current_object.create_method(
216
- meth.name.to_s,
217
- parameters: parlour_params,
218
- returns: returns,
219
- class_method: meth.scope == :class
220
- ) do |m|
221
- if @keep_original_comments
222
- m.add_comments(meth.docstring.all.split("\n"))
223
- else
224
- parser = YARD::Docstring.parser
225
- parser.parse(meth.docstring.all)
226
-
227
- docs_array = parser.text.split("\n")
228
-
229
- # Add @param tags if there are any with names and descriptions.
230
- params = parser.tags.select { |tag| tag.tag_name == 'param' && tag.is_a?(YARD::Tags::Tag) && !tag.name.nil? }
231
- # Add a blank line if there's anything before the params.
232
- docs_array << '' if docs_array.length.positive? && params.length.positive?
233
- params.each do |param|
234
- docs_array << '' if docs_array.last != '' && docs_array.length.positive?
235
- # Output params in the form of:
236
- # _@param_ `foo` — Lorem ipsum.
237
- # _@param_ `foo`
238
- if param.text.nil?
239
- docs_array << "_@param_ `#{param.name}`"
240
- else
241
- docs_array << "_@param_ `#{param.name}` — #{param.text}"
242
- end
243
- end
244
-
245
- # Add @return tags (there could possibly be more than one, despite this not being supported)
246
- returns = parser.tags.select { |tag| tag.tag_name == 'return' && tag.is_a?(YARD::Tags::Tag) && !tag.text.nil? && tag.text.strip != '' }
247
- # Add a blank line if there's anything before the returns.
248
- docs_array << '' if docs_array.length.positive? && returns.length.positive?
249
- returns.each do |retn|
250
- docs_array << '' if docs_array.last != '' && docs_array.length.positive?
251
- # Output returns in the form of:
252
- # _@return_ — Lorem ipsum.
253
- docs_array << "_@return_ — #{retn.text}"
254
- end
255
-
256
- # Iterate through the @example tags for a given YARD doc and output them in Markdown codeblocks.
257
- examples = parser.tags.select { |tag| tag.tag_name == 'example' && tag.is_a?(YARD::Tags::Tag) }
258
- examples.each do |example|
259
- # Only add a blank line if there's anything before the example.
260
- docs_array << '' if docs_array.length.positive?
261
- # Include the example's 'name' if there is one.
262
- docs_array << example.name unless example.name.nil? || example.name == ""
263
- docs_array << "```ruby"
264
- docs_array.concat(example.text.split("\n"))
265
- docs_array << "```"
266
- end if examples.length.positive?
267
-
268
- # Add @note and @deprecated tags.
269
- notice_tags = parser.tags.select { |tag| ['note', 'deprecated'].include?(tag.tag_name) && tag.is_a?(YARD::Tags::Tag) }
270
- # Add a blank line if there's anything before the params.
271
- docs_array << '' if docs_array.last != '' && docs_array.length.positive? && notice_tags.length.positive?
272
- notice_tags.each do |notice_tag|
273
- docs_array << '' if docs_array.last != ''
274
- # Output note/deprecated/see in the form of:
275
- # _@note_ — Lorem ipsum.
276
- # _@note_
277
- if notice_tag.text.nil?
278
- docs_array << "_@#{notice_tag.tag_name}_"
279
- else
280
- docs_array << "_@#{notice_tag.tag_name}_ — #{notice_tag.text}"
281
- end
282
- end
283
-
284
- # Add @see tags.
285
- see_tags = parser.tags.select { |tag| tag.tag_name == 'see' && tag.is_a?(YARD::Tags::Tag) }
286
- # Add a blank line if there's anything before the params.
287
- docs_array << '' if docs_array.last != '' && docs_array.length.positive? && see_tags.length.positive?
288
- see_tags.each do |see_tag|
289
- docs_array << '' if docs_array.last != ''
290
- # Output note/deprecated/see in the form of:
291
- # _@see_ `B` — Lorem ipsum.
292
- # _@see_ `B`
293
- if see_tag.text.nil?
294
- docs_array << "_@see_ `#{see_tag.name}`"
295
- else
296
- docs_array << "_@see_ `#{see_tag.name}` — #{see_tag.text}"
297
- end
298
- end
299
-
300
- m.add_comments(docs_array)
301
- end
302
- end
303
- end
304
- end
305
-
306
- # Given a YARD NamespaceObject, add lines defining its mixins, methods
307
- # and children to the RBI file.
308
- # @param [YARD::CodeObjects::NamespaceObject] item
309
- # @return [void]
310
- def add_namespace(item)
311
- count_namespace
312
-
313
- superclass = nil
314
- superclass = item.superclass.path.to_s if item.type == :class && item.superclass.to_s != "Object"
315
-
316
- parent = @current_object
317
- @current_object = item.type == :class \
318
- ? parent.create_class(item.name.to_s, superclass: superclass)
319
- : parent.create_module(item.name.to_s)
320
- @current_object.add_comments(item.docstring.all.split("\n"))
321
-
322
- add_mixins(item)
323
- add_methods(item)
324
- add_constants(item)
325
-
326
- item.children.select { |x| [:class, :module].include?(x.type) }
327
- .each { |child| add_namespace(child) }
328
-
329
- @current_object = parent
330
- end
331
-
332
- # Populates the RBI generator with the contents of the YARD registry. You
333
- # must load the YARD registry first!
334
- # @return [void]
335
- def populate
336
- # Generate top-level modules, which recurses to all modules
337
- YARD::Registry.root.children
338
- .select { |x| [:class, :module].include?(x.type) }
339
- .each { |child| add_namespace(child) }
340
- end
341
-
342
- # Populates the RBI generator with the contents of the YARD registry, then
343
- # uses the loaded Parlour::RbiGenerator to generate the RBI file. You must
344
- # load the YARD registry first!
345
- # @return [void]
346
- def generate
347
- populate
348
- @parlour.rbi
349
- end
350
-
351
- # Loads the YARD registry, populates the RBI file, and prints any relevant
352
- # final logs.
353
- # @return [void]
354
- def run
355
- # Get YARD ready
356
- YARD::Registry.load!
357
-
358
- # Populate the RBI
359
- populate
360
-
361
- if object_count.zero?
362
- Logging.warn("No objects processed.")
363
- Logging.warn("Have you definitely generated the YARD documentation for this project?")
364
- Logging.warn("Run `yard` to generate docs.")
365
- end
366
-
367
- Logging.done("Processed #{object_count} objects (#{@namespace_count} namespaces and #{@method_count} methods)")
368
-
369
- Logging.hooks.clear
370
-
371
- unless warnings.empty?
372
- Logging.warn("There were #{warnings.length} important warnings in the RBI file, listed below.")
373
- if @replace_errors_with_untyped
374
- Logging.warn("The types which caused them have been replaced with T.untyped.")
375
- else
376
- Logging.warn("The types which caused them have been replaced with SORD_ERROR_ constants.")
377
- end
378
- Logging.warn("Please edit the file to fix these errors.")
379
- Logging.warn("Alternatively, edit your YARD documentation so that your types are valid and re-run Sord.")
380
- warnings.each do |(msg, item, _)|
381
- puts " (#{Rainbow(item&.path).bold}) #{msg}"
382
- end
383
- end
384
- rescue
385
- Logging.error($!)
386
- $@.each do |line|
387
- puts " #{line}"
388
- end
389
- end
390
- end
391
- end