rbs2ts 1.0.0.pre.alpha.3 → 1.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb1b221a5b895071e46b77dcf36fd1df3e789b0af6f91cb78adbfef7aa633c1c
4
- data.tar.gz: 1a452ee8992929e84d0cfde11006e1b9918a4dbfb5cd597506523d6491199c7f
3
+ metadata.gz: 4743b73de60833c61821721455c9c6f23cb7e6f9616179df62af629ad9ceb99c
4
+ data.tar.gz: '097d7ccc401a1c0684c433b4fc11279409ff75d6bf99e2b2340953f456d40986'
5
5
  SHA512:
6
- metadata.gz: c6242c6655d8ab4d43267c4fb4d7a99471d27047b5b1ea04025aada0066f272b61ffa353a06bcd238a11890d3a65ff5bf32cb6471543a00d5036b734e5b84cba
7
- data.tar.gz: 9a9d8d78d4eadd2cd9999063d841d878259f5bc52ccfbb0ea02b4691ef0a3133d8c8ab4cb954767925d0564e0c930697b1cd0017a4d476704a8f08515933bfd1
6
+ metadata.gz: d0b28b0b98c1cb73b247f397da3b64cc0c9c2d9bebd71f54c5a377fbbb9301b5ffd2c337810be8c61e2d71eca5df613707a3b266144816e8dcce31d26c55a095
7
+ data.tar.gz: 5ee48310c33f8ede00253b84037ccfab31a7cbd4cc1262aa041bab223a431b991b6792875d327724f71503c84a81ba1460fe9ad05130f13802a8cdded6cf71f6
@@ -0,0 +1 @@
1
+ README.md
@@ -10,7 +10,7 @@
10
10
  "request": "launch",
11
11
  "program": "${workspaceRoot}/exe/rbs2ts",
12
12
  "useBundler": true,
13
- "args": ["convert", "spec/sample_rbs/test.rbs"]
13
+ "args": ["convert", "spec/fixtures/test.rbs"]
14
14
  }
15
15
  ]
16
16
  }
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Rbs2ts
2
2
 
3
- [![mugi_uno](https://circleci.com/<VCS>/<ORG_NAME>/<PROJECT_NAME>.svg?style=svg)](LINK)
4
-
5
- Convert RBS to TypeScript type definition.
3
+ A RubyGem that converts Ruby RBS to TypeScript definitions.
6
4
 
7
5
  ## Installation
8
6
 
@@ -16,16 +14,195 @@ gem install rbs2ts
16
14
  rbs2ts convert type.rbs
17
15
  ```
18
16
 
19
- ---
17
+ ## Example
18
+
19
+ from RBS
20
+
21
+ ```
22
+ type TypeofInteger = Integer
23
+ type TypeofFloat = Float
24
+ type TypeofNumeric = Numeric
25
+ type TypeofString = String
26
+ type TypeofBool = Bool
27
+ type TypeofVoid = void
28
+ type TypeofUntyped = untyped
29
+ type TypeofNil = nil
30
+
31
+ type IntegerLiteral = 123
32
+ type StringLiteral = 'abc'
33
+ type TrueLiteral = true
34
+ type FalseLiteral = false
35
+
36
+ type IntersectionType = String & Integer & Bool
37
+ type UnionType = String | Integer | Bool
38
+
39
+ type ArrayType = Array[String]
40
+
41
+ type TupleType = [ ]
42
+ type TupleEmptyType = [String, Integer]
43
+
44
+ type OptionalType = String?
45
+
46
+ type RecordType = {
47
+ s: String,
48
+ next: {
49
+ i: Integer,
50
+ f: Float
51
+ }?
52
+ }
53
+
54
+ class Klass
55
+ @val: String
56
+ attr_accessor a: String
57
+ attr_reader b: Integer
58
+ attr_writer c: Bool
59
+
60
+ attr_reader r: {
61
+ d: String,
62
+ e: {
63
+ f: String,
64
+ g: String?
65
+ }?
66
+ }
67
+
68
+ def to_tuple: () -> [{ s: String, f: Float }?]
69
+ def required_positional: (String) -> void
70
+ def required_positional_name: (String str) -> void
71
+ def optional_positional: (?String) -> void
72
+ def optional_positional_name: (?String? str) -> void
73
+ def rest_positional: (*String) -> void
74
+ def rest_positional_name: (*String str) -> void
75
+ def rest_positional_with_trailing: (*String, Integer) -> void
76
+ def rest_positional_name_with_trailing: (*String str, Integer trailing) -> void
77
+ def required_keyword: (str: String) -> void
78
+ def optional_keyword: (?str: String?) -> void
79
+ def rest_keywords: (**String) -> void
80
+ def rest_keywords_name: (**String rest) -> void
81
+
82
+ def all_arguments: (
83
+ String required_positional,
84
+ ?Integer? optional_positional,
85
+ *String rest_positionals_s,
86
+ Integer trailing_positional_s,
87
+ required_keyword: String,
88
+ ?optional_keyword: Integer?,
89
+ **String rest_keywords
90
+ ) -> [{ s: String, f: Float }?]
91
+ end
92
+
93
+ module Module
94
+ @val: String
95
+ type AliasType = String
96
+
97
+ def func: (String, Integer) -> { str: String, int: Integer }
98
+
99
+ class NestedClass
100
+ attr_accessor a: AliasType
101
+ end
102
+ end
103
+
104
+ interface _Interface
105
+ def func: (String, Integer) -> { str: String, int: Integer }
106
+ end
107
+ ```
108
+
109
+ to TypeScript
110
+
111
+ ```typescript
112
+ export type TypeofInteger = number;
20
113
 
21
- ## ToDo
114
+ export type TypeofFloat = number;
22
115
 
23
- - [x] Literal type
24
- - [ ] Interface type
25
- - [ ] Literal type
26
- - [ ] Tuple Type
27
- - [ ] Base Types
28
- - [ ] Method Type (Argument Types and Return Types)
29
- - [ ] Class declaration
30
- - [ ] Module declaration
31
- - [ ] Interface declaration
116
+ export type TypeofNumeric = number;
117
+
118
+ export type TypeofString = string;
119
+
120
+ export type TypeofBool = boolean;
121
+
122
+ export type TypeofVoid = void;
123
+
124
+ export type TypeofUntyped = any;
125
+
126
+ export type TypeofNil = null;
127
+
128
+ export type IntegerLiteral = 123;
129
+
130
+ export type StringLiteral = "abc";
131
+
132
+ export type TrueLiteral = true;
133
+
134
+ export type FalseLiteral = false;
135
+
136
+ export type IntersectionType = string & number & boolean;
137
+
138
+ export type UnionType = string | number | boolean;
139
+
140
+ export type ArrayType = string[];
141
+
142
+ export type TupleType = [];
143
+
144
+ export type TupleEmptyType = [string, number];
145
+
146
+ export type OptionalType = string | null | undefined;
147
+
148
+ export type RecordType = {
149
+ s: string;
150
+ next: {
151
+ i: number;
152
+ f: number;
153
+ } | null | undefined;
154
+ };
155
+
156
+ export declare class Klass {
157
+ val: string;
158
+ a: string;
159
+ readonly b: number;
160
+ c: boolean;
161
+ readonly r: {
162
+ d: string;
163
+ e: {
164
+ f: string;
165
+ g: string | null | undefined;
166
+ } | null | undefined;
167
+ };
168
+ toTuple(): [({
169
+ s: string;
170
+ f: number;
171
+ } | null | undefined)];
172
+ requiredPositional(arg1: string): void;
173
+ requiredPositionalName(str: string): void;
174
+ optionalPositional(arg1?: string): void;
175
+ optionalPositionalName(str?: string | null | undefined): void;
176
+ restPositional(...arg1: string[]): void;
177
+ restPositionalName(...str: string[]): void;
178
+ restPositionalWithTrailing(arg1: string[], arg2: number): void;
179
+ restPositionalNameWithTrailing(str: string[], trailing: number): void;
180
+ requiredKeyword(arg1: { str: string }): void;
181
+ optionalKeyword(arg1: { str?: string | null | undefined }): void;
182
+ restKeywords(arg1: { [key: string]: unknown; }): void;
183
+ restKeywordsName(arg1: { [key: string]: unknown; }): void;
184
+ allArguments(requiredPositional: string, optionalPositional?: number | null | undefined, restPositionalsS?: string[], trailingPositionalS?: number, arg1?: { requiredKeyword: string, optionalKeyword?: number | null | undefined, [key: string]: unknown; }): [({
185
+ s: string;
186
+ f: number;
187
+ } | null | undefined)];
188
+ };
189
+
190
+ export namespace Module {
191
+ export declare let val: string;
192
+ export type AliasType = string;
193
+ export declare function func(arg1: string, arg2: number): {
194
+ str: string;
195
+ int: number;
196
+ };
197
+ export declare class NestedClass {
198
+ a: AliasType;
199
+ };
200
+ };
201
+
202
+ export interface Interface {
203
+ func(arg1: string, arg2: number): {
204
+ str: string;
205
+ int: number;
206
+ };
207
+ };
208
+ ```
@@ -3,5 +3,7 @@ end
3
3
 
4
4
  require 'case_transform'
5
5
 
6
+ require 'rbs2ts/converter/helper'
6
7
  require 'rbs2ts/converter/declarations'
7
8
  require 'rbs2ts/converter/types'
9
+ require 'rbs2ts/converter/members'
@@ -9,6 +9,12 @@ module Rbs2ts
9
9
  def to_ts
10
10
  decls_ts = @decls.map do |d|
11
11
  case d
12
+ when ::RBS::AST::Declarations::Class then
13
+ Converter::Declarations::Class.new(d).to_ts
14
+ when ::RBS::AST::Declarations::Module then
15
+ Converter::Declarations::Module.new(d).to_ts
16
+ when ::RBS::AST::Declarations::Interface then
17
+ Converter::Declarations::Interface.new(d).to_ts
12
18
  when ::RBS::AST::Declarations::Alias then
13
19
  Converter::Declarations::Alias.new(d).to_ts
14
20
  end
@@ -29,26 +35,109 @@ module Rbs2ts
29
35
  end
30
36
 
31
37
  def name
32
- declaration.name.to_s.gsub(/:/, '')
38
+ declaration.name.name.to_s.gsub(/:/, '')
33
39
  end
34
40
 
35
41
  private
36
42
 
37
43
  attr_reader :declaration
38
44
  end
39
-
45
+
40
46
  class Class < Base
47
+ def to_ts
48
+ members_ts = declaration.members.map {|member|
49
+ member_to_ts(member)
50
+ }.reject(&:empty?).join("\n")
51
+
52
+ <<~TS
53
+ export declare class #{name} {
54
+ #{Helper.indent(members_ts)}
55
+ };
56
+ TS
57
+ .chomp
58
+ end
59
+
60
+ def member_to_ts(member)
61
+ case member
62
+ when ::RBS::AST::Members::InstanceVariable then
63
+ Converter::Members::InstanceVariable.new(member).to_ts
64
+ when ::RBS::AST::Members::AttrReader then
65
+ Converter::Members::AttrReader.new(member).to_ts
66
+ when ::RBS::AST::Members::AttrWriter then
67
+ Converter::Members::AttrWriter.new(member).to_ts
68
+ when ::RBS::AST::Members::AttrAccessor then
69
+ Converter::Members::AttrAccessor.new(member).to_ts
70
+ when ::RBS::AST::Members::MethodDefinition
71
+ Converter::Members::MethodDefinition.new(member).to_ts
72
+ else
73
+ ''
74
+ end
75
+ end
41
76
  end
42
77
 
43
78
  class Module < Base
79
+ def to_ts
80
+ members_ts = declaration.members.map {|member|
81
+ member_to_ts(member)
82
+ }.reject(&:empty?).join("\n")
83
+
84
+ <<~TS
85
+ export namespace #{name} {
86
+ #{Helper.indent(members_ts)}
87
+ };
88
+ TS
89
+ .chomp
90
+ end
91
+
92
+ def member_to_ts(member)
93
+ case member
94
+ when ::RBS::AST::Declarations::Class then
95
+ Converter::Declarations::Class.new(member).to_ts
96
+ when ::RBS::AST::Declarations::Module then
97
+ Converter::Declarations::Module.new(member).to_ts
98
+ when ::RBS::AST::Declarations::Interface then
99
+ Converter::Declarations::Interface.new(member).to_ts
100
+ when ::RBS::AST::Declarations::Alias then
101
+ Converter::Declarations::Alias.new(member).to_ts
102
+ when ::RBS::AST::Members::InstanceVariable
103
+ ts = Converter::Members::InstanceVariable.new(member).to_ts
104
+ "export declare let #{ts}"
105
+ when ::RBS::AST::Members::MethodDefinition
106
+ ts = Converter::Members::MethodDefinition.new(member).to_ts
107
+ "export declare function #{ts}"
108
+ else
109
+ ''
110
+ end
111
+ end
44
112
  end
45
113
 
46
114
  class Interface < Base
115
+ def to_ts
116
+ members_ts = declaration.members.map {|member|
117
+ member_to_ts(member)
118
+ }.reject(&:empty?).join("\n")
119
+
120
+ <<~TS
121
+ export interface #{name.gsub(/_/, '')} {
122
+ #{Helper.indent(members_ts)}
123
+ };
124
+ TS
125
+ .chomp
126
+ end
127
+
128
+ def member_to_ts(member)
129
+ case member
130
+ when ::RBS::AST::Members::MethodDefinition
131
+ Converter::Members::MethodDefinition.new(member).to_ts
132
+ else
133
+ ''
134
+ end
135
+ end
47
136
  end
48
137
 
49
138
  class Alias < Base
50
139
  def to_ts
51
- "type #{name} = #{Converter::Types::Resolver.to_ts(declaration.type)};"
140
+ "export type #{name} = #{Converter::Types::Resolver.to_ts(declaration.type)};"
52
141
  end
53
142
  end
54
143
 
@@ -0,0 +1,16 @@
1
+ module Rbs2ts
2
+ module Converter
3
+ module Helper
4
+ class << self
5
+ INDENT = ' '
6
+
7
+ def indent(text, level = 1)
8
+ text
9
+ .split("\n")
10
+ .map {|t| "#{INDENT * level}#{t}" }
11
+ .join("\n")
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,146 @@
1
+ module Rbs2ts
2
+ module Converter
3
+ module Members
4
+ class Base
5
+ def initialize(member)
6
+ @member = member
7
+ end
8
+
9
+ def to_ts
10
+ ''
11
+ end
12
+
13
+ def name
14
+ CaseTransform.camel_lower(member.name.to_s.gsub(/:/, ''))
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :member
20
+ end
21
+
22
+ class InstanceVariable < Base
23
+ def to_ts
24
+ "#{name.gsub(/@/, '')}: #{Converter::Types::Resolver.to_ts(member.type)};"
25
+ end
26
+ end
27
+
28
+ class AttrReader < Base
29
+ def to_ts
30
+ "readonly #{name}: #{Converter::Types::Resolver.to_ts(member.type)};"
31
+ end
32
+ end
33
+
34
+ class AttrWriter < Base
35
+ def to_ts
36
+ "#{name}: #{Converter::Types::Resolver.to_ts(member.type)};"
37
+ end
38
+ end
39
+
40
+ class AttrAccessor < Base
41
+ def to_ts
42
+ "#{name}: #{Converter::Types::Resolver.to_ts(member.type)};"
43
+ end
44
+ end
45
+
46
+ class MethodDefinition < Base
47
+ def initialize(member)
48
+ super
49
+ @args_count = 0
50
+ end
51
+
52
+ def to_ts
53
+ "#{name}(#{args_to_ts}): #{return_type_to_ts};"
54
+ end
55
+
56
+ def args_to_ts
57
+ [
58
+ *required_positional_to_ts,
59
+ *optional_positional_to_ts,
60
+ *rest_positionals_to_ts,
61
+ *trailing_positionals_to_ts,
62
+ *keyword_args_to_ts
63
+ ].join(", ")
64
+ end
65
+
66
+ def arg_name(arg)
67
+ name = arg.name.nil? ? next_default_arg_name : arg.name.to_s
68
+ CaseTransform.camel_lower(name)
69
+ end
70
+
71
+ def next_default_arg_name
72
+ @args_count = @args_count + 1
73
+ "arg#{@args_count.to_s}"
74
+ end
75
+
76
+ def required_positional_to_ts
77
+ method_type.required_positionals.map {|arg|
78
+ "#{arg_name(arg)}: #{Converter::Types::Resolver.to_ts(arg.type)}"
79
+ }
80
+ end
81
+
82
+ def optional_positional_to_ts
83
+ method_type.optional_positionals.map {|arg|
84
+ "#{arg_name(arg)}?: #{Converter::Types::Resolver.to_ts(arg.type)}"
85
+ }
86
+ end
87
+
88
+ def rest_positionals_to_ts
89
+ arg = method_type.rest_positionals
90
+
91
+ return [] if arg.nil?
92
+
93
+ has_rest_after_arguments ?
94
+ ["#{arg_name(arg)}#{optional_ts_code}: #{Converter::Types::Resolver.to_ts(arg.type)}[]"] :
95
+ ["...#{arg_name(arg)}#{optional_ts_code}: #{Converter::Types::Resolver.to_ts(arg.type)}[]"]
96
+ end
97
+
98
+ def trailing_positionals_to_ts
99
+ method_type.trailing_positionals.map {|arg|
100
+ "#{arg_name(arg)}#{optional_ts_code}: #{Converter::Types::Resolver.to_ts(arg.type)}"
101
+ }
102
+ end
103
+
104
+ def keyword_args_to_ts
105
+ required_keywords_ts = method_type.required_keywords.map {|key, value|
106
+ "#{CaseTransform.camel_lower(key.to_s)}: #{Converter::Types::Resolver.to_ts(value.type)}"
107
+ }
108
+ optional_keywords_ts = method_type.optional_keywords.map {|key, value|
109
+ "#{CaseTransform.camel_lower(key.to_s)}?: #{Converter::Types::Resolver.to_ts(value.type)}"
110
+ }
111
+ rest_keywords_ts = method_type.rest_keywords.nil? ? [] : ["[key: string]: unknown;"]
112
+
113
+ ts_array = [
114
+ *required_keywords_ts,
115
+ *optional_keywords_ts,
116
+ *rest_keywords_ts
117
+ ]
118
+
119
+ return [] if ts_array.empty?
120
+
121
+ ts = ts_array.join(', ')
122
+
123
+ ["#{next_default_arg_name}#{optional_ts_code}: { #{ts} }"]
124
+ end
125
+
126
+ def method_type
127
+ member.types.first.type
128
+ end
129
+
130
+ def return_type_to_ts
131
+ Converter::Types::Resolver.to_ts(method_type.return_type)
132
+ end
133
+
134
+ def optional_ts_code
135
+ method_type.optional_positionals.present? ? '?' : ''
136
+ end
137
+
138
+ def has_rest_after_arguments
139
+ method_type.trailing_positionals.present? ||
140
+ method_type.required_keywords.present? ||
141
+ method_type.optional_keywords.present?
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -46,33 +46,43 @@ module Rbs2ts
46
46
  end
47
47
 
48
48
  class Record < ConverterBase
49
- INDENT = ' '
50
- @@nest = 0
51
-
52
49
  def to_ts
53
- @@nest = @@nest + 1
54
-
55
- indent = INDENT * @@nest
56
50
  field_lines = type.fields.map { |name, type|
57
- "#{indent}#{CaseTransform.camel_lower(name.to_s)}: #{Types::Resolver.to_ts(type)};"
51
+ "#{CaseTransform.camel_lower(name.to_s)}: #{Types::Resolver.to_ts(type)};"
58
52
  }
59
53
 
60
- @@nest = @@nest - 1
61
-
62
54
  return '{}' if field_lines.empty?
63
55
 
64
56
  field_ts = field_lines.join("\n")
65
57
 
66
58
  ts = <<~CODE
67
59
  {
68
- #{field_ts}
69
- #{INDENT * @@nest}}
60
+ #{Helper.indent(field_ts)}
61
+ }
70
62
  CODE
71
63
 
72
64
  ts.chomp
73
65
  end
74
66
  end
75
67
 
68
+ class Tuple < ConverterBase
69
+ def to_ts
70
+ tuple_types_ts = type.types.map {|t|
71
+ ts = Types::Resolver.to_ts(t)
72
+
73
+ if t.is_a?(::RBS::Types::Union) ||
74
+ t.is_a?(::RBS::Types::Intersection) ||
75
+ t.is_a?(::RBS::Types::Optional)
76
+ "(#{ts})"
77
+ else
78
+ ts
79
+ end
80
+ }.join(', ')
81
+
82
+ "[#{tuple_types_ts}]"
83
+ end
84
+ end
85
+
76
86
  class Literal < ConverterBase
77
87
  def to_ts
78
88
  case type.literal
@@ -121,12 +131,12 @@ module Rbs2ts
121
131
  Types::Array.new(type).to_ts
122
132
  when 'String' then
123
133
  Types::String.new(type).to_ts
124
- when 'Integer' then
134
+ when 'Numeric', 'Integer', 'Float' then
125
135
  Types::Integer.new(type).to_ts
126
136
  when 'Bool' then
127
137
  Types::Bool.new(type).to_ts
128
138
  else
129
- type.name.to_s.gsub(/:/, '')
139
+ type.name.name.to_s.gsub(/:/, '')
130
140
  end
131
141
  end
132
142
  end
@@ -192,6 +202,8 @@ module Rbs2ts
192
202
  Types::Intersection
193
203
  when ::RBS::Types::Record then
194
204
  Types::Record
205
+ when ::RBS::Types::Tuple then
206
+ Types::Tuple
195
207
  else
196
208
  Types::Fallback
197
209
  end
@@ -1,3 +1,3 @@
1
1
  module Rbs2ts
2
- VERSION = "1.0.0-alpha.3"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["mugi-uno"]
10
10
  spec.email = ["mugi.uno@gmail.com"]
11
11
 
12
- spec.summary = "Convert rbs to typescript"
12
+ spec.summary = "A RubyGem that converts Ruby RBS to TypeScript definitions."
13
13
  spec.homepage = "https://github.com/mugi-uno/rbs2ts"
14
14
  spec.license = "MIT"
15
15
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbs2ts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.alpha.3
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mugi-uno
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-14 00:00:00.000000000 Z
11
+ date: 2021-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -104,6 +104,7 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - ".circleci/config.yml"
106
106
  - ".gitignore"
107
+ - ".prettierignore"
107
108
  - ".rspec"
108
109
  - ".travis.yml"
109
110
  - ".vscode/launch.json"
@@ -117,6 +118,8 @@ files:
117
118
  - lib/rbs2ts/cli.rb
118
119
  - lib/rbs2ts/converter.rb
119
120
  - lib/rbs2ts/converter/declarations.rb
121
+ - lib/rbs2ts/converter/helper.rb
122
+ - lib/rbs2ts/converter/members.rb
120
123
  - lib/rbs2ts/converter/types.rb
121
124
  - lib/rbs2ts/version.rb
122
125
  - rbs2ts.gemspec
@@ -135,12 +138,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
138
  version: '0'
136
139
  required_rubygems_version: !ruby/object:Gem::Requirement
137
140
  requirements:
138
- - - ">"
141
+ - - ">="
139
142
  - !ruby/object:Gem::Version
140
- version: 1.3.1
143
+ version: '0'
141
144
  requirements: []
142
145
  rubygems_version: 3.1.4
143
146
  signing_key:
144
147
  specification_version: 4
145
- summary: Convert rbs to typescript
148
+ summary: A RubyGem that converts Ruby RBS to TypeScript definitions.
146
149
  test_files: []