tapioca 0.6.4 → 0.7.2

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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -2
  3. data/README.md +27 -15
  4. data/Rakefile +10 -14
  5. data/lib/tapioca/cli.rb +65 -80
  6. data/lib/tapioca/{generators/base.rb → commands/command.rb} +16 -9
  7. data/lib/tapioca/{generators → commands}/dsl.rb +59 -45
  8. data/lib/tapioca/{generators → commands}/gem.rb +93 -30
  9. data/lib/tapioca/{generators → commands}/init.rb +9 -13
  10. data/lib/tapioca/{generators → commands}/require.rb +8 -10
  11. data/lib/tapioca/commands/todo.rb +86 -0
  12. data/lib/tapioca/commands.rb +13 -0
  13. data/lib/tapioca/dsl/compiler.rb +185 -0
  14. data/lib/tapioca/{compilers/dsl → dsl/compilers}/aasm.rb +12 -9
  15. data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_controller_helpers.rb +13 -20
  16. data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_mailer.rb +10 -8
  17. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_job.rb +11 -9
  18. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_attributes.rb +13 -11
  19. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_secure_password.rb +10 -12
  20. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_associations.rb +28 -34
  21. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +18 -16
  22. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
  23. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +12 -8
  24. data/lib/tapioca/dsl/compilers/active_record_relations.rb +712 -0
  25. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_scope.rb +21 -20
  26. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_typed_store.rb +11 -16
  27. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
  28. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +14 -10
  29. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_concern.rb +19 -14
  30. data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_current_attributes.rb +16 -21
  31. data/lib/tapioca/{compilers/dsl → dsl/compilers}/config.rb +11 -9
  32. data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
  33. data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +23 -22
  34. data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
  35. data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +22 -10
  36. data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +12 -13
  37. data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
  38. data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +11 -9
  39. data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
  40. data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +20 -15
  41. data/lib/tapioca/dsl/compilers.rb +31 -0
  42. data/lib/tapioca/{compilers/dsl → dsl}/extensions/frozen_record.rb +2 -2
  43. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +114 -0
  44. data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +29 -0
  45. data/lib/tapioca/{compilers/dsl → dsl/helpers}/param_helper.rb +6 -3
  46. data/lib/tapioca/dsl/pipeline.rb +169 -0
  47. data/lib/tapioca/gem/events.rb +120 -0
  48. data/lib/tapioca/gem/listeners/base.rb +48 -0
  49. data/lib/tapioca/gem/listeners/dynamic_mixins.rb +32 -0
  50. data/lib/tapioca/gem/listeners/methods.rb +183 -0
  51. data/lib/tapioca/gem/listeners/mixins.rb +101 -0
  52. data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +21 -0
  53. data/lib/tapioca/gem/listeners/sorbet_enums.rb +26 -0
  54. data/lib/tapioca/gem/listeners/sorbet_helpers.rb +29 -0
  55. data/lib/tapioca/gem/listeners/sorbet_props.rb +33 -0
  56. data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +23 -0
  57. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +79 -0
  58. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +51 -0
  59. data/lib/tapioca/gem/listeners/subconstants.rb +37 -0
  60. data/lib/tapioca/gem/listeners/yard_doc.rb +96 -0
  61. data/lib/tapioca/gem/listeners.rb +16 -0
  62. data/lib/tapioca/gem/pipeline.rb +365 -0
  63. data/lib/tapioca/helpers/cli_helper.rb +7 -0
  64. data/lib/tapioca/helpers/config_helper.rb +5 -8
  65. data/lib/tapioca/helpers/shims_helper.rb +87 -0
  66. data/lib/tapioca/helpers/signatures_helper.rb +17 -0
  67. data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
  68. data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
  69. data/lib/tapioca/helpers/test/isolation.rb +1 -1
  70. data/lib/tapioca/helpers/test/template.rb +13 -2
  71. data/lib/tapioca/helpers/type_variable_helper.rb +43 -0
  72. data/lib/tapioca/internal.rb +18 -10
  73. data/lib/tapioca/rbi_ext/model.rb +14 -50
  74. data/lib/tapioca/rbi_formatter.rb +37 -0
  75. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +227 -0
  76. data/lib/tapioca/runtime/generic_type_registry.rb +168 -0
  77. data/lib/tapioca/runtime/loader.rb +123 -0
  78. data/lib/tapioca/runtime/reflection.rb +157 -0
  79. data/lib/tapioca/runtime/trackers/autoload.rb +72 -0
  80. data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -0
  81. data/lib/tapioca/runtime/trackers/mixin.rb +80 -0
  82. data/lib/tapioca/runtime/trackers/required_ancestor.rb +50 -0
  83. data/lib/tapioca/{trackers.rb → runtime/trackers.rb} +4 -3
  84. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +69 -34
  85. data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
  86. data/lib/tapioca/{compilers → static}/requires_compiler.rb +2 -2
  87. data/lib/tapioca/static/symbol_loader.rb +83 -0
  88. data/lib/tapioca/static/symbol_table_parser.rb +63 -0
  89. data/lib/tapioca/version.rb +1 -1
  90. data/lib/tapioca.rb +2 -7
  91. metadata +83 -62
  92. data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -720
  93. data/lib/tapioca/compilers/dsl/base.rb +0 -195
  94. data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
  95. data/lib/tapioca/compilers/dsl_compiler.rb +0 -134
  96. data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -223
  97. data/lib/tapioca/compilers/sorbet.rb +0 -59
  98. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
  99. data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
  100. data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
  101. data/lib/tapioca/compilers/todos_compiler.rb +0 -32
  102. data/lib/tapioca/generators/todo.rb +0 -76
  103. data/lib/tapioca/generators.rb +0 -9
  104. data/lib/tapioca/generic_type_registry.rb +0 -164
  105. data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -108
  106. data/lib/tapioca/loader.rb +0 -119
  107. data/lib/tapioca/reflection.rb +0 -151
  108. data/lib/tapioca/trackers/autoload.rb +0 -70
  109. data/lib/tapioca/trackers/constant_definition.rb +0 -42
  110. data/lib/tapioca/trackers/mixin.rb +0 -78
@@ -2,8 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- module Generators
6
- class Dsl < Base
5
+ module Commands
6
+ class Dsl < Command
7
7
  sig do
8
8
  params(
9
9
  requested_constants: T::Array[String],
@@ -13,12 +13,11 @@ module Tapioca
13
13
  file_header: T::Boolean,
14
14
  compiler_path: String,
15
15
  tapioca_path: String,
16
- default_command: String,
17
- file_writer: Thor::Actions,
18
16
  should_verify: T::Boolean,
19
17
  quiet: T::Boolean,
20
18
  verbose: T::Boolean,
21
19
  number_of_workers: T.nilable(Integer),
20
+ rbi_formatter: RBIFormatter
22
21
  ).void
23
22
  end
24
23
  def initialize(
@@ -29,12 +28,11 @@ module Tapioca
29
28
  file_header:,
30
29
  compiler_path:,
31
30
  tapioca_path:,
32
- default_command:,
33
- file_writer: FileWriter.new,
34
31
  should_verify: false,
35
32
  quiet: false,
36
33
  verbose: false,
37
- number_of_workers: nil
34
+ number_of_workers: nil,
35
+ rbi_formatter: DEFAULT_RBI_FORMATTER
38
36
  )
39
37
  @requested_constants = requested_constants
40
38
  @outpath = outpath
@@ -47,18 +45,19 @@ module Tapioca
47
45
  @quiet = quiet
48
46
  @verbose = verbose
49
47
  @number_of_workers = number_of_workers
48
+ @rbi_formatter = rbi_formatter
50
49
 
51
- super(default_command: default_command, file_writer: file_writer)
50
+ super()
52
51
 
53
- @loader = T.let(nil, T.nilable(Loader))
52
+ @loader = T.let(nil, T.nilable(Runtime::Loader))
54
53
  end
55
54
 
56
55
  sig { override.void }
57
- def generate
56
+ def execute
58
57
  load_dsl_extensions
59
58
  load_application(eager_load: @requested_constants.empty?)
60
59
  abort_if_pending_migrations!
61
- load_dsl_generators
60
+ load_dsl_compilers
62
61
 
63
62
  if @should_verify
64
63
  say("Checking for out-of-date RBIs...")
@@ -70,18 +69,18 @@ module Tapioca
70
69
  outpath = @should_verify ? Pathname.new(Dir.mktmpdir) : @outpath
71
70
  rbi_files_to_purge = existing_rbi_filenames(@requested_constants)
72
71
 
73
- compiler = Compilers::DslCompiler.new(
72
+ pipeline = Tapioca::Dsl::Pipeline.new(
74
73
  requested_constants: constantize(@requested_constants),
75
- requested_generators: constantize_generators(@only),
76
- excluded_generators: constantize_generators(@exclude),
74
+ requested_compilers: constantize_compilers(@only),
75
+ excluded_compilers: constantize_compilers(@exclude),
77
76
  error_handler: ->(error) {
78
77
  say_error(error, :bold, :red)
79
78
  },
80
79
  number_of_workers: @number_of_workers
81
80
  )
82
81
 
83
- processed_files = compiler.run do |constant, contents|
84
- constant_name = T.must(Reflection.name_of(constant))
82
+ processed_files = pipeline.run do |constant, contents|
83
+ constant_name = T.must(Tapioca::Runtime::Reflection.name_of(constant))
85
84
 
86
85
  if @verbose && !@quiet
87
86
  say_status(:processing, constant_name, :yellow)
@@ -91,7 +90,7 @@ module Tapioca
91
90
  constant_name,
92
91
  contents,
93
92
  outpath: outpath,
94
- quiet: @should_verify || @quiet && !@verbose
93
+ quiet: @should_verify || (@quiet && !@verbose)
95
94
  )
96
95
  end
97
96
 
@@ -137,14 +136,15 @@ module Tapioca
137
136
  end
138
137
 
139
138
  sig { void }
140
- def load_dsl_generators
141
- say("Loading DSL generator classes... ")
139
+ def load_dsl_compilers
140
+ say("Loading DSL compiler classes... ")
142
141
 
143
142
  Dir.glob([
144
143
  "#{@compiler_path}/*.rb",
145
- "#{@tapioca_path}/generators/**/*.rb",
146
- ]).each do |generator|
147
- require File.expand_path(generator)
144
+ "#{@tapioca_path}/generators/**/*.rb", # TODO: Here for backcompat, remove later
145
+ "#{@tapioca_path}/compilers/**/*.rb",
146
+ ]).each do |compiler|
147
+ require File.expand_path(compiler)
148
148
  end
149
149
 
150
150
  say("Done", :green)
@@ -165,11 +165,11 @@ module Tapioca
165
165
 
166
166
  sig { params(constant_names: T::Array[String]).returns(T::Array[Module]) }
167
167
  def constantize(constant_names)
168
- constant_map = constant_names.map do |name|
168
+ constant_map = constant_names.to_h do |name|
169
169
  [name, Object.const_get(name)]
170
170
  rescue NameError
171
171
  [name, nil]
172
- end.to_h
172
+ end
173
173
 
174
174
  unprocessable_constants = constant_map.select { |_, v| v.nil? }
175
175
  unless unprocessable_constants.empty?
@@ -185,22 +185,35 @@ module Tapioca
185
185
  constant_map.values
186
186
  end
187
187
 
188
- sig { params(generator_names: T::Array[String]).returns(T::Array[T.class_of(Compilers::Dsl::Base)]) }
189
- def constantize_generators(generator_names)
190
- generator_map = generator_names.to_h do |name|
191
- [name, Compilers::Dsl::Base.resolve(name)]
188
+ sig { params(compiler_names: T::Array[String]).returns(T::Array[T.class_of(Tapioca::Dsl::Compiler)]) }
189
+ def constantize_compilers(compiler_names)
190
+ compiler_map = compiler_names.to_h do |name|
191
+ [name, resolve(name)]
192
192
  end
193
193
 
194
- unprocessable_generators = generator_map.select { |_, v| v.nil? }
195
- unless unprocessable_generators.empty?
196
- unprocessable_generators.each do |name, _|
197
- say("Error: Cannot find generator '#{name}'", :red)
194
+ unprocessable_compilers = compiler_map.select { |_, v| v.nil? }
195
+ unless unprocessable_compilers.empty?
196
+ unprocessable_compilers.each do |name, _|
197
+ say("Error: Cannot find compiler '#{name}'", :red)
198
198
  end
199
199
 
200
200
  exit(1)
201
201
  end
202
202
 
203
- T.cast(generator_map.values, T::Array[T.class_of(Compilers::Dsl::Base)])
203
+ T.cast(compiler_map.values, T::Array[T.class_of(Tapioca::Dsl::Compiler)])
204
+ end
205
+
206
+ sig { params(name: String).returns(T.nilable(T.class_of(Tapioca::Dsl::Compiler))) }
207
+ def resolve(name)
208
+ # Try to find built-in tapioca compiler first, then globally defined compiler.
209
+ potentials = Tapioca::Dsl::Compilers::NAMESPACES.map do |namespace|
210
+ Object.const_get(namespace + name)
211
+ rescue NameError
212
+ # Skip if we can't find compiler by the potential name
213
+ nil
214
+ end
215
+
216
+ potentials.compact.first
204
217
  end
205
218
 
206
219
  sig do
@@ -216,13 +229,14 @@ module Tapioca
216
229
 
217
230
  filename = outpath / rbi_filename_for(constant_name)
218
231
 
219
- rbi.set_file_header(
232
+ @rbi_formatter.write_header!(
233
+ rbi,
220
234
  generate_command_for(constant_name),
221
- reason: "dynamic methods in `#{constant_name}`",
222
- display_heading: @file_header
223
- )
235
+ reason: "dynamic methods in `#{constant_name}`"
236
+ ) if @file_header
224
237
 
225
- create_file(filename, rbi.transformed_string, verbose: !quiet)
238
+ rbi_string = @rbi_formatter.print_file(rbi)
239
+ create_file(filename, rbi_string, verbose: !quiet)
226
240
 
227
241
  filename
228
242
  end
@@ -231,7 +245,7 @@ module Tapioca
231
245
  def perform_dsl_verification(dir)
232
246
  diff = verify_dsl_rbi(tmp_dir: dir)
233
247
 
234
- report_diff_and_exit_if_out_of_date(diff, "dsl")
248
+ report_diff_and_exit_if_out_of_date(diff, :dsl)
235
249
  ensure
236
250
  FileUtils.remove_entry(dir)
237
251
  end
@@ -294,13 +308,13 @@ module Tapioca
294
308
  " File(s) #{cause}:\n - #{filenames}"
295
309
  end
296
310
 
297
- sig { params(diff: T::Hash[String, Symbol], command: String).void }
311
+ sig { params(diff: T::Hash[String, Symbol], command: Symbol).void }
298
312
  def report_diff_and_exit_if_out_of_date(diff, command)
299
313
  if diff.empty?
300
314
  say("Nothing to do, all RBIs are up-to-date.")
301
315
  else
302
316
  say("RBI files are out-of-date. In your development environment, please run:", :green)
303
- say(" `#{@default_command} #{command}`", [:green, :bold])
317
+ say(" `#{default_command(command)}`", [:green, :bold])
304
318
  say("Once it is complete, be sure to commit and push any changes", :green)
305
319
 
306
320
  say("")
@@ -321,9 +335,9 @@ module Tapioca
321
335
  end.sort
322
336
  end
323
337
 
324
- sig { returns(Loader) }
338
+ sig { returns(Runtime::Loader) }
325
339
  def loader
326
- @loader ||= Loader.new
340
+ @loader ||= Runtime::Loader.new
327
341
  end
328
342
 
329
343
  sig { params(class_name: String).returns(String) }
@@ -345,12 +359,12 @@ module Tapioca
345
359
 
346
360
  sig { params(constant: String).returns(String) }
347
361
  def generate_command_for(constant)
348
- "#{@default_command} dsl #{constant}"
362
+ default_command(:dsl, constant)
349
363
  end
350
364
 
351
365
  sig { void }
352
366
  def load_dsl_extensions
353
- Dir["#{__dir__}/../compilers/dsl/extensions/*.rb"].sort.each { |f| require(f) }
367
+ Dir["#{__dir__}/../dsl/extensions/*.rb"].sort.each { |f| require(f) }
354
368
  end
355
369
  end
356
370
  end
@@ -2,8 +2,10 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- module Generators
6
- class Gem < Base
5
+ module Commands
6
+ class Gem < Command
7
+ include SorbetHelper
8
+
7
9
  sig do
8
10
  params(
9
11
  gem_names: T::Array[String],
@@ -11,13 +13,14 @@ module Tapioca
11
13
  prerequire: T.nilable(String),
12
14
  postrequire: String,
13
15
  typed_overrides: T::Hash[String, String],
14
- default_command: String,
15
16
  outpath: Pathname,
16
17
  file_header: T::Boolean,
17
18
  doc: T::Boolean,
18
19
  include_exported_rbis: T::Boolean,
19
- file_writer: Thor::Actions,
20
- number_of_workers: T.nilable(Integer)
20
+ number_of_workers: T.nilable(Integer),
21
+ auto_strictness: T::Boolean,
22
+ dsl_dir: String,
23
+ rbi_formatter: RBIFormatter
21
24
  ).void
22
25
  end
23
26
  def initialize(
@@ -26,13 +29,14 @@ module Tapioca
26
29
  prerequire:,
27
30
  postrequire:,
28
31
  typed_overrides:,
29
- default_command:,
30
32
  outpath:,
31
33
  file_header:,
32
34
  doc:,
33
35
  include_exported_rbis:,
34
- file_writer: FileWriter.new,
35
- number_of_workers: nil
36
+ number_of_workers: nil,
37
+ auto_strictness: true,
38
+ dsl_dir: DEFAULT_DSL_DIR,
39
+ rbi_formatter: DEFAULT_RBI_FORMATTER
36
40
  )
37
41
  @gem_names = gem_names
38
42
  @exclude = exclude
@@ -42,10 +46,13 @@ module Tapioca
42
46
  @outpath = outpath
43
47
  @file_header = file_header
44
48
  @number_of_workers = number_of_workers
49
+ @auto_strictness = auto_strictness
50
+ @dsl_dir = dsl_dir
51
+ @rbi_formatter = rbi_formatter
45
52
 
46
- super(default_command: default_command, file_writer: file_writer)
53
+ super()
47
54
 
48
- @loader = T.let(nil, T.nilable(Loader))
55
+ @loader = T.let(nil, T.nilable(Runtime::Loader))
49
56
  @bundle = T.let(nil, T.nilable(Gemfile))
50
57
  @existing_rbis = T.let(nil, T.nilable(T::Hash[String, String]))
51
58
  @expected_rbis = T.let(nil, T.nilable(T::Hash[String, String]))
@@ -54,7 +61,7 @@ module Tapioca
54
61
  end
55
62
 
56
63
  sig { override.void }
57
- def generate
64
+ def execute
58
65
  require_gem_file
59
66
 
60
67
  gem_queue = gems_to_generate(@gem_names).reject { |gem| @exclude.include?(gem.name) }
@@ -71,6 +78,8 @@ module Tapioca
71
78
  end
72
79
 
73
80
  if anything_done
81
+ update_strictnesses(gem_queue.map(&:name), gem_dir: @outpath.to_s, dsl_dir: @dsl_dir) if @auto_strictness
82
+
74
83
  say("All operations performed in working directory.", [:green, :bold])
75
84
  say("Please review changes and commit them.", [:green, :bold])
76
85
  else
@@ -93,6 +102,8 @@ module Tapioca
93
102
  ].any?
94
103
 
95
104
  if anything_done
105
+ update_strictnesses([], gem_dir: @outpath.to_s, dsl_dir: @dsl_dir) if @auto_strictness
106
+
96
107
  say("All operations performed in working directory.", [:green, :bold])
97
108
  say("Please review changes and commit them.", [:green, :bold])
98
109
  else
@@ -104,9 +115,9 @@ module Tapioca
104
115
 
105
116
  private
106
117
 
107
- sig { returns(Loader) }
118
+ sig { returns(Runtime::Loader) }
108
119
  def loader
109
- @loader ||= Loader.new
120
+ @loader ||= Runtime::Loader.new
110
121
  end
111
122
 
112
123
  sig { returns(Gemfile) }
@@ -124,7 +135,7 @@ module Tapioca
124
135
  exit(1)
125
136
  end
126
137
 
127
- Tapioca::Trackers::Autoload.eager_load_all!
138
+ Runtime::Trackers::Autoload.eager_load_all!
128
139
 
129
140
  say(" Done", :green)
130
141
  unless bundle.missing_specs.empty?
@@ -153,24 +164,24 @@ module Tapioca
153
164
  gem_name = set_color(gem.name, :yellow, :bold)
154
165
 
155
166
  rbi = RBI::File.new(strictness: @typed_overrides[gem.name] || "true")
156
- rbi.set_file_header(
157
- "#{@default_command} gem #{gem.name}",
158
- reason: "types exported from the `#{gem.name}` gem",
159
- display_heading: @file_header
160
- )
161
167
 
162
- Compilers::SymbolTableCompiler.new.compile(gem, rbi, 0, @doc)
168
+ @rbi_formatter.write_header!(rbi,
169
+ default_command(:gem, gem.name),
170
+ reason: "types exported from the `#{gem.name}` gem",) if @file_header
171
+
172
+ rbi.root = Tapioca::Gem::Pipeline.new(gem, include_doc: @doc).compile
163
173
 
164
174
  merge_with_exported_rbi(gem, rbi) if @include_exported_rbis
165
175
 
166
176
  if rbi.empty?
167
- rbi.set_empty_body_content
177
+ @rbi_formatter.write_empty_body_comment!(rbi)
168
178
  say("Compiled #{gem_name} (empty output)", :yellow)
169
179
  else
170
180
  say("Compiled #{gem_name}", :green)
171
181
  end
172
182
 
173
- create_file(@outpath / gem.rbi_file_name, rbi.transformed_string)
183
+ rbi_string = @rbi_formatter.print_file(rbi)
184
+ create_file(@outpath / gem.rbi_file_name, rbi_string)
174
185
 
175
186
  T.unsafe(Pathname).glob((@outpath / "#{gem.name}@*.rbi").to_s) do |file|
176
187
  remove_file(file) unless file.basename.to_s == gem.rbi_file_name
@@ -191,7 +202,7 @@ module Tapioca
191
202
  diff[filename] = gem_rbi_exists?(gem_name) ? :changed : :added
192
203
  end
193
204
 
194
- report_diff_and_exit_if_out_of_date(diff, "gem")
205
+ report_diff_and_exit_if_out_of_date(diff, :gem)
195
206
  end
196
207
 
197
208
  sig { void }
@@ -265,7 +276,7 @@ module Tapioca
265
276
  say_error("If you populated ", :yellow)
266
277
  say_error("#{file} ", :bold, :blue)
267
278
  say_error("with ", :yellow)
268
- say_error("`#{@default_command} require`", :bold, :blue)
279
+ say_error("`#{default_command(:require)}`", :bold, :blue)
269
280
  say_error("you should probably review it and remove the faulty line.", :yellow)
270
281
  end
271
282
 
@@ -296,13 +307,13 @@ module Tapioca
296
307
  existing_rbis.key?(gem_name)
297
308
  end
298
309
 
299
- sig { params(diff: T::Hash[String, Symbol], command: String).void }
310
+ sig { params(diff: T::Hash[String, Symbol], command: Symbol).void }
300
311
  def report_diff_and_exit_if_out_of_date(diff, command)
301
312
  if diff.empty?
302
313
  say("Nothing to do, all RBIs are up-to-date.")
303
314
  else
304
315
  say("RBI files are out-of-date. In your development environment, please run:", :green)
305
- say(" `#{@default_command} #{command}`", [:green, :bold])
316
+ say(" `#{default_command(command)}`", [:green, :bold])
306
317
  say("Once it is complete, be sure to commit and push any changes", :green)
307
318
 
308
319
  say("")
@@ -325,16 +336,14 @@ module Tapioca
325
336
  sig { returns(T::Hash[String, String]) }
326
337
  def existing_rbis
327
338
  @existing_rbis ||= Pathname.glob((@outpath / "*@*.rbi").to_s)
328
- .map { |f| T.cast(f.basename(".*").to_s.split("@", 2), [String, String]) }
329
- .to_h
339
+ .to_h { |f| T.cast(f.basename(".*").to_s.split("@", 2), [String, String]) }
330
340
  end
331
341
 
332
342
  sig { returns(T::Hash[String, String]) }
333
343
  def expected_rbis
334
344
  @expected_rbis ||= bundle.dependencies
335
345
  .reject { |gem| @exclude.include?(gem.name) }
336
- .map { |gem| [gem.name, gem.version.to_s] }
337
- .to_h
346
+ .to_h { |gem| [gem.name, gem.version.to_s] }
338
347
  end
339
348
 
340
349
  sig { params(gem_name: String, version: String).returns(Pathname) }
@@ -370,6 +379,60 @@ module Tapioca
370
379
  say_error("\n\n RBIs exported by `#{gem.name}` contain errors and can't be used:", :yellow)
371
380
  say_error("Cause: #{e.message} (#{e.location})")
372
381
  end
382
+
383
+ sig { params(gem_names: T::Array[String], gem_dir: String, dsl_dir: String).void }
384
+ def update_strictnesses(gem_names, gem_dir: DEFAULT_GEM_DIR, dsl_dir: DEFAULT_DSL_DIR)
385
+ return unless File.directory?(dsl_dir)
386
+
387
+ error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
388
+
389
+ say("Typechecking RBI files... ")
390
+ res = sorbet(
391
+ "--no-config",
392
+ "--error-url-base=#{error_url_base}",
393
+ "--isolate-error-code 4010",
394
+ dsl_dir,
395
+ gem_dir
396
+ )
397
+ say(" Done", :green)
398
+
399
+ errors = Spoom::Sorbet::Errors::Parser.parse_string(res.err)
400
+
401
+ if errors.empty?
402
+ say("No error found", [:green, :bold])
403
+ return
404
+ end
405
+
406
+ files = []
407
+
408
+ errors.each do |error|
409
+ # Collect the file with error
410
+ files << error.file
411
+ error.more.each do |line|
412
+ # Also collect the conflicting definition file paths
413
+ next unless line.include?("Previous definition")
414
+ files << line.split(":").first&.strip
415
+ end
416
+ end
417
+
418
+ files
419
+ .uniq
420
+ .sort
421
+ .select do |file|
422
+ name = gem_name_from_rbi_path(file)
423
+ file.start_with?(gem_dir) && (gem_names.empty? || gem_names.include?(name))
424
+ end.each do |file|
425
+ Spoom::Sorbet::Sigils.change_sigil_in_file(file, "false")
426
+ say("\n Changed strictness of #{file} to `typed: false` (conflicting with DSL files)", [:yellow, :bold])
427
+ end
428
+
429
+ say("\n")
430
+ end
431
+
432
+ sig { params(path: String).returns(String) }
433
+ def gem_name_from_rbi_path(path)
434
+ T.must(File.basename(path, ".rbi").split("@").first)
435
+ end
373
436
  end
374
437
  end
375
438
  end
@@ -2,36 +2,32 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- module Generators
6
- class Init < Base
5
+ module Commands
6
+ class Init < Command
7
7
  sig do
8
8
  params(
9
9
  sorbet_config: String,
10
10
  tapioca_config: String,
11
- default_postrequire: String,
12
- default_command: String,
13
- file_writer: Thor::Actions
11
+ default_postrequire: String
14
12
  ).void
15
13
  end
16
14
  def initialize(
17
15
  sorbet_config:,
18
16
  tapioca_config:,
19
- default_postrequire:,
20
- default_command:,
21
- file_writer: FileWriter.new
17
+ default_postrequire:
22
18
  )
23
19
  @sorbet_config = sorbet_config
24
20
  @tapioca_config = tapioca_config
25
21
  @default_postrequire = default_postrequire
26
22
 
27
- super(default_command: default_command, file_writer: file_writer)
23
+ super()
28
24
 
29
25
  @installer = T.let(nil, T.nilable(Bundler::Installer))
30
26
  @spec = T.let(nil, T.nilable(Bundler::StubSpecification))
31
27
  end
32
28
 
33
29
  sig { override.void }
34
- def generate
30
+ def execute
35
31
  create_sorbet_config
36
32
  create_tapioca_config
37
33
  create_post_require
@@ -73,19 +69,19 @@ module Tapioca
73
69
  # typed: true
74
70
  # frozen_string_literal: true
75
71
 
76
- # Add your extra requires here (`#{@default_command} require` can be used to boostrap this list)
72
+ # Add your extra requires here (`#{default_command(:require)}` can be used to boostrap this list)
77
73
  CONTENT
78
74
  end
79
75
 
80
76
  sig { void }
81
77
  def create_binstub
82
- force = File.exist?(@default_command)
78
+ force = File.exist?(Tapioca::BINARY_FILE)
83
79
 
84
80
  installer.generate_bundler_executable_stubs(spec, { force: force })
85
81
 
86
82
  say_status(
87
83
  force ? :force : :create,
88
- @default_command,
84
+ Tapioca::BINARY_FILE,
89
85
  force ? :yellow : :green
90
86
  )
91
87
  end
@@ -2,26 +2,24 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- module Generators
6
- class Require < Base
5
+ module Commands
6
+ class Require < Command
7
7
  sig do
8
8
  params(
9
9
  requires_path: String,
10
- sorbet_config_path: String,
11
- default_command: String,
12
- file_writer: Thor::Actions
10
+ sorbet_config_path: String
13
11
  ).void
14
12
  end
15
- def initialize(requires_path:, sorbet_config_path:, default_command:, file_writer: FileWriter.new)
13
+ def initialize(requires_path:, sorbet_config_path:)
16
14
  @requires_path = requires_path
17
15
  @sorbet_config_path = sorbet_config_path
18
16
 
19
- super(default_command: default_command, file_writer: file_writer)
17
+ super()
20
18
  end
21
19
 
22
20
  sig { override.void }
23
- def generate
24
- compiler = Compilers::RequiresCompiler.new(@sorbet_config_path)
21
+ def execute
22
+ compiler = Static::RequiresCompiler.new(@sorbet_config_path)
25
23
  name = set_color(@requires_path, :yellow, :bold)
26
24
  say("Compiling #{name}, this may take a few seconds... ")
27
25
 
@@ -44,7 +42,7 @@ module Tapioca
44
42
  say("Done", :green)
45
43
 
46
44
  say("All requires from this application have been written to #{name}.", [:green, :bold])
47
- cmd = set_color("#{@default_command} gem", :yellow, :bold)
45
+ cmd = set_color(default_command(:gem), :yellow, :bold)
48
46
  say("Please review changes and commit them, then run `#{cmd}`.", [:green, :bold])
49
47
  end
50
48
  end