tapioca 0.4.27 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|