tsrs 0.18.0-aarch64-linux-musl

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +177 -0
  4. data/ext/tsrs/extconf.rb +121 -0
  5. data/ext/tsrs/include/tsrs.h +113 -0
  6. data/ext/tsrs/tsrs.c +261 -0
  7. data/lib/tsrs/3.2/tsrs.so +0 -0
  8. data/lib/tsrs/3.3/tsrs.so +0 -0
  9. data/lib/tsrs/3.4/tsrs.so +0 -0
  10. data/lib/tsrs/4.0/tsrs.so +0 -0
  11. data/lib/tsrs/backend.rb +66 -0
  12. data/lib/tsrs/check_result.rb +61 -0
  13. data/lib/tsrs/diagnosed.rb +21 -0
  14. data/lib/tsrs/diagnostic.rb +112 -0
  15. data/lib/tsrs/errors.rb +25 -0
  16. data/lib/tsrs/options.rb +62 -0
  17. data/lib/tsrs/session.rb +57 -0
  18. data/lib/tsrs/transpile_result.rb +54 -0
  19. data/lib/tsrs/version.rb +5 -0
  20. data/lib/tsrs.rb +80 -0
  21. data/licenses/README.md +11 -0
  22. data/licenses/tsrs-Apache-2.0.txt +55 -0
  23. data/licenses/tsrs-NOTICE.txt +416 -0
  24. data/rust/.cargo/config.toml +5 -0
  25. data/rust/Cargo.lock +1301 -0
  26. data/rust/Cargo.toml +39 -0
  27. data/rust/build.rs +77 -0
  28. data/rust/cbindgen.toml +25 -0
  29. data/rust/rust-toolchain.toml +5 -0
  30. data/rust/rustfmt.toml +3 -0
  31. data/rust/src/check.rs +136 -0
  32. data/rust/src/lib.rs +213 -0
  33. data/rust/src/options.rs +60 -0
  34. data/rust/src/result.rs +52 -0
  35. data/rust/src/session.rs +194 -0
  36. data/rust/src/status.rs +26 -0
  37. data/rust/src/transpile.rs +66 -0
  38. data/rust/src/worker.rs +84 -0
  39. data/sig/tsrs/backend.rbs +44 -0
  40. data/sig/tsrs/check_result.rbs +33 -0
  41. data/sig/tsrs/diagnosed.rbs +15 -0
  42. data/sig/tsrs/diagnostic.rbs +66 -0
  43. data/sig/tsrs/errors.rbs +37 -0
  44. data/sig/tsrs/options.rbs +19 -0
  45. data/sig/tsrs/session.rbs +26 -0
  46. data/sig/tsrs/transpile_result.rbs +29 -0
  47. data/sig/tsrs/types.rbs +5 -0
  48. data/sig/tsrs/version.rbs +5 -0
  49. data/sig/tsrs.rbs +27 -0
  50. data/tsrs.gemspec +45 -0
  51. metadata +102 -0
data/ext/tsrs/tsrs.c ADDED
@@ -0,0 +1,261 @@
1
+ #include <ruby.h>
2
+ #include <ruby/encoding.h>
3
+ #include <ruby/thread.h>
4
+ #include "include/tsrs.h"
5
+
6
+ static VALUE rb_mTsrs;
7
+ static VALUE rb_mBackend;
8
+ static VALUE rb_eError;
9
+ static VALUE rb_eOptionError;
10
+ static VALUE rb_eTsrsEncodingError;
11
+ static VALUE rb_eTranspileError;
12
+ static VALUE rb_eInternalError;
13
+ static VALUE rb_ePanicError;
14
+ static VALUE rb_eCheckError;
15
+ static VALUE rb_cSessionHandle;
16
+
17
+ typedef struct TsrsResult (*tsrs_source_function)(const char *, const char *);
18
+ typedef struct TsrsResult (*tsrs_options_function)(const char *);
19
+
20
+ struct source_call_arguments {
21
+ tsrs_source_function function;
22
+ const char *source;
23
+ const char *options;
24
+ struct TsrsResult result;
25
+ };
26
+
27
+ struct options_call_arguments {
28
+ tsrs_options_function function;
29
+ const char *options;
30
+ struct TsrsResult result;
31
+ };
32
+
33
+ static VALUE make_utf8_string(const char *cstring) {
34
+ return rb_enc_str_new_cstr(cstring, rb_utf8_encoding());
35
+ }
36
+
37
+ static VALUE take_utf8_string(char *cstring) {
38
+ if (!cstring) return Qnil;
39
+
40
+ VALUE string = make_utf8_string(cstring);
41
+ tsrs_string_free(cstring);
42
+
43
+ return string;
44
+ }
45
+
46
+ static VALUE error_class_for(enum TsrsErrorCode code) {
47
+ switch (code) {
48
+ case TSRS_ERROR_CODE_OPTION: return rb_eOptionError;
49
+ case TSRS_ERROR_CODE_ENCODING: return rb_eTsrsEncodingError;
50
+ case TSRS_ERROR_CODE_TRANSPILE: return rb_eTranspileError;
51
+ case TSRS_ERROR_CODE_CHECK: return rb_eCheckError;
52
+ case TSRS_ERROR_CODE_PANIC: return rb_ePanicError;
53
+ default: return rb_eInternalError;
54
+ }
55
+ }
56
+
57
+ static VALUE unwrap(struct TsrsResult result) {
58
+ if (result.error) {
59
+ VALUE message = make_utf8_string(result.error);
60
+ VALUE error_class = error_class_for(result.code);
61
+
62
+ tsrs_result_free(result);
63
+
64
+ rb_raise(error_class, "%s", StringValueCStr(message));
65
+ }
66
+
67
+ if (!result.value) {
68
+ tsrs_result_free(result);
69
+
70
+ rb_raise(rb_eInternalError, "tsrs returned no result");
71
+ }
72
+
73
+ VALUE value = rb_enc_str_new(result.value, (long) result.value_len, rb_utf8_encoding());
74
+
75
+ tsrs_result_free(result);
76
+
77
+ return value;
78
+ }
79
+
80
+ static void *source_call_without_gvl(void *data) {
81
+ struct source_call_arguments *arguments = (struct source_call_arguments *) data;
82
+
83
+ arguments->result = arguments->function(arguments->source, arguments->options);
84
+
85
+ return NULL;
86
+ }
87
+
88
+ static void *options_call_without_gvl(void *data) {
89
+ struct options_call_arguments *arguments = (struct options_call_arguments *) data;
90
+
91
+ arguments->result = arguments->function(arguments->options);
92
+
93
+ return NULL;
94
+ }
95
+
96
+ static VALUE source_call(tsrs_source_function function, VALUE source, VALUE options) {
97
+ struct source_call_arguments arguments;
98
+
99
+ arguments.function = function;
100
+ arguments.source = StringValueCStr(source);
101
+ arguments.options = StringValueCStr(options);
102
+
103
+ rb_thread_call_without_gvl(source_call_without_gvl, &arguments, NULL, NULL);
104
+
105
+ RB_GC_GUARD(source);
106
+ RB_GC_GUARD(options);
107
+
108
+ return unwrap(arguments.result);
109
+ }
110
+
111
+ static VALUE options_call(tsrs_options_function function, VALUE options) {
112
+ struct options_call_arguments arguments;
113
+
114
+ arguments.function = function;
115
+ arguments.options = StringValueCStr(options);
116
+
117
+ rb_thread_call_without_gvl(options_call_without_gvl, &arguments, NULL, NULL);
118
+
119
+ RB_GC_GUARD(options);
120
+
121
+ return unwrap(arguments.result);
122
+ }
123
+
124
+ struct session_call_arguments {
125
+ struct TsrsSession *session;
126
+ const char *options;
127
+ struct TsrsResult result;
128
+ };
129
+
130
+ static void session_free(void *data) {
131
+ tsrs_session_free((struct TsrsSession *) data);
132
+ }
133
+
134
+ static const rb_data_type_t session_type = {
135
+ "Tsrs::Backend::SessionHandle",
136
+ { NULL, session_free, NULL },
137
+ NULL,
138
+ NULL,
139
+ RUBY_TYPED_FREE_IMMEDIATELY,
140
+ };
141
+
142
+ static struct TsrsSession *session_of(VALUE handle) {
143
+ struct TsrsSession *session = (struct TsrsSession *) rb_check_typeddata(handle, &session_type);
144
+
145
+ if (!session) rb_raise(rb_eInternalError, "the session is gone");
146
+
147
+ return session;
148
+ }
149
+
150
+ static void *session_call_without_gvl(void *data) {
151
+ struct session_call_arguments *arguments = (struct session_call_arguments *) data;
152
+
153
+ arguments->result = tsrs_session_check(arguments->session, arguments->options);
154
+
155
+ return NULL;
156
+ }
157
+
158
+ static VALUE rb_session_new(VALUE self, VALUE cwd) {
159
+ (void) self;
160
+
161
+ struct TsrsSession *session = tsrs_session_new(StringValueCStr(cwd));
162
+
163
+ if (!session) rb_raise(rb_eInternalError, "tsrs could not start a session");
164
+
165
+ return TypedData_Wrap_Struct(rb_cSessionHandle, &session_type, session);
166
+ }
167
+
168
+ static VALUE rb_session_check(VALUE self, VALUE handle, VALUE options) {
169
+ (void) self;
170
+
171
+ struct session_call_arguments arguments;
172
+
173
+ arguments.options = StringValueCStr(options);
174
+ arguments.session = tsrs_session_retain(session_of(handle));
175
+
176
+ rb_thread_call_without_gvl(session_call_without_gvl, &arguments, NULL, NULL);
177
+
178
+ tsrs_session_free(arguments.session);
179
+
180
+ RB_GC_GUARD(handle);
181
+ RB_GC_GUARD(options);
182
+
183
+ return unwrap(arguments.result);
184
+ }
185
+
186
+ static VALUE rb_session_close(VALUE self, VALUE handle) {
187
+ (void) self;
188
+
189
+ tsrs_session_close(session_of(handle));
190
+
191
+ return Qnil;
192
+ }
193
+
194
+ static VALUE rb_transpile(VALUE self, VALUE source, VALUE options) {
195
+ (void) self;
196
+
197
+ return source_call(tsrs_transpile, source, options);
198
+ }
199
+
200
+ static VALUE rb_transpile_declaration(VALUE self, VALUE source, VALUE options) {
201
+ (void) self;
202
+
203
+ return source_call(tsrs_transpile_declaration, source, options);
204
+ }
205
+
206
+ static VALUE rb_check(VALUE self, VALUE options) {
207
+ (void) self;
208
+
209
+ return options_call(tsrs_check, options);
210
+ }
211
+
212
+ static VALUE rb_panic_for_test(VALUE self) {
213
+ (void) self;
214
+
215
+ return unwrap(tsrs_panic_for_test());
216
+ }
217
+
218
+ static VALUE rb_native_version(VALUE self) {
219
+ (void) self;
220
+
221
+ return take_utf8_string(tsrs_version());
222
+ }
223
+
224
+ static VALUE rb_tsrs_version(VALUE self) {
225
+ (void) self;
226
+
227
+ return take_utf8_string(tsrs_tsrs_version());
228
+ }
229
+
230
+ static VALUE rb_typescript_version(VALUE self) {
231
+ (void) self;
232
+
233
+ return take_utf8_string(tsrs_typescript_version());
234
+ }
235
+
236
+ void Init_tsrs(void) {
237
+ rb_mTsrs = rb_define_module("Tsrs");
238
+ rb_mBackend = rb_define_module_under(rb_mTsrs, "Backend");
239
+
240
+ rb_eError = rb_define_class_under(rb_mTsrs, "Error", rb_eStandardError);
241
+ rb_eOptionError = rb_define_class_under(rb_mTsrs, "OptionError", rb_eError);
242
+ rb_eTsrsEncodingError = rb_define_class_under(rb_mTsrs, "EncodingError", rb_eError);
243
+ rb_eTranspileError = rb_define_class_under(rb_mTsrs, "TranspileError", rb_eError);
244
+ rb_eInternalError = rb_define_class_under(rb_mTsrs, "InternalError", rb_eError);
245
+ rb_ePanicError = rb_define_class_under(rb_mTsrs, "PanicError", rb_eInternalError);
246
+ rb_eCheckError = rb_define_class_under(rb_mTsrs, "CheckError", rb_eError);
247
+
248
+ rb_cSessionHandle = rb_define_class_under(rb_mBackend, "SessionHandle", rb_cObject);
249
+ rb_undef_alloc_func(rb_cSessionHandle);
250
+
251
+ rb_define_singleton_method(rb_mBackend, "transpile", rb_transpile, 2);
252
+ rb_define_singleton_method(rb_mBackend, "transpile_declaration", rb_transpile_declaration, 2);
253
+ rb_define_singleton_method(rb_mBackend, "check", rb_check, 1);
254
+ rb_define_singleton_method(rb_mBackend, "session_new", rb_session_new, 1);
255
+ rb_define_singleton_method(rb_mBackend, "session_check", rb_session_check, 2);
256
+ rb_define_singleton_method(rb_mBackend, "session_close", rb_session_close, 1);
257
+ rb_define_singleton_method(rb_mBackend, "panic_for_test", rb_panic_for_test, 0);
258
+ rb_define_singleton_method(rb_mBackend, "version", rb_native_version, 0);
259
+ rb_define_singleton_method(rb_mBackend, "tsrs_version", rb_tsrs_version, 0);
260
+ rb_define_singleton_method(rb_mBackend, "typescript_version", rb_typescript_version, 0);
261
+ }
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tsrs
4
+ module Backend
5
+ module Unavailable
6
+ #: (String, String) -> String
7
+ def transpile(_source, _options_json)
8
+ unavailable(__method__)
9
+ end
10
+
11
+ #: (String, String) -> String
12
+ def transpile_declaration(_source, _options_json)
13
+ unavailable(__method__)
14
+ end
15
+
16
+ #: (String) -> String
17
+ def check(_options_json)
18
+ unavailable(__method__)
19
+ end
20
+
21
+ #: (String) -> untyped
22
+ def session_new(_cwd)
23
+ unavailable(__method__)
24
+ end
25
+
26
+ #: (untyped, String) -> String
27
+ def session_check(_handle, _options_json)
28
+ unavailable(__method__)
29
+ end
30
+
31
+ #: (untyped) -> nil
32
+ def session_close(_handle)
33
+ unavailable(__method__)
34
+ end
35
+
36
+ #: () -> String
37
+ def panic_for_test
38
+ unavailable(__method__)
39
+ end
40
+
41
+ #: () -> String
42
+ def version
43
+ unavailable(__method__)
44
+ end
45
+
46
+ #: () -> String
47
+ def tsrs_version
48
+ unavailable(__method__)
49
+ end
50
+
51
+ #: () -> String
52
+ def typescript_version
53
+ unavailable(__method__)
54
+ end
55
+
56
+ private
57
+
58
+ #: (Symbol?) -> bot
59
+ def unavailable(name)
60
+ raise NotImplementedError, "Tsrs::Backend.#{name} is defined by the native extension, which did not load"
61
+ end
62
+ end
63
+
64
+ extend Unavailable
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Tsrs
6
+ class CheckResult
7
+ include Diagnosed
8
+
9
+ attr_reader :status #: Integer
10
+ attr_reader :exit_status #: Symbol
11
+ attr_reader :diagnostics #: Array[Tsrs::Diagnostic]
12
+ attr_reader :output #: String
13
+
14
+ #: (String) -> Tsrs::CheckResult
15
+ def self.from_json(payload)
16
+ data = JSON.parse(payload)
17
+ status = data.fetch("status")
18
+
19
+ new(
20
+ status: status.fetch("code"),
21
+ exit_status: status.fetch("name").to_sym,
22
+ output: data.fetch("output", ""),
23
+ diagnostics: data.fetch("diagnostics").map { |diagnostic| Diagnostic.from_hash(diagnostic) }
24
+ )
25
+ end
26
+
27
+ #: (status: Integer, exit_status: Symbol, ?output: String, ?diagnostics: Array[Tsrs::Diagnostic]) -> void
28
+ def initialize(status:, exit_status:, output: "", diagnostics: [])
29
+ @status = status
30
+ @exit_status = exit_status
31
+ @output = output.freeze
32
+ @diagnostics = diagnostics.freeze
33
+
34
+ freeze
35
+ end
36
+
37
+ #: () -> bool
38
+ def success?
39
+ status.zero?
40
+ end
41
+
42
+ #: (?strict: bool) -> self
43
+ def validate!(strict: false)
44
+ return self if success? && (!strict || diagnostics.empty?)
45
+
46
+ raise TypeError, diagnostics unless diagnostics.empty?
47
+
48
+ raise Error, (output.empty? ? "tsc exited with status #{status} (#{exit_status})" : output.strip)
49
+ end
50
+
51
+ #: () -> String
52
+ def to_s
53
+ diagnostics.join("\n")
54
+ end
55
+
56
+ #: () -> String
57
+ def inspect
58
+ "#<#{self.class.name} status=#{status} errors=#{errors.length} diagnostics=#{diagnostics.length}>"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tsrs
4
+ # @rbs module-self _Diagnosed
5
+ module Diagnosed
6
+ #: () -> Array[Tsrs::Diagnostic]
7
+ def errors
8
+ diagnostics.select(&:error?)
9
+ end
10
+
11
+ #: () -> Array[Tsrs::Diagnostic]
12
+ def warnings
13
+ diagnostics.select(&:warning?)
14
+ end
15
+
16
+ #: () -> bool
17
+ def errors?
18
+ diagnostics.any?(&:error?)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tsrs
4
+ # Lines and columns are 1-based. `start` and `finish` are offsets in UTF-16 code units, as TypeScript counts.
5
+ #
6
+ class Diagnostic
7
+ CATEGORIES = [:warning, :error, :suggestion, :message].freeze #: Array[Symbol]
8
+
9
+ attr_reader :code #: Integer
10
+ attr_reader :category #: Symbol
11
+ attr_reader :message #: String
12
+ attr_reader :file #: String?
13
+ attr_reader :line #: Integer?
14
+ attr_reader :column #: Integer?
15
+ attr_reader :end_line #: Integer?
16
+ attr_reader :end_column #: Integer?
17
+ attr_reader :start #: Integer?
18
+ attr_reader :finish #: Integer?
19
+ attr_reader :source #: String?
20
+ attr_reader :message_chain #: Array[Tsrs::Diagnostic]
21
+ attr_reader :related_information #: Array[Tsrs::Diagnostic]
22
+
23
+ #: (Hash[String, untyped]) -> Tsrs::Diagnostic
24
+ def self.from_hash(hash)
25
+ start_position = hash["startPosition"]
26
+ end_position = hash["endPosition"]
27
+
28
+ new(
29
+ code: hash.fetch("code"),
30
+ category: CATEGORIES.fetch(hash.fetch("category"), :message),
31
+ message: hash.fetch("text"),
32
+ file: hash["fileName"],
33
+ line: start_position && (start_position.fetch("line") + 1),
34
+ column: start_position && (start_position.fetch("character") + 1),
35
+ end_line: end_position && (end_position.fetch("line") + 1),
36
+ end_column: end_position && (end_position.fetch("character") + 1),
37
+ start: offset(hash["pos"]),
38
+ finish: offset(hash["end"]),
39
+ source: hash["source"],
40
+ message_chain: (hash["messageChain"] || []).map { |link| from_hash(link) },
41
+ related_information: (hash["relatedInformation"] || []).map { |related| from_hash(related) }
42
+ )
43
+ end
44
+
45
+ #: (untyped) -> Integer?
46
+ def self.offset(value)
47
+ value.is_a?(Integer) && value >= 0 ? value : nil
48
+ end
49
+
50
+ #: (code: Integer, category: Symbol, message: String, ?file: String?, ?line: Integer?, ?column: Integer?, ?end_line: Integer?, ?end_column: Integer?, ?start: Integer?, ?finish: Integer?, ?source: String?, ?message_chain: Array[Tsrs::Diagnostic], ?related_information: Array[Tsrs::Diagnostic]) -> void
51
+ def initialize(code:, category:, message:, file: nil, line: nil, column: nil, end_line: nil, end_column: nil, start: nil, finish: nil, source: nil, message_chain: [], related_information: [])
52
+ @code = code
53
+ @category = category
54
+ @message = message.freeze
55
+ @file = file&.freeze
56
+ @line = line
57
+ @column = column
58
+ @end_line = end_line
59
+ @end_column = end_column
60
+ @start = start
61
+ @finish = finish
62
+ @source = source&.freeze
63
+ @message_chain = message_chain.freeze
64
+ @related_information = related_information.freeze
65
+
66
+ freeze
67
+ end
68
+
69
+ #: () -> bool
70
+ def error?
71
+ category == :error
72
+ end
73
+
74
+ #: () -> bool
75
+ def warning?
76
+ category == :warning
77
+ end
78
+
79
+ #: () -> String
80
+ def location
81
+ return "" unless file
82
+
83
+ line ? "#{file}:#{line}:#{column}" : file.to_s
84
+ end
85
+
86
+ #: () -> String
87
+ def full_message
88
+ ([message] + chain_lines(message_chain, 1)).join("\n")
89
+ end
90
+
91
+ #: () -> String
92
+ def to_s
93
+ prefix = "#{category} TS#{code}: #{full_message}"
94
+
95
+ file ? "#{location} - #{prefix}" : prefix
96
+ end
97
+
98
+ #: () -> String
99
+ def inspect
100
+ "#<#{self.class.name} #{category} TS#{code} #{"#{location} " unless location.empty?}#{message.inspect}>"
101
+ end
102
+
103
+ private
104
+
105
+ #: (Array[Tsrs::Diagnostic], Integer) -> Array[String]
106
+ def chain_lines(chain, depth)
107
+ chain.flat_map do |link|
108
+ ["#{" " * depth}#{link.message}"] + chain_lines(link.message_chain, depth + 1)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tsrs
4
+ class Error < StandardError; end
5
+ class OptionError < Error; end
6
+ class EncodingError < Error; end
7
+ class TranspileError < Error; end
8
+ class InternalError < Error; end
9
+ class PanicError < InternalError; end
10
+ class CheckError < Error; end
11
+
12
+ class DiagnosticError < Error
13
+ attr_reader :diagnostics #: Array[Tsrs::Diagnostic]
14
+
15
+ #: (Array[Tsrs::Diagnostic]) -> void
16
+ def initialize(diagnostics)
17
+ @diagnostics = diagnostics
18
+
19
+ super(diagnostics.join("\n"))
20
+ end
21
+ end
22
+
23
+ class SyntaxError < DiagnosticError; end
24
+ class TypeError < DiagnosticError; end
25
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tsrs
4
+ module Options
5
+ REFUSED = {
6
+ "build" => "build mode is not supported",
7
+ "watch" => "watch mode is not supported",
8
+ "init" => "--init is not supported by tsrs",
9
+ "showConfig" => "--showConfig is not supported by tsrs",
10
+ "help" => "--help prints to the terminal, it is not an option for a call",
11
+ "all" => "--all prints to the terminal, it is not an option for a call",
12
+ "version" => "--version prints to the terminal, use Tsrs.typescript_version instead",
13
+ }.freeze #: Hash[String, String]
14
+
15
+ #: (Hash[Symbol | String, untyped]) -> Array[String]
16
+ def self.arguments(options)
17
+ options.each_with_object([]) do |(key, value), arguments|
18
+ next if value.nil?
19
+
20
+ name = option_name(key)
21
+
22
+ refuse!(name)
23
+
24
+ arguments.push(*flag(name, value))
25
+ end
26
+ end
27
+
28
+ #: (Symbol | String) -> String
29
+ def self.option_name(key)
30
+ name = key.to_s.delete_prefix("--")
31
+
32
+ return name unless key.is_a?(Symbol)
33
+
34
+ name.gsub(/_([a-z0-9])/) { ::Regexp.last_match(1).to_s.upcase }
35
+ end
36
+
37
+ #: (String) -> void
38
+ def self.refuse!(name)
39
+ reason = REFUSED[name]
40
+
41
+ raise OptionError, "#{name}: #{reason}" if reason
42
+ end
43
+
44
+ #: (String, untyped) -> Array[String]
45
+ def self.flag(name, value)
46
+ case value
47
+ when true
48
+ ["--#{name}"]
49
+ when false
50
+ ["--#{name}", "false"]
51
+ when Array
52
+ ["--#{name}", value.join(",")]
53
+ when Hash
54
+ raise OptionError, "#{name} takes a #{value.class}, which tsc's command line has no way to say"
55
+ else
56
+ ["--#{name}", value.to_s]
57
+ end
58
+ end
59
+
60
+ private_class_method :refuse!, :flag
61
+ end
62
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Tsrs
6
+ # Unlike Tsrs.check, a session gives a check's memory back. Compiler options come from the tsconfig.json only.
7
+ #
8
+ class Session
9
+ attr_reader :cwd #: String
10
+
11
+ #: [T] (?cwd: String) { (Tsrs::Session) -> T } -> T
12
+ def self.open(cwd: Dir.pwd)
13
+ session = new(cwd: cwd)
14
+
15
+ begin
16
+ yield session
17
+ ensure
18
+ session.close
19
+ end
20
+ end
21
+
22
+ #: (?cwd: String) -> void
23
+ def initialize(cwd: Dir.pwd)
24
+ @cwd = File.expand_path(cwd.to_s)
25
+ @handle = Backend.session_new(@cwd)
26
+ @closed = false
27
+ end
28
+
29
+ #: (?String) -> Tsrs::CheckResult
30
+ def check(project = "tsconfig.json")
31
+ raise Error, "the session is closed" if closed?
32
+
33
+ request = JSON.generate({ project: File.expand_path(project.to_s, cwd) })
34
+
35
+ CheckResult.from_json(Backend.session_check(@handle, request))
36
+ end
37
+
38
+ #: () -> void
39
+ def close
40
+ return if closed?
41
+
42
+ @closed = true
43
+
44
+ Backend.session_close(@handle)
45
+ end
46
+
47
+ #: () -> bool
48
+ def closed?
49
+ @closed
50
+ end
51
+
52
+ #: () -> String
53
+ def inspect
54
+ "#<#{self.class.name} cwd=#{cwd.inspect}#{" closed" if closed?}>"
55
+ end
56
+ end
57
+ end