react-rails 1.9.0 → 3.2.1

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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +345 -9
  3. data/README.md +137 -389
  4. data/lib/assets/javascripts/JSXTransformer.js +6 -6
  5. data/lib/assets/javascripts/react_ujs.js +1 -7
  6. data/lib/assets/react-source/development/react-server.js +423 -21528
  7. data/lib/assets/react-source/development/react.js +191 -21409
  8. data/lib/assets/react-source/production/react-server.js +2 -19
  9. data/lib/assets/react-source/production/react.js +2 -19
  10. data/lib/generators/react/component_generator.rb +203 -73
  11. data/lib/generators/react/install_generator.rb +98 -25
  12. data/lib/generators/templates/component.es6.jsx +9 -12
  13. data/lib/generators/templates/component.es6.tsx +24 -0
  14. data/lib/generators/templates/component.js.jsx +15 -18
  15. data/lib/generators/templates/component.js.jsx.coffee +5 -8
  16. data/lib/generators/templates/component.js.jsx.tsx +36 -0
  17. data/lib/generators/templates/react_server_rendering.rb +4 -0
  18. data/lib/generators/templates/server_rendering.js +6 -0
  19. data/lib/generators/templates/server_rendering_pack.js +5 -0
  20. data/lib/react/jsx/babel_transformer.rb +12 -6
  21. data/lib/react/jsx/jsx_transformer.rb +7 -6
  22. data/lib/react/jsx/processor.rb +3 -1
  23. data/lib/react/jsx/sprockets_strategy.rb +12 -6
  24. data/lib/react/jsx/template.rb +7 -6
  25. data/lib/react/jsx.rb +11 -7
  26. data/lib/react/rails/asset_variant.rb +7 -8
  27. data/lib/react/rails/component_mount.rb +48 -14
  28. data/lib/react/rails/controller_lifecycle.rb +36 -7
  29. data/lib/react/rails/controller_renderer.rb +13 -4
  30. data/lib/react/rails/railtie.rb +34 -29
  31. data/lib/react/rails/test_helper.rb +25 -0
  32. data/lib/react/rails/version.rb +4 -2
  33. data/lib/react/rails/view_helper.rb +3 -1
  34. data/lib/react/rails.rb +9 -7
  35. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_replay.js +1 -1
  36. data/lib/react/server_rendering/bundle_renderer/console_reset.js +3 -0
  37. data/lib/react/server_rendering/bundle_renderer/timeout_polyfill.js +26 -0
  38. data/lib/react/server_rendering/bundle_renderer.rb +117 -0
  39. data/lib/react/server_rendering/environment_container.rb +2 -0
  40. data/lib/react/server_rendering/exec_js_renderer.rb +43 -16
  41. data/lib/react/server_rendering/manifest_container.rb +5 -1
  42. data/lib/react/server_rendering/separate_server_bundle_container.rb +19 -0
  43. data/lib/react/server_rendering/yaml_manifest_container.rb +12 -4
  44. data/lib/react/server_rendering.rb +26 -12
  45. data/lib/react-rails.rb +12 -4
  46. data/lib/react.rb +8 -4
  47. metadata +106 -41
  48. data/lib/assets/javascripts/react_ujs_event_setup.js +0 -29
  49. data/lib/assets/javascripts/react_ujs_mount.js +0 -104
  50. data/lib/assets/javascripts/react_ujs_native.js +0 -18
  51. data/lib/assets/javascripts/react_ujs_pjax.js +0 -10
  52. data/lib/assets/javascripts/react_ujs_turbolinks.js +0 -9
  53. data/lib/assets/javascripts/react_ujs_turbolinks_classic.js +0 -10
  54. data/lib/assets/javascripts/react_ujs_turbolinks_classic_deprecated.js +0 -13
  55. data/lib/assets/react-source/development-with-addons/react-server.js +0 -24053
  56. data/lib/assets/react-source/development-with-addons/react.js +0 -23890
  57. data/lib/assets/react-source/production-with-addons/react-server.js +0 -19
  58. data/lib/assets/react-source/production-with-addons/react.js +0 -19
  59. data/lib/generators/react/ujs_generator.rb +0 -44
  60. data/lib/react/server_rendering/sprockets_renderer.rb +0 -79
  61. /data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_polyfill.js +0 -0
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Generators
3
5
  class ComponentGenerator < ::Rails::Generators::NamedBase
4
- source_root File.expand_path '../../templates', __FILE__
6
+ source_root File.expand_path "../templates", __dir__
5
7
  desc <<-DESC.strip_heredoc
6
8
  Description:
7
- Scaffold a react component into app/assets/javascripts/components.
9
+ Scaffold a React component into `components/` of your Shakapacker source or asset pipeline.
8
10
  The generated component will include a basic render function and a PropTypes
9
11
  hash to help with development.
10
12
 
@@ -46,100 +48,228 @@ module React
46
48
  DESC
47
49
 
48
50
  argument :attributes,
49
- :type => :array,
50
- :default => [],
51
- :banner => "field[:type] field[:type] ..."
51
+ type: :array,
52
+ default: [],
53
+ banner: "field[:type] field[:type] ..."
52
54
 
53
55
  class_option :es6,
54
56
  type: :boolean,
55
57
  default: false,
56
- desc: 'Output es6 class based component'
58
+ desc: "Output es6 class based component"
59
+
60
+ class_option :ts,
61
+ type: :boolean,
62
+ default: false,
63
+ desc: "Output tsx class based component"
57
64
 
58
65
  class_option :coffee,
59
66
  type: :boolean,
60
67
  default: false,
61
- desc: 'Output coffeescript based component'
68
+ desc: "Output coffeescript based component"
62
69
 
63
70
  REACT_PROP_TYPES = {
64
- "node" => 'React.PropTypes.node',
65
- "bool" => 'React.PropTypes.bool',
66
- "boolean" => 'React.PropTypes.bool',
67
- "string" => 'React.PropTypes.string',
68
- "number" => 'React.PropTypes.number',
69
- "object" => 'React.PropTypes.object',
70
- "array" => 'React.PropTypes.array',
71
- "shape" => 'React.PropTypes.shape({})',
72
- "element" => 'React.PropTypes.element',
73
- "func" => 'React.PropTypes.func',
74
- "function" => 'React.PropTypes.func',
75
- "any" => 'React.PropTypes.any',
76
-
77
- "instanceOf" => ->(type) {
78
- 'React.PropTypes.instanceOf(%s)' % type.to_s.camelize
71
+ "node" => "PropTypes.node",
72
+ "bool" => "PropTypes.bool",
73
+ "boolean" => "PropTypes.bool",
74
+ "string" => "PropTypes.string",
75
+ "number" => "PropTypes.number",
76
+ "object" => "PropTypes.object",
77
+ "array" => "PropTypes.array",
78
+ "shape" => "PropTypes.shape({})",
79
+ "element" => "PropTypes.element",
80
+ "func" => "PropTypes.func",
81
+ "function" => "PropTypes.func",
82
+ "any" => "PropTypes.any",
83
+
84
+ "instanceOf" => lambda { |type|
85
+ "PropTypes.instanceOf(#{type.to_s.camelize})"
86
+ },
87
+
88
+ "oneOf" => lambda { |*options|
89
+ enums = options.map { |k| "'#{k}'" }.join(",")
90
+ "PropTypes.oneOf([#{enums}])"
79
91
  },
80
92
 
81
- "oneOf" => ->(*options) {
82
- enums = options.map{|k| "'#{k.to_s}'"}.join(',')
83
- 'React.PropTypes.oneOf([%s])' % enums
93
+ "oneOfType" => lambda { |*options|
94
+ types = options.map { |k| lookup(k.to_s, k.to_s).to_s }.join(",")
95
+ "PropTypes.oneOfType([#{types}])"
96
+ }
97
+ }.freeze
98
+
99
+ TYPESCRIPT_TYPES = {
100
+ "node" => "React.ReactNode",
101
+ "bool" => "boolean",
102
+ "boolean" => "boolean",
103
+ "string" => "string",
104
+ "number" => "number",
105
+ "object" => "object",
106
+ "array" => "Array<any>",
107
+ "shape" => "object",
108
+ "element" => "object",
109
+ "func" => "object",
110
+ "function" => "object",
111
+ "any" => "any",
112
+
113
+ "instanceOf" => lambda { |type|
114
+ type.to_s.camelize
84
115
  },
85
116
 
86
- "oneOfType" => ->(*options) {
87
- types = options.map{|k| "#{lookup(k.to_s, k.to_s)}" }.join(',')
88
- 'React.PropTypes.oneOfType([%s])' % types
117
+ "oneOf" => lambda { |*opts|
118
+ opts.map { |k| "'#{k}'" }.join(" | ")
89
119
  },
90
- }
120
+
121
+ "oneOfType" => lambda { |*opts|
122
+ opts.map { |k| ts_lookup(k.to_s, k.to_s).to_s }.join(" | ")
123
+ }
124
+ }.freeze
91
125
 
92
126
  def create_component_file
93
- extension = case
94
- when options[:es6]
95
- 'es6.jsx'
96
- when options[:coffee]
97
- 'js.jsx.coffee'
127
+ template_extension = detect_template_extension
128
+ # Prefer Shakapacker to Sprockets:
129
+ if shakapacker?
130
+ new_file_name = file_name.camelize
131
+ extension = if options[:coffee]
132
+ "coffee"
133
+ elsif options[:ts]
134
+ "tsx"
98
135
  else
99
- 'js.jsx'
100
- end
136
+ "js"
137
+ end
138
+ target_dir = webpack_configuration.source_path
139
+ .join("components")
140
+ .relative_path_from(::Rails.root)
141
+ .to_s
142
+ else
143
+ new_file_name = file_name
144
+ extension = template_extension
145
+ target_dir = "app/assets/javascripts/components"
146
+ end
101
147
 
102
- file_path = File.join('app/assets/javascripts/components', "#{file_name}.#{extension}")
103
- template("component.#{extension}", file_path)
148
+ file_path = File.join(target_dir, class_path, "#{new_file_name}.#{extension}")
149
+ template("component.#{template_extension}", file_path)
104
150
  end
105
151
 
106
152
  private
107
153
 
108
- def parse_attributes!
109
- self.attributes = (attributes || []).map do |attr|
110
- name, type, options = "", "", ""
111
- options_regex = /(?<options>{.*})/
112
-
113
- name, type = attr.split(':')
114
-
115
- if matchdata = options_regex.match(type)
116
- options = matchdata[:options]
117
- type = type.gsub(options_regex, '')
118
- end
119
-
120
- { :name => name, :type => lookup(type, options) }
121
- end
122
- end
123
-
124
- def self.lookup(type = "node", options = "")
125
- react_prop_type = REACT_PROP_TYPES[type]
126
- if react_prop_type.blank?
127
- if type =~ /^[[:upper:]]/
128
- react_prop_type = REACT_PROP_TYPES['instanceOf']
129
- else
130
- react_prop_type = REACT_PROP_TYPES['node']
131
- end
132
- end
133
-
134
- options = options.to_s.gsub(/[{}]/, '').split(',')
135
-
136
- react_prop_type = react_prop_type.call(*options) if react_prop_type.respond_to? :call
137
- react_prop_type
138
- end
139
-
140
- def lookup(type = "node", options = "")
141
- self.class.lookup(type, options)
142
- end
154
+ def webpack_configuration
155
+ Shakapacker.respond_to?(:config) ? Shakapacker.config : Shakapacker::Configuration
156
+ end
157
+
158
+ def component_name
159
+ file_name.camelize
160
+ end
161
+
162
+ def file_header
163
+ if shakapacker?
164
+ return %(import * as React from "react"\n) if options[:ts]
165
+
166
+ <<~JS
167
+ import React from "react"
168
+ import PropTypes from "prop-types"
169
+ JS
170
+ else
171
+ ""
172
+ end
173
+ end
174
+
175
+ def file_footer
176
+ if shakapacker?
177
+ %(export default #{component_name})
178
+ else
179
+ ""
180
+ end
181
+ end
182
+
183
+ def shakapacker?
184
+ defined?(Shakapacker)
185
+ end
186
+
187
+ def parse_attributes!
188
+ self.attributes = (attributes || []).map do |attr|
189
+ args = ""
190
+ args_regex = /(?<args>{.*})/
191
+
192
+ name, type = attr.split(":")
193
+
194
+ if (matchdata = args_regex.match(type))
195
+ args = matchdata[:args]
196
+ type = type.gsub(args_regex, "")
197
+ end
198
+
199
+ if options[:ts]
200
+ { name: name, type: ts_lookup(name, type, args), union: union?(args) }
201
+ else
202
+ { name: name, type: lookup(type, args) }
203
+ end
204
+ end
205
+ end
206
+
207
+ def union?(args = "")
208
+ args.to_s.gsub(/[{}]/, "").split(",").count > 1
209
+ end
210
+
211
+ def self.ts_lookup(_name, type = "node", args = "")
212
+ ts_type = TYPESCRIPT_TYPES[type]
213
+ if ts_type.blank?
214
+ ts_type = if /^[[:upper:]]/.match?(type)
215
+ TYPESCRIPT_TYPES["instanceOf"]
216
+ else
217
+ TYPESCRIPT_TYPES["node"]
218
+ end
219
+ end
220
+
221
+ args = args.to_s.gsub(/[{}]/, "").split(",")
222
+
223
+ if ts_type.respond_to? :call
224
+ return ts_type.call(type) if args.blank?
225
+
226
+ ts_type = ts_type.call(*args)
227
+ end
228
+
229
+ ts_type
230
+ end
231
+
232
+ def ts_lookup(name, type = "node", args = "")
233
+ self.class.ts_lookup(name, type, args)
234
+ end
235
+
236
+ def self.lookup(type = "node", options = "")
237
+ react_prop_type = REACT_PROP_TYPES[type]
238
+ if react_prop_type.blank?
239
+ react_prop_type = if /^[[:upper:]]/.match?(type)
240
+ REACT_PROP_TYPES["instanceOf"]
241
+ else
242
+ REACT_PROP_TYPES["node"]
243
+ end
244
+ end
245
+
246
+ options = options.to_s.gsub(/[{}]/, "").split(",")
247
+
248
+ react_prop_type = react_prop_type.call(*options) if react_prop_type.respond_to? :call
249
+ react_prop_type
250
+ end
251
+
252
+ def lookup(type = "node", options = "")
253
+ self.class.lookup(type, options)
254
+ end
255
+
256
+ def detect_template_extension
257
+ if options[:coffee]
258
+ "js.jsx.coffee"
259
+ elsif options[:ts] && es6_enabled?
260
+ "es6.tsx"
261
+ elsif options[:ts]
262
+ "js.jsx.tsx"
263
+ elsif es6_enabled?
264
+ "es6.jsx"
265
+ else
266
+ "js.jsx"
267
+ end
268
+ end
269
+
270
+ def es6_enabled?
271
+ options[:es6] || shakapacker?
272
+ end
143
273
  end
144
274
  end
145
275
  end
@@ -1,30 +1,88 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module React
2
4
  module Generators
3
5
  class InstallGenerator < ::Rails::Generators::Base
4
- source_root File.expand_path '../../templates', __FILE__
6
+ source_root File.expand_path "../templates", __dir__
5
7
 
6
- desc 'Create default react.js folder layout and prep application.js'
8
+ desc "Create default react.js folder layout and prep application.js"
7
9
 
8
10
  class_option :skip_git,
9
- type: :boolean,
10
- aliases: '-g',
11
- default: false,
12
- desc: 'Skip Git keeps'
11
+ type: :boolean,
12
+ aliases: "-g",
13
+ default: false,
14
+ desc: "Skip Git keeps"
15
+
16
+ class_option :skip_server_rendering,
17
+ type: :boolean,
18
+ default: false,
19
+ desc: "Don't generate server_rendering.js or config/initializers/react_server_rendering.rb"
13
20
 
21
+ # Make an empty `components/` directory in the right place:
14
22
  def create_directory
15
- empty_directory 'app/assets/javascripts/components'
16
- create_file 'app/assets/javascripts/components/.gitkeep' unless options[:skip_git]
23
+ components_dir = if shakapacker?
24
+ Pathname.new(javascript_dir).parent.to_s
25
+ else
26
+ javascript_dir
27
+ end
28
+ empty_directory File.join(components_dir, "components")
29
+ return if options[:skip_git]
30
+
31
+ create_file File.join(components_dir, "components/.keep")
32
+ end
33
+
34
+ # Add requires, setup UJS
35
+ def setup_react
36
+ if shakapacker?
37
+ setup_react_shakapacker
38
+ else
39
+ setup_react_sprockets
40
+ end
41
+ end
42
+
43
+ def create_server_rendering
44
+ if options[:skip_server_rendering]
45
+ nil
46
+ elsif shakapacker?
47
+ ssr_manifest_path = File.join(javascript_dir, "server_rendering.js")
48
+ template("server_rendering_pack.js", ssr_manifest_path)
49
+ else
50
+ ssr_manifest_path = File.join(javascript_dir, "server_rendering.js")
51
+ template("server_rendering.js", ssr_manifest_path)
52
+ initializer_path = "config/initializers/react_server_rendering.rb"
53
+ template("react_server_rendering.rb", initializer_path)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def shakapacker?
60
+ !!defined?(Shakapacker)
61
+ end
62
+
63
+ def javascript_dir
64
+ if shakapacker?
65
+ shakapacker_source_path
66
+ .relative_path_from(::Rails.root)
67
+ .to_s
68
+ else
69
+ "app/assets/javascripts"
70
+ end
17
71
  end
18
72
 
19
- def inject_react
20
- require_react = "//= require react\n"
73
+ def manifest
74
+ Pathname.new(destination_root).join(javascript_dir, "application.js")
75
+ end
76
+
77
+ def setup_react_sprockets
78
+ require_react = "//= require react\n//= require react_ujs\n//= require components\n"
21
79
 
22
80
  if manifest.exist?
23
81
  manifest_contents = File.read(manifest)
24
82
 
25
- if match = manifest_contents.match(/\/\/=\s+require\s+turbolinks\s+\n/)
83
+ if (match = manifest_contents.match(%r{//=\s+require\s+turbolinks\s+\n}))
26
84
  inject_into_file manifest, require_react, { after: match[0] }
27
- elsif match = manifest_contents.match(/\/\/=\s+require_tree[^\n]*/)
85
+ elsif (match = manifest_contents.match(%r{//=\s+require_tree[^\n]*}))
28
86
  inject_into_file manifest, require_react, { before: match[0] }
29
87
  else
30
88
  append_file manifest, require_react
@@ -32,26 +90,41 @@ module React
32
90
  else
33
91
  create_file manifest, require_react
34
92
  end
35
- end
36
93
 
37
- def inject_components
38
- inject_into_file manifest, "//= require components\n", {after: "//= require react\n"}
94
+ components_js = "//= require_tree ./components\n"
95
+ components_file = File.join(javascript_dir, "components.js")
96
+ create_file components_file, components_js
39
97
  end
40
98
 
41
- def inject_react_ujs
42
- inject_into_file manifest, "//= require react_ujs\n", {after: "//= require react\n"}
43
- end
99
+ SHAKAPACKER_SETUP_UJS = <<~JS
100
+ // Support component names relative to this directory:
101
+ var componentRequireContext = require.context("components", true);
102
+ var ReactRailsUJS = require("react_ujs");
103
+ ReactRailsUJS.useContext(componentRequireContext);
104
+ JS
44
105
 
45
- def create_components
46
- components_js = "//= require_tree ./components\n"
47
- components_file = File.join(*%w(app assets javascripts components.js))
48
- create_file components_file, components_js
106
+ def require_package_json_gem
107
+ require "bundler/inline"
108
+
109
+ gemfile(true) { gem "package_json" }
110
+
111
+ puts "using package_json v#{PackageJson::VERSION}"
49
112
  end
50
113
 
51
- private
114
+ def setup_react_shakapacker
115
+ require_package_json_gem
52
116
 
53
- def manifest
54
- Pathname.new(destination_root).join('app/assets/javascripts', 'application.js')
117
+ PackageJson.read.manager.add(["react_ujs"])
118
+
119
+ if manifest.exist?
120
+ append_file(manifest, SHAKAPACKER_SETUP_UJS)
121
+ else
122
+ create_file(manifest, SHAKAPACKER_SETUP_UJS)
123
+ end
124
+ end
125
+
126
+ def shakapacker_source_path
127
+ Shakapacker.config.source_entry_path
55
128
  end
56
129
  end
57
130
  end
@@ -1,17 +1,12 @@
1
- class <%= file_name.camelize %> extends React.Component {
2
- render () {
3
- <% if attributes.size > 0 -%>
4
- return (
5
- <div>
1
+ <%= file_header %>
2
+ const <%= component_name %> = (props) => {
3
+ return (
4
+ <React.Fragment>
6
5
  <% attributes.each do |attribute| -%>
7
- <div><%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}</div>
8
- <% end -%>
9
- </div>
10
- );
11
- <% else -%>
12
- return <div />;
6
+ <%= attribute[:name].titleize %>: {props.<%= attribute[:name].camelize(:lower) %>}
13
7
  <% end -%>
14
- }
8
+ </React.Fragment>
9
+ )
15
10
  }
16
11
 
17
12
  <% if attributes.size > 0 -%>
@@ -21,3 +16,5 @@ class <%= file_name.camelize %> extends React.Component {
21
16
  <% end -%>
22
17
  };
23
18
  <% end -%>
19
+
20
+ <%= file_footer %>
@@ -0,0 +1,24 @@
1
+ <%= file_header %>
2
+ interface I<%= component_name %>Props {
3
+ <% if attributes.size > 0 -%>
4
+ <% attributes.each do |attribute| -%>
5
+ <% if attribute[:union] -%>
6
+ <%= attribute[:name].camelize(:lower) %>: <%= attribute[:name].titleize %>;
7
+ <% else -%>
8
+ <%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %>;
9
+ <% end -%>
10
+ <% end -%>
11
+ <% end -%>
12
+ }
13
+
14
+ const <%= component_name %> = (props: I<%= component_name %>Props) => {
15
+ return (
16
+ <React.Fragment>
17
+ <% attributes.each do |attribute| -%>
18
+ <%= attribute[:name].titleize %>: {props.<%= attribute[:name].camelize(:lower) %>}
19
+ <% end -%>
20
+ </React.Fragment>
21
+ )
22
+ }
23
+
24
+ <%= file_footer %>
@@ -1,23 +1,20 @@
1
- var <%= file_name.camelize %> = React.createClass({
2
- <% if attributes.size > 0 -%>
3
- propTypes: {
4
- <% attributes.each_with_index do |attribute, idx| -%>
5
- <%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %><% if (idx < attributes.length-1) %>,<% end %>
6
- <% end -%>
7
- },
1
+ <%= file_header %>
2
+ function <%= component_name %>(props) {
3
+ return (
4
+ <React.Fragment>
5
+ <% attributes.each do |attribute| -%>
6
+ <%= attribute[:name].titleize %>: {props.<%= attribute[:name].camelize(:lower) %>}
8
7
  <% end -%>
8
+ </React.Fragment>
9
+ );
10
+ }
9
11
 
10
- render: function() {
11
12
  <% if attributes.size > 0 -%>
12
- return (
13
- <div>
14
- <% attributes.each do |attribute| -%>
15
- <div><%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}</div>
13
+ <%= file_name.camelize %>.propTypes = {
14
+ <% attributes.each_with_index do |attribute, idx| -%>
15
+ <%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %><% if (idx < attributes.length-1) %>,<% end %>
16
16
  <% end -%>
17
- </div>
18
- );
19
- <% else -%>
20
- return <div />;
17
+ };
21
18
  <% end -%>
22
- }
23
- });
19
+
20
+ <%= file_footer %>
@@ -1,4 +1,4 @@
1
- class @<%= file_name.camelize %> extends React.Component
1
+ <%= file_header %>class <%= component_name %> extends React.Component
2
2
  <% if attributes.size > 0 -%>
3
3
  @propTypes =
4
4
  <% attributes.each do |attribute| -%>
@@ -7,12 +7,9 @@ class @<%= file_name.camelize %> extends React.Component
7
7
 
8
8
  <% end -%>
9
9
  render: ->
10
- <% if attributes.size > 0 -%>
11
- `<div>
10
+ `<React.Fragment>
12
11
  <% attributes.each do |attribute| -%>
13
- <div><%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}</div>
12
+ <%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}
14
13
  <% end -%>
15
- </div>`
16
- <% else -%>
17
- `<div />`
18
- <% end -%>
14
+ </React.Fragment>`
15
+ <%= file_footer %>
@@ -0,0 +1,36 @@
1
+ <%= file_header %>
2
+ <% unions = attributes.select{ |a| a[:union] } -%>
3
+ <% if unions.size > 0 -%>
4
+ <% unions.each do |e| -%>
5
+ type <%= e[:name].titleize %> = <%= e[:type]%>
6
+ <% end -%>
7
+ <% end -%>
8
+
9
+ interface I<%= component_name %>Props {
10
+ <% if attributes.size > 0 -%>
11
+ <% attributes.each do | attribute | -%>
12
+ <% if attribute[:union] -%>
13
+ <%= attribute[:name].camelize(:lower) %>: <%= attribute[:name].titleize %>;
14
+ <% else -%>
15
+ <%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %>;
16
+ <% end -%>
17
+ <% end -%>
18
+ <% end -%>
19
+ }
20
+
21
+ interface I<%= component_name %>State {
22
+ }
23
+
24
+ class <%= component_name %> extends React.Component <I<%= component_name %>Props, I<%= component_name %>State> {
25
+ render() {
26
+ return (
27
+ <React.Fragment>
28
+ <% attributes.each do |attribute| -%>
29
+ <%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}
30
+ <% end -%>
31
+ </React.Fragment>
32
+ );
33
+ }
34
+ }
35
+
36
+ <%= file_footer %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # To render React components in production, precompile the server rendering manifest:
4
+ Rails.application.config.assets.precompile += ["server_rendering.js"]
@@ -0,0 +1,6 @@
1
+ //= require react-server
2
+ //= require react_ujs
3
+ //= require ./components
4
+ //
5
+ // By default, this file is loaded for server-side rendering.
6
+ // It should require your components and any dependencies.
@@ -0,0 +1,5 @@
1
+ // By default, this pack is loaded for server-side rendering.
2
+ // It must expose react_ujs as `ReactRailsUJS` and prepare a require context.
3
+ var componentRequireContext = require.context("components", true);
4
+ var ReactRailsUJS = require("react_ujs");
5
+ ReactRailsUJS.useContext(componentRequireContext);