tapioca 0.4.27 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +15 -15
- data/README.md +2 -2
- data/Rakefile +5 -7
- data/exe/tapioca +2 -2
- data/lib/tapioca/cli.rb +172 -2
- data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
- data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
- data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
- data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
- data/lib/tapioca/compilers/dsl/active_model_secure_password.rb +101 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
- data/lib/tapioca/compilers/dsl/active_record_fixtures.rb +86 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
- data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
- data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
- data/lib/tapioca/compilers/dsl/active_support_concern.rb +106 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
- data/lib/tapioca/compilers/dsl/base.rb +108 -82
- data/lib/tapioca/compilers/dsl/config.rb +111 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
- data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
- data/lib/tapioca/compilers/dsl/mixed_in_class_attributes.rb +74 -0
- data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
- data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
- data/lib/tapioca/compilers/dsl/smart_properties.rb +21 -33
- data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
- data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +25 -40
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +198 -0
- data/lib/tapioca/compilers/requires_compiler.rb +2 -2
- data/lib/tapioca/compilers/sorbet.rb +25 -5
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +122 -206
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
- data/lib/tapioca/compilers/symbol_table_compiler.rb +5 -11
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/config.rb +3 -0
- data/lib/tapioca/config_builder.rb +5 -2
- data/lib/tapioca/constant_locator.rb +6 -8
- data/lib/tapioca/gemfile.rb +14 -11
- data/lib/tapioca/generators/base.rb +61 -0
- data/lib/tapioca/generators/dsl.rb +362 -0
- data/lib/tapioca/generators/gem.rb +345 -0
- data/lib/tapioca/generators/init.rb +79 -0
- data/lib/tapioca/generators/require.rb +52 -0
- data/lib/tapioca/generators/todo.rb +76 -0
- data/lib/tapioca/generators.rb +9 -0
- data/lib/tapioca/generic_type_registry.rb +25 -98
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
- data/lib/tapioca/internal.rb +2 -10
- data/lib/tapioca/loader.rb +11 -31
- data/lib/tapioca/rbi_ext/model.rb +166 -0
- data/lib/tapioca/reflection.rb +138 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
- data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +3 -0
- metadata +45 -23
- data/lib/tapioca/cli/main.rb +0 -146
- data/lib/tapioca/core_ext/class.rb +0 -28
- data/lib/tapioca/core_ext/string.rb +0 -18
- data/lib/tapioca/generator.rb +0 -633
- data/lib/tapioca/rbi/model.rb +0 -405
- data/lib/tapioca/rbi/printer.rb +0 -410
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
- data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -1,8 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "parlour"
|
5
|
-
|
6
4
|
begin
|
7
5
|
require "rails"
|
8
6
|
require "action_controller"
|
@@ -89,13 +87,13 @@ module Tapioca
|
|
89
87
|
class UrlHelpers < Base
|
90
88
|
extend T::Sig
|
91
89
|
|
92
|
-
sig { override.params(root:
|
90
|
+
sig { override.params(root: RBI::Tree, constant: Module).void }
|
93
91
|
def decorate(root, constant)
|
94
92
|
case constant
|
95
93
|
when GeneratedPathHelpersModule.singleton_class, GeneratedUrlHelpersModule.singleton_class
|
96
94
|
generate_module_for(root, constant)
|
97
95
|
else
|
98
|
-
root.
|
96
|
+
root.create_path(constant) do |mod|
|
99
97
|
create_mixins_for(mod, constant, GeneratedUrlHelpersModule)
|
100
98
|
create_mixins_for(mod, constant, GeneratedPathHelpersModule)
|
101
99
|
end
|
@@ -112,9 +110,8 @@ module Tapioca
|
|
112
110
|
Object.const_set(:GeneratedUrlHelpersModule, Rails.application.routes.named_routes.url_helpers_module)
|
113
111
|
Object.const_set(:GeneratedPathHelpersModule, Rails.application.routes.named_routes.path_helpers_module)
|
114
112
|
|
115
|
-
|
116
|
-
|
117
|
-
next unless Module.instance_method(:name).bind(mod).call
|
113
|
+
constants = all_modules.select do |mod|
|
114
|
+
next unless name_of(mod)
|
118
115
|
|
119
116
|
includes_helper?(mod, GeneratedUrlHelpersModule) ||
|
120
117
|
includes_helper?(mod, GeneratedPathHelpersModule) ||
|
@@ -127,7 +124,7 @@ module Tapioca
|
|
127
124
|
|
128
125
|
private
|
129
126
|
|
130
|
-
sig { params(root:
|
127
|
+
sig { params(root: RBI::Tree, constant: Module).void }
|
131
128
|
def generate_module_for(root, constant)
|
132
129
|
root.create_module(T.must(constant.name)) do |mod|
|
133
130
|
mod.create_include("::ActionDispatch::Routing::UrlFor")
|
@@ -136,14 +133,14 @@ module Tapioca
|
|
136
133
|
constant.instance_methods(false).each do |method|
|
137
134
|
mod.create_method(
|
138
135
|
method.to_s,
|
139
|
-
parameters: [
|
136
|
+
parameters: [create_rest_param("args", type: "T.untyped")],
|
140
137
|
return_type: "String"
|
141
138
|
)
|
142
139
|
end
|
143
140
|
end
|
144
141
|
end
|
145
142
|
|
146
|
-
sig { params(mod:
|
143
|
+
sig { params(mod: RBI::Scope, constant: Module, helper_module: Module).void }
|
147
144
|
def create_mixins_for(mod, constant, helper_module)
|
148
145
|
include_helper = constant.ancestors.include?(helper_module) || NON_DISCOVERABLE_INCLUDERS.include?(constant)
|
149
146
|
extend_helper = constant.singleton_class.ancestors.include?(helper_module)
|
@@ -20,20 +20,21 @@ module Tapioca
|
|
20
20
|
sig do
|
21
21
|
params(
|
22
22
|
requested_constants: T::Array[Module],
|
23
|
-
requested_generators: T::Array[
|
23
|
+
requested_generators: T::Array[T.class_of(Dsl::Base)],
|
24
|
+
excluded_generators: T::Array[T.class_of(Dsl::Base)],
|
24
25
|
error_handler: T.nilable(T.proc.params(error: String).void)
|
25
26
|
).void
|
26
27
|
end
|
27
|
-
def initialize(requested_constants:, requested_generators: [], error_handler: nil)
|
28
|
+
def initialize(requested_constants:, requested_generators: [], excluded_generators: [], error_handler: nil)
|
28
29
|
@generators = T.let(
|
29
|
-
gather_generators(requested_generators),
|
30
|
+
gather_generators(requested_generators, excluded_generators),
|
30
31
|
T::Enumerable[Dsl::Base]
|
31
32
|
)
|
32
33
|
@requested_constants = requested_constants
|
33
34
|
@error_handler = T.let(error_handler || $stderr.method(:puts), T.proc.params(error: String).void)
|
34
35
|
end
|
35
36
|
|
36
|
-
sig { params(blk: T.proc.params(constant: Module, rbi:
|
37
|
+
sig { params(blk: T.proc.params(constant: Module, rbi: RBI::File).void).void }
|
37
38
|
def run(&blk)
|
38
39
|
constants_to_process = gather_constants(requested_constants)
|
39
40
|
|
@@ -50,27 +51,27 @@ module Tapioca
|
|
50
51
|
|
51
52
|
blk.call(constant, rbi)
|
52
53
|
end
|
54
|
+
|
55
|
+
generators.flat_map(&:errors).each do |msg|
|
56
|
+
report_error(msg)
|
57
|
+
end
|
53
58
|
end
|
54
59
|
|
55
60
|
private
|
56
61
|
|
57
|
-
sig
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
proc do |klass|
|
64
|
-
generator = klass.name&.sub(/^Tapioca::Compilers::Dsl::/, '')&.downcase
|
65
|
-
generators.include?(generator)
|
66
|
-
end
|
62
|
+
sig do
|
63
|
+
params(
|
64
|
+
requested_generators: T::Array[T.class_of(Dsl::Base)],
|
65
|
+
excluded_generators: T::Array[T.class_of(Dsl::Base)]
|
66
|
+
).returns(T::Enumerable[Dsl::Base])
|
67
67
|
end
|
68
|
+
def gather_generators(requested_generators, excluded_generators)
|
69
|
+
generator_klasses = ::Tapioca::Reflection.descendants_of(Dsl::Base).select do |klass|
|
70
|
+
(requested_generators.empty? || requested_generators.include?(klass)) &&
|
71
|
+
!excluded_generators.include?(klass)
|
72
|
+
end.sort_by { |klass| T.must(klass.name) }
|
68
73
|
|
69
|
-
|
70
|
-
def gather_generators(requested_generators)
|
71
|
-
generator_filter = generator_filter(requested_generators)
|
72
|
-
|
73
|
-
T.cast(Dsl::Base.descendants.select(&generator_filter).map(&:new), T::Enumerable[Dsl::Base])
|
74
|
+
generator_klasses.map(&:new)
|
74
75
|
end
|
75
76
|
|
76
77
|
sig { params(requested_constants: T::Array[Module]).returns(T::Set[Module]) }
|
@@ -80,34 +81,18 @@ module Tapioca
|
|
80
81
|
constants
|
81
82
|
end
|
82
83
|
|
83
|
-
sig { params(constant: Module).returns(T.nilable(
|
84
|
+
sig { params(constant: Module).returns(T.nilable(RBI::File)) }
|
84
85
|
def rbi_for_constant(constant)
|
85
|
-
|
86
|
+
file = RBI::File.new(strictness: "true")
|
86
87
|
|
87
88
|
generators.each do |generator|
|
88
89
|
next unless generator.handles?(constant)
|
89
|
-
generator.decorate(
|
90
|
+
generator.decorate(file.root, constant)
|
90
91
|
end
|
91
92
|
|
92
|
-
return if
|
93
|
-
|
94
|
-
resolve_conflicts(parlour)
|
93
|
+
return if file.root.empty?
|
95
94
|
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
sig { params(parlour: Parlour::RbiGenerator).void }
|
100
|
-
def resolve_conflicts(parlour)
|
101
|
-
Parlour::ConflictResolver.new.resolve_conflicts(parlour.root) do |msg, candidates|
|
102
|
-
error = StringIO.new
|
103
|
-
error.puts "=== Error ==="
|
104
|
-
error.puts msg
|
105
|
-
error.puts "# Candidates"
|
106
|
-
candidates.each_with_index do |candidate, index|
|
107
|
-
error.puts " #{index}. #{candidate.describe}"
|
108
|
-
end
|
109
|
-
report_error(error.string)
|
110
|
-
end
|
95
|
+
file
|
111
96
|
end
|
112
97
|
|
113
98
|
sig { params(error: String).returns(T.noreturn) }
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class DynamicMixinCompiler
|
5
|
+
extend T::Sig
|
6
|
+
include Tapioca::Reflection
|
7
|
+
|
8
|
+
sig { returns(T::Array[Module]) }
|
9
|
+
attr_reader :dynamic_extends, :dynamic_includes
|
10
|
+
|
11
|
+
sig { returns(T::Array[Symbol]) }
|
12
|
+
attr_reader :class_attribute_readers, :class_attribute_writers, :class_attribute_predicates
|
13
|
+
|
14
|
+
sig { returns(T::Array[Symbol]) }
|
15
|
+
attr_reader :instance_attribute_readers, :instance_attribute_writers, :instance_attribute_predicates
|
16
|
+
|
17
|
+
sig { params(constant: Module).void }
|
18
|
+
def initialize(constant)
|
19
|
+
@constant = constant
|
20
|
+
mixins_from_modules = {}.compare_by_identity
|
21
|
+
class_attribute_readers = T.let([], T::Array[Symbol])
|
22
|
+
class_attribute_writers = T.let([], T::Array[Symbol])
|
23
|
+
class_attribute_predicates = T.let([], T::Array[Symbol])
|
24
|
+
|
25
|
+
instance_attribute_readers = T.let([], T::Array[Symbol])
|
26
|
+
instance_attribute_writers = T.let([], T::Array[Symbol])
|
27
|
+
instance_attribute_predicates = T.let([], T::Array[Symbol])
|
28
|
+
|
29
|
+
Class.new do
|
30
|
+
# Override the `self.include` method
|
31
|
+
define_singleton_method(:include) do |mod|
|
32
|
+
# Take a snapshot of the list of singleton class ancestors
|
33
|
+
# before the actual include
|
34
|
+
before = singleton_class.ancestors
|
35
|
+
# Call the actual `include` method with the supplied module
|
36
|
+
super(mod).tap do
|
37
|
+
# Take a snapshot of the list of singleton class ancestors
|
38
|
+
# after the actual include
|
39
|
+
after = singleton_class.ancestors
|
40
|
+
# The difference is the modules that are added to the list
|
41
|
+
# of ancestors of the singleton class. Those are all the
|
42
|
+
# modules that were `extend`ed due to the `include` call.
|
43
|
+
#
|
44
|
+
# We record those modules on our lookup table keyed by
|
45
|
+
# the included module with the values being all the modules
|
46
|
+
# that that module pulls into the singleton class.
|
47
|
+
#
|
48
|
+
# We need to reverse the order, since the extend order should
|
49
|
+
# be the inverse of the ancestor order. That is, earlier
|
50
|
+
# extended modules would be later in the ancestor chain.
|
51
|
+
mixins_from_modules[mod] = (after - before).reverse!
|
52
|
+
end
|
53
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
54
|
+
# this is a best effort, bail if we can't perform this
|
55
|
+
end
|
56
|
+
|
57
|
+
define_singleton_method(:class_attribute) do |*attrs, **kwargs|
|
58
|
+
class_attribute_readers.concat(attrs)
|
59
|
+
class_attribute_writers.concat(attrs)
|
60
|
+
|
61
|
+
instance_predicate = kwargs.fetch(:instance_predicate, true)
|
62
|
+
instance_accessor = kwargs.fetch(:instance_accessor, true)
|
63
|
+
instance_reader = kwargs.fetch(:instance_reader, instance_accessor)
|
64
|
+
instance_writer = kwargs.fetch(:instance_writer, instance_accessor)
|
65
|
+
|
66
|
+
if instance_reader
|
67
|
+
instance_attribute_readers.concat(attrs)
|
68
|
+
end
|
69
|
+
|
70
|
+
if instance_writer
|
71
|
+
instance_attribute_writers.concat(attrs)
|
72
|
+
end
|
73
|
+
|
74
|
+
if instance_predicate
|
75
|
+
class_attribute_predicates.concat(attrs)
|
76
|
+
|
77
|
+
if instance_reader
|
78
|
+
instance_attribute_predicates.concat(attrs)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
super(*attrs, **kwargs) if defined?(super)
|
83
|
+
end
|
84
|
+
|
85
|
+
# rubocop:disable Style/MissingRespondToMissing
|
86
|
+
T::Sig::WithoutRuntime.sig { params(symbol: Symbol, args: T.untyped).returns(T.untyped) }
|
87
|
+
def method_missing(symbol, *args)
|
88
|
+
# We need this here so that we can handle any random instance
|
89
|
+
# method calls on the fake including class that may be done by
|
90
|
+
# the included module during the `self.included` hook.
|
91
|
+
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
extend T::Sig
|
95
|
+
|
96
|
+
T::Sig::WithoutRuntime.sig { params(symbol: Symbol, args: T.untyped).returns(T.untyped) }
|
97
|
+
def method_missing(symbol, *args)
|
98
|
+
# Similarly, we need this here so that we can handle any
|
99
|
+
# random class method calls on the fake including class
|
100
|
+
# that may be done by the included module during the
|
101
|
+
# `self.included` hook.
|
102
|
+
end
|
103
|
+
end
|
104
|
+
# rubocop:enable Style/MissingRespondToMissing
|
105
|
+
end.include(constant)
|
106
|
+
|
107
|
+
# The value that corresponds to the original included constant
|
108
|
+
# is the list of all dynamically extended modules because of that
|
109
|
+
# constant. We grab that value by deleting the key for the original
|
110
|
+
# constant.
|
111
|
+
@dynamic_extends = T.let(mixins_from_modules.delete(constant) || [], T::Array[Module])
|
112
|
+
|
113
|
+
# Since we deleted the original constant from the list of keys, all
|
114
|
+
# the keys that remain are the ones that are dynamically included modules
|
115
|
+
# during the include of the original constant.
|
116
|
+
@dynamic_includes = T.let(mixins_from_modules.keys, T::Array[Module])
|
117
|
+
|
118
|
+
@class_attribute_readers = T.let(class_attribute_readers, T::Array[Symbol])
|
119
|
+
@class_attribute_writers = T.let(class_attribute_writers, T::Array[Symbol])
|
120
|
+
@class_attribute_predicates = T.let(class_attribute_predicates, T::Array[Symbol])
|
121
|
+
|
122
|
+
@instance_attribute_readers = T.let(instance_attribute_readers, T::Array[Symbol])
|
123
|
+
@instance_attribute_writers = T.let(instance_attribute_writers, T::Array[Symbol])
|
124
|
+
@instance_attribute_predicates = T.let(instance_attribute_predicates, T::Array[Symbol])
|
125
|
+
end
|
126
|
+
|
127
|
+
sig { returns(T::Boolean) }
|
128
|
+
def empty_attributes?
|
129
|
+
@class_attribute_readers.empty? && @class_attribute_writers.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
sig { params(tree: RBI::Tree).void }
|
133
|
+
def compile_class_attributes(tree)
|
134
|
+
return if empty_attributes?
|
135
|
+
|
136
|
+
# Create a synthetic module to hold the generated class methods
|
137
|
+
tree << RBI::Module.new("GeneratedClassMethods") do |mod|
|
138
|
+
class_attribute_readers.each do |attribute|
|
139
|
+
mod << RBI::Method.new(attribute.to_s)
|
140
|
+
end
|
141
|
+
|
142
|
+
class_attribute_writers.each do |attribute|
|
143
|
+
mod << RBI::Method.new("#{attribute}=") do |method|
|
144
|
+
method << RBI::Param.new("value")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class_attribute_predicates.each do |attribute|
|
149
|
+
mod << RBI::Method.new("#{attribute}?")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Create a synthetic module to hold the generated instance methods
|
154
|
+
tree << RBI::Module.new("GeneratedInstanceMethods") do |mod|
|
155
|
+
instance_attribute_readers.each do |attribute|
|
156
|
+
mod << RBI::Method.new(attribute.to_s)
|
157
|
+
end
|
158
|
+
|
159
|
+
instance_attribute_writers.each do |attribute|
|
160
|
+
mod << RBI::Method.new("#{attribute}=") do |method|
|
161
|
+
method << RBI::Param.new("value")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
instance_attribute_predicates.each do |attribute|
|
166
|
+
mod << RBI::Method.new("#{attribute}?")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Add a mixes_in_class_methods and include for the generated modules
|
171
|
+
tree << RBI::MixesInClassMethods.new("GeneratedClassMethods")
|
172
|
+
tree << RBI::Include.new("GeneratedInstanceMethods")
|
173
|
+
end
|
174
|
+
|
175
|
+
sig { params(tree: RBI::Tree).returns([T::Array[Module], T::Array[Module]]) }
|
176
|
+
def compile_mixes_in_class_methods(tree)
|
177
|
+
includes = dynamic_includes.select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
|
178
|
+
includes.each do |mod|
|
179
|
+
qname = qualified_name_of(mod)
|
180
|
+
tree << RBI::Include.new(T.must(qname))
|
181
|
+
end
|
182
|
+
|
183
|
+
# If we can generate multiple mixes_in_class_methods, then we want to use all dynamic extends that are not the
|
184
|
+
# constant itself
|
185
|
+
mixed_in_class_methods = dynamic_extends.select { |mod| mod != @constant }
|
186
|
+
return [[], []] if mixed_in_class_methods.empty?
|
187
|
+
|
188
|
+
mixed_in_class_methods.each do |mod|
|
189
|
+
qualified_name = qualified_name_of(mod)
|
190
|
+
next if qualified_name.nil? || qualified_name.empty?
|
191
|
+
tree << RBI::MixesInClassMethods.new(qualified_name)
|
192
|
+
end
|
193
|
+
|
194
|
+
[mixed_in_class_methods, includes]
|
195
|
+
rescue
|
196
|
+
[[], []] # silence errors
|
197
|
+
end
|
198
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "spoom"
|
5
5
|
|
6
6
|
module Tapioca
|
7
7
|
module Compilers
|
@@ -87,7 +87,7 @@ module Tapioca
|
|
87
87
|
sig { params(files: T::Enumerable[String], name: String).returns(T::Boolean) }
|
88
88
|
def name_in_project?(files, name)
|
89
89
|
files.any? do |file|
|
90
|
-
File.basename(file,
|
90
|
+
File.basename(file, ".rb") == name
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -1,15 +1,25 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require "pathname"
|
5
|
+
require "shellwords"
|
6
6
|
|
7
7
|
module Tapioca
|
8
8
|
module Compilers
|
9
9
|
module Sorbet
|
10
|
-
|
10
|
+
SORBET_GEM_SPEC = T.let(
|
11
|
+
Gem::Specification.find_by_name("sorbet-static"),
|
12
|
+
Gem::Specification
|
13
|
+
)
|
14
|
+
SORBET = T.let(
|
15
|
+
Pathname.new(SORBET_GEM_SPEC.full_gem_path) / "libexec" / "sorbet",
|
16
|
+
Pathname
|
17
|
+
)
|
11
18
|
EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
|
12
19
|
|
20
|
+
FEATURE_REQUIREMENTS = T.let({
|
21
|
+
}.freeze, T::Hash[Symbol, Gem::Requirement])
|
22
|
+
|
13
23
|
class << self
|
14
24
|
extend(T::Sig)
|
15
25
|
|
@@ -20,7 +30,7 @@ module Tapioca
|
|
20
30
|
sorbet_path,
|
21
31
|
"--quiet",
|
22
32
|
*args,
|
23
|
-
].join(
|
33
|
+
].join(" "),
|
24
34
|
err: "/dev/null"
|
25
35
|
).read
|
26
36
|
end
|
@@ -31,6 +41,16 @@ module Tapioca
|
|
31
41
|
sorbet_path = SORBET if sorbet_path.empty?
|
32
42
|
sorbet_path.to_s.shellescape
|
33
43
|
end
|
44
|
+
|
45
|
+
sig { params(feature: Symbol, version: T.nilable(Gem::Version)).returns(T::Boolean) }
|
46
|
+
def supports?(feature, version: nil)
|
47
|
+
version = SORBET_GEM_SPEC.version unless version
|
48
|
+
requirement = FEATURE_REQUIREMENTS[feature]
|
49
|
+
|
50
|
+
raise "Invalid Sorbet feature #{feature}" unless requirement
|
51
|
+
|
52
|
+
requirement.satisfied_by?(version)
|
53
|
+
end
|
34
54
|
end
|
35
55
|
end
|
36
56
|
end
|