tsrs 0.18.0-x86_64-linux-gnu
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +177 -0
- data/ext/tsrs/extconf.rb +121 -0
- data/ext/tsrs/include/tsrs.h +113 -0
- data/ext/tsrs/tsrs.c +261 -0
- data/lib/tsrs/3.2/tsrs.so +0 -0
- data/lib/tsrs/3.3/tsrs.so +0 -0
- data/lib/tsrs/3.4/tsrs.so +0 -0
- data/lib/tsrs/4.0/tsrs.so +0 -0
- data/lib/tsrs/backend.rb +66 -0
- data/lib/tsrs/check_result.rb +61 -0
- data/lib/tsrs/diagnosed.rb +21 -0
- data/lib/tsrs/diagnostic.rb +112 -0
- data/lib/tsrs/errors.rb +25 -0
- data/lib/tsrs/options.rb +62 -0
- data/lib/tsrs/session.rb +57 -0
- data/lib/tsrs/transpile_result.rb +54 -0
- data/lib/tsrs/version.rb +5 -0
- data/lib/tsrs.rb +80 -0
- data/licenses/README.md +11 -0
- data/licenses/tsrs-Apache-2.0.txt +55 -0
- data/licenses/tsrs-NOTICE.txt +416 -0
- data/rust/.cargo/config.toml +5 -0
- data/rust/Cargo.lock +1301 -0
- data/rust/Cargo.toml +39 -0
- data/rust/build.rs +77 -0
- data/rust/cbindgen.toml +25 -0
- data/rust/rust-toolchain.toml +5 -0
- data/rust/rustfmt.toml +3 -0
- data/rust/src/check.rs +136 -0
- data/rust/src/lib.rs +213 -0
- data/rust/src/options.rs +60 -0
- data/rust/src/result.rs +52 -0
- data/rust/src/session.rs +194 -0
- data/rust/src/status.rs +26 -0
- data/rust/src/transpile.rs +66 -0
- data/rust/src/worker.rs +84 -0
- data/sig/tsrs/backend.rbs +44 -0
- data/sig/tsrs/check_result.rbs +33 -0
- data/sig/tsrs/diagnosed.rbs +15 -0
- data/sig/tsrs/diagnostic.rbs +66 -0
- data/sig/tsrs/errors.rbs +37 -0
- data/sig/tsrs/options.rbs +19 -0
- data/sig/tsrs/session.rbs +26 -0
- data/sig/tsrs/transpile_result.rbs +29 -0
- data/sig/tsrs/types.rbs +5 -0
- data/sig/tsrs/version.rbs +5 -0
- data/sig/tsrs.rbs +27 -0
- data/tsrs.gemspec +45 -0
- 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
|
data/lib/tsrs/backend.rb
ADDED
|
@@ -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
|
data/lib/tsrs/errors.rb
ADDED
|
@@ -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
|
data/lib/tsrs/options.rb
ADDED
|
@@ -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
|
data/lib/tsrs/session.rb
ADDED
|
@@ -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
|