@@ -55,6 +55,11 @@ class ComponentGenerator < ::Rails::Generators::NamedBase
55
55
default : false ,
56
56
desc : 'Output es6 class based component'
57
57
58
+ class_option :ts ,
59
+ type : :boolean ,
60
+ default : false ,
61
+ desc : 'Output tsx class based component'
62
+
58
63
class_option :coffee ,
59
64
type : :boolean ,
60
65
default : false ,
@@ -89,9 +94,38 @@ class ComponentGenerator < ::Rails::Generators::NamedBase
89
94
}
90
95
}
91
96
97
+ TYPESCRIPT_TYPES = {
98
+ 'node' => 'React.ReactNode' ,
99
+ 'bool' => 'boolean' ,
100
+ 'boolean' => 'boolean' ,
101
+ 'string' => 'string' ,
102
+ 'number' => 'number' ,
103
+ 'object' => 'object' ,
104
+ 'array' => 'Array<any>' ,
105
+ 'shape' => 'object' ,
106
+ 'element' => 'object' ,
107
+ 'func' => 'object' ,
108
+ 'function' => 'object' ,
109
+ 'any' => 'any' ,
110
+
111
+ 'instanceOf' => -> ( type ) {
112
+ type . to_s . camelize
113
+ } ,
114
+
115
+ 'oneOf' => -> ( *opts ) {
116
+ opts . map { |k | "'#{ k . to_s } '" } . join ( " | " )
117
+ } ,
118
+
119
+ 'oneOfType' => -> ( *opts ) {
120
+ opts . map { |k | "#{ ts_lookup ( k . to_s , k . to_s ) } " } . join ( " | " )
121
+ }
122
+ }
123
+
92
124
def create_component_file
93
125
template_extension = if options [ :coffee ]
94
126
'js.jsx.coffee'
127
+ elsif options [ :ts ]
128
+ 'js.jsx.tsx'
95
129
elsif options [ :es6 ] || webpacker?
96
130
'es6.jsx'
97
131
else
@@ -101,7 +135,13 @@ def create_component_file
101
135
# Prefer webpacker to sprockets:
102
136
if webpacker?
103
137
new_file_name = file_name . camelize
104
- extension = options [ :coffee ] ? 'coffee' : 'js'
138
+ extension = if options [ :coffee ]
139
+ 'coffee'
140
+ elsif options [ :ts ]
141
+ 'tsx'
142
+ else
143
+ 'js'
144
+ end
105
145
target_dir = webpack_configuration . source_path
106
146
. join ( 'components' )
107
147
. relative_path_from ( ::Rails . root )
@@ -128,6 +168,7 @@ def component_name
128
168
129
169
def file_header
130
170
if webpacker?
171
+ return %|import * as React from "react"\n | if options [ :ts ]
131
172
%|import React from "react"\n import PropTypes from "prop-types"\n |
132
173
else
133
174
''
@@ -146,23 +187,58 @@ def webpacker?
146
187
defined? ( Webpacker )
147
188
end
148
189
149
- def parse_attributes!
150
- self . attributes = ( attributes || [ ] ) . map do |attr |
151
- name = ''
152
- type = ''
153
- options = ''
154
- options_regex = /(?<options>{.*})/
190
+ def parse_attributes!
191
+ self . attributes = ( attributes || [ ] ) . map do |attr |
192
+ name = ''
193
+ type = ''
194
+ args = ''
195
+ args_regex = /(?<args>{.*})/
196
+
197
+ name , type = attr . split ( ':' )
198
+
199
+ if matchdata = args_regex . match ( type )
200
+ args = matchdata [ :args ]
201
+ type = type . gsub ( args_regex , '' )
202
+ end
203
+
204
+ if options [ :ts ]
205
+ { :name => name , :type => ts_lookup ( name , type , args ) , :union => union? ( args ) }
206
+ else
207
+ { :name => name , :type => lookup ( type , args ) }
208
+ end
209
+ end
210
+ end
211
+
212
+ def union? ( args = '' )
213
+ return args . to_s . gsub ( /[{}]/ , '' ) . split ( ',' ) . count > 1
214
+ end
155
215
156
- name , type = attr . split ( ':' )
216
+ def self . ts_lookup ( name , type = 'node' , args = '' )
217
+ ts_type = TYPESCRIPT_TYPES [ type ]
218
+ if ts_type . blank?
219
+ if type =~ /^[[:upper:]]/
220
+ ts_type = TYPESCRIPT_TYPES [ 'instanceOf' ]
221
+ else
222
+ ts_type = TYPESCRIPT_TYPES [ 'node' ]
223
+ end
224
+ end
157
225
158
- if matchdata = options_regex . match ( type )
159
- options = matchdata [ :options ]
160
- type = type . gsub ( options_regex , '' )
161
- end
226
+ args = args . to_s . gsub ( /[{}]/ , '' ) . split ( ',' )
162
227
163
- { :name => name , :type => lookup ( type , options ) }
164
- end
165
- end
228
+ if ts_type . respond_to? :call
229
+ if args . blank?
230
+ return ts_type . call ( type )
231
+ end
232
+
233
+ ts_type = ts_type . call ( *args )
234
+ end
235
+
236
+ ts_type
237
+ end
238
+
239
+ def ts_lookup ( name , type = 'node' , args = '' )
240
+ self . class . ts_lookup ( name , type , args )
241
+ end
166
242
167
243
def self . lookup ( type = 'node' , options = '' )
168
244
react_prop_type = REACT_PROP_TYPES [ type ]
0 commit comments