isomorfeus-react 16.13.11 → 16.13.12

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/lib/isomorfeus/react/config.rb +189 -189
  4. data/lib/isomorfeus/react/memcached_component_cache.rb +19 -19
  5. data/lib/isomorfeus/react/redis_component_cache.rb +19 -19
  6. data/lib/isomorfeus/react/thread_local_component_cache.rb +15 -15
  7. data/lib/isomorfeus/react_view_helper.rb +231 -231
  8. data/lib/isomorfeus/top_level.rb +103 -103
  9. data/lib/isomorfeus/top_level_ssr.rb +42 -42
  10. data/lib/isomorfeus-react-material-ui.rb +4 -4
  11. data/lib/isomorfeus-react-native.rb +5 -5
  12. data/lib/isomorfeus-react-paper.rb +4 -4
  13. data/lib/isomorfeus-react.rb +120 -120
  14. data/lib/isomorfeus_react/lucid_app/api.rb +26 -26
  15. data/lib/isomorfeus_react/lucid_app/base.rb +7 -7
  16. data/lib/isomorfeus_react/lucid_app/mixin.rb +23 -23
  17. data/lib/isomorfeus_react/lucid_app/native_component_constructor.rb +48 -48
  18. data/lib/isomorfeus_react/lucid_app/native_lucid_component_constructor.rb +94 -95
  19. data/lib/isomorfeus_react/lucid_component/api.rb +75 -75
  20. data/lib/isomorfeus_react/lucid_component/app_store_proxy.rb +37 -37
  21. data/lib/isomorfeus_react/lucid_component/base.rb +7 -7
  22. data/lib/isomorfeus_react/lucid_component/class_store_proxy.rb +44 -44
  23. data/lib/isomorfeus_react/lucid_component/initializer.rb +14 -14
  24. data/lib/isomorfeus_react/lucid_component/instance_store_proxy.rb +44 -44
  25. data/lib/isomorfeus_react/lucid_component/mixin.rb +22 -22
  26. data/lib/isomorfeus_react/lucid_component/native_component_constructor.rb +35 -35
  27. data/lib/isomorfeus_react/lucid_component/native_lucid_component_constructor.rb +82 -83
  28. data/lib/isomorfeus_react/lucid_component/styles_api.rb +34 -34
  29. data/lib/isomorfeus_react/lucid_func/base.rb +7 -7
  30. data/lib/isomorfeus_react/lucid_func/initializer.rb +11 -11
  31. data/lib/isomorfeus_react/lucid_func/mixin.rb +18 -18
  32. data/lib/isomorfeus_react/lucid_func/native_component_constructor.rb +81 -81
  33. data/lib/isomorfeus_react/react/function_component/api.rb +105 -105
  34. data/lib/isomorfeus_react/react/function_component/base.rb +8 -8
  35. data/lib/isomorfeus_react/react/function_component/initializer.rb +10 -10
  36. data/lib/isomorfeus_react/react/function_component/mixin.rb +17 -17
  37. data/lib/isomorfeus_react/react/function_component/native_component_constructor.rb +48 -48
  38. data/lib/isomorfeus_react/react/memo_component/base.rb +8 -8
  39. data/lib/isomorfeus_react/react/memo_component/mixin.rb +17 -17
  40. data/lib/isomorfeus_react/react/memo_component/native_component_constructor.rb +49 -49
  41. data/lib/isomorfeus_react_material/lucid_material/app/base.rb +8 -8
  42. data/lib/isomorfeus_react_material/lucid_material/app/mixin.rb +20 -20
  43. data/lib/isomorfeus_react_material/lucid_material/app/native_component_constructor.rb +50 -50
  44. data/lib/isomorfeus_react_material/lucid_material/component/base.rb +9 -9
  45. data/lib/isomorfeus_react_material/lucid_material/component/mixin.rb +19 -19
  46. data/lib/isomorfeus_react_material/lucid_material/component/native_component_constructor.rb +36 -36
  47. data/lib/isomorfeus_react_material/lucid_material/func/base.rb +9 -9
  48. data/lib/isomorfeus_react_material/lucid_material/func/mixin.rb +15 -15
  49. data/lib/isomorfeus_react_material/lucid_material/func/native_component_constructor.rb +83 -83
  50. data/lib/isomorfeus_react_paper/lucid_paper/app/base.rb +9 -9
  51. data/lib/isomorfeus_react_paper/lucid_paper/app/mixin.rb +19 -19
  52. data/lib/isomorfeus_react_paper/lucid_paper/app/native_component_constructor.rb +32 -32
  53. data/lib/isomorfeus_react_paper/lucid_paper/component/base.rb +9 -9
  54. data/lib/isomorfeus_react_paper/lucid_paper/component/mixin.rb +18 -18
  55. data/lib/isomorfeus_react_paper/lucid_paper/component/native_component_constructor.rb +25 -25
  56. data/lib/isomorfeus_react_paper/lucid_paper/func/base.rb +9 -9
  57. data/lib/isomorfeus_react_paper/lucid_paper/func/mixin.rb +14 -14
  58. data/lib/isomorfeus_react_paper/lucid_paper/func/native_component_constructor.rb +71 -71
  59. data/lib/lucid_app/context.rb +7 -7
  60. data/lib/lucid_prop_declaration/mixin.rb +126 -126
  61. data/lib/react/children.rb +34 -34
  62. data/lib/react/component/api.rb +134 -134
  63. data/lib/react/component/base.rb +8 -8
  64. data/lib/react/component/callbacks.rb +115 -115
  65. data/lib/react/component/elements.rb +60 -60
  66. data/lib/react/component/features.rb +48 -48
  67. data/lib/react/component/history.rb +69 -65
  68. data/lib/react/component/initializer.rb +11 -11
  69. data/lib/react/component/location.rb +19 -15
  70. data/lib/react/component/match.rb +35 -31
  71. data/lib/react/component/mixin.rb +20 -20
  72. data/lib/react/component/native_component_constructor.rb +69 -70
  73. data/lib/react/component/props.rb +83 -83
  74. data/lib/react/component/resolution.rb +97 -97
  75. data/lib/react/component/state.rb +58 -54
  76. data/lib/react/component/styles.rb +66 -66
  77. data/lib/react/context_wrapper.rb +48 -44
  78. data/lib/react/native_constant_wrapper.rb +29 -29
  79. data/lib/react/ref.rb +16 -12
  80. data/lib/react/synthetic_event.rb +52 -52
  81. data/lib/react/version.rb +3 -3
  82. data/lib/react.rb +296 -296
  83. data/lib/react_dom.rb +41 -41
  84. data/lib/react_dom_server.rb +18 -18
  85. data/lib/react_native/component/elements.rb +203 -203
  86. data/lib/react_native/lucid_app/react_native_component_constructor.rb +51 -51
  87. data/lib/react_native/lucid_component/react_native_component_constructor.rb +37 -37
  88. data/lib/react_native/lucid_func/react_native_component_constructor.rb +82 -82
  89. data/lib/react_native/react.rb +120 -120
  90. metadata +23 -17
@@ -1,231 +1,231 @@
1
- module Isomorfeus
2
- module ReactViewHelper
3
- def cached_mount_component(component_name, props = {}, asset = 'web_ssr.js', static = false)
4
- key = "#{component_name}#{props}#{asset}"
5
- if Isomorfeus.production?
6
- render_result, @ssr_response_status, @ssr_styles = component_cache.fetch(key)
7
- return render_result if render_result
8
- end
9
- render_result = mount_component(component_name, props, asset, static)
10
- status = ssr_response_status
11
- component_cache.store(key, render_result, status, ssr_styles) if status >= 200 && status < 300
12
- render_result
13
- end
14
-
15
- def cached_mount_static_component(component_name, props = {}, asset = 'web_ssr.js')
16
- cached_mount_component(component_name, props, asset, true)
17
- end
18
-
19
- def mount_component(component_name, props = {}, asset = 'web_ssr.js', static = false)
20
- @ssr_response_status = nil
21
- @ssr_styles = nil
22
- thread_id_asset = "#{Thread.current.object_id}#{asset}"
23
- render_result = if static
24
- '<div>'
25
- else
26
- "<div data-iso-env=\"#{Isomorfeus.env}\" data-iso-root=\"#{component_name}\" data-iso-props='#{Oj.dump(props, mode: :strict)}'"
27
- end
28
- if Isomorfeus.server_side_rendering
29
-
30
- if Isomorfeus.development?
31
- # always create a new context, effectively reloading code
32
- # delete the existing context first, saves memory
33
- if Isomorfeus.ssr_contexts.key?(thread_id_asset)
34
- uuid = Isomorfeus.ssr_contexts[thread_id_asset].instance_variable_get(:@uuid)
35
- runtime = Isomorfeus.ssr_contexts[thread_id_asset].instance_variable_get(:@runtime)
36
- runtime.vm.delete_context(uuid)
37
- end
38
- asset_path = "#{Isomorfeus.ssr_hot_asset_url}#{asset}"
39
- begin
40
- asset = Net::HTTP.get(URI(asset_path))
41
- rescue Exception => e
42
- Isomorfeus.raise_error(message: "Server Side Rendering: Failed loading asset #{asset_path} from webpack dev server. Error: #{e.message}", stack: e.backtrace )
43
- end
44
- if asset.strip.start_with?('<')
45
- Isomorfeus.raise_error(message: "Server Side Rendering: Failed loading asset #{asset_path} from webpack dev server, asset is not javascript. Did the webpack build succeed?")
46
- end
47
- begin
48
- Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(asset)
49
- rescue Exception => e
50
- Isomorfeus.raise_error(message: "Server Side Rendering: Failed creating context for #{asset_path}. Error: #{e.message}", stack: e.backtrace)
51
- end
52
- else
53
- # initialize speednode context
54
- unless Isomorfeus.ssr_contexts.key?(thread_id_asset)
55
- asset_file_name = OpalWebpackLoader::Manifest.lookup_path_for(asset)
56
- Isomorfeus.raise_error(message: "Server Side Rendering: Build asset file not found for #{asset}. Has it been build?") unless asset_file_name
57
- asset_path = File.join('public', asset_file_name)
58
- Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(File.read(asset_path))
59
- end
60
- end
61
-
62
- # build javascript for rendering first pass
63
- # it will initialize buffers to guard against leaks, maybe caused by previous exceptions
64
- javascript = <<~JAVASCRIPT
65
- global.Opal.React.render_buffer = [];
66
- global.Opal.React.active_components = [];
67
- global.Opal.React.active_redux_components = [];
68
- global.FirstPassFinished = false;
69
- global.Exception = false;
70
- global.IsomorfeusSessionId = '#{Thread.current[:isomorfeus_session_id]}';
71
- global.Opal.Isomorfeus['$env=']('#{Isomorfeus.env}');
72
- if (typeof global.Opal.Isomorfeus.$negotiated_locale === 'function') {
73
- global.Opal.Isomorfeus["$negotiated_locale="]('#{props[:locale]}');
74
- }
75
- global.Opal.Isomorfeus['$force_init!']();
76
- global.Opal.Isomorfeus['$ssr_response_status='](200);
77
- global.Opal.Isomorfeus.TopLevel['$ssr_route_path=']('#{props[:location]}');
78
- JAVASCRIPT
79
-
80
- # if location_host and scheme are given and if Transport is loaded, connect and then render,
81
- # otherwise do not render because only one pass is required
82
- ws_scheme = props[:location_scheme] == 'https:' ? 'wss:' : 'ws:'
83
- location_host = props[:location_host] ? props[:location_host] : 'localhost'
84
- api_ws_path = Isomorfeus.respond_to?(:api_websocket_path) ? Isomorfeus.api_websocket_path : ''
85
- transport_ws_url = ws_scheme + location_host + api_ws_path
86
- javascript << <<~JAVASCRIPT
87
- let api_ws_path = '#{api_ws_path}';
88
- let exception;
89
- if (typeof global.Opal.Isomorfeus.Transport !== 'undefined' && api_ws_path !== '') {
90
- global.Opal.Isomorfeus.TopLevel["$transport_ws_url="]("#{transport_ws_url}");
91
- global.Opal.send(global.Opal.Isomorfeus.Transport.$promise_connect(), 'then', [], ($$1 = function(){
92
- try {
93
- if (#{static}) { global.Opal.Isomorfeus.TopLevel.$render_component_to_static_markup('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
94
- else { global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
95
- global.FirstPassFinished = 'transport';
96
- } catch (e) {
97
- global.Exception = e;
98
- global.FirstPassFinished = 'transport';
99
- }
100
- }, $$1.$$s = this, $$1.$$arity = 0, $$1))
101
- } else { return global.FirstPassFinished = true; };
102
- JAVASCRIPT
103
-
104
- # execute first render pass
105
- first_pass_skipped = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
106
-
107
- # wait for first pass to finish
108
- unless first_pass_skipped
109
- first_pass_finished, exception = Isomorfeus.ssr_contexts[thread_id_asset].exec('return [global.FirstPassFinished, global.Exception ? { message: global.Exception.message, stack: global.Exception.stack } : false ]')
110
- Isomorfeus.raise_error(message: "Server Side Rendering: #{exception['message']}", stack: exception['stack']) if exception
111
- unless first_pass_finished
112
- start_time = Time.now
113
- while !first_pass_finished
114
- break if (Time.now - start_time) > 10
115
- sleep 0.01
116
- first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')
117
- end
118
- end
119
-
120
- # wait for transport requests to finish
121
- if first_pass_finished == 'transport'
122
- transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
123
- if transport_busy
124
- start_time = Time.now
125
- while transport_busy
126
- break if (Time.now - start_time) > 10
127
- sleep 0.01
128
- transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
129
- end
130
- end
131
- end
132
- end
133
-
134
- # build javascript for second render pass
135
- # guard against leaks from first pass, maybe because of a exception
136
- javascript = <<~JAVASCRIPT
137
- global.Opal.React.render_buffer = [];
138
- global.Opal.React.active_components = [];
139
- global.Opal.React.active_redux_components = [];
140
- global.Exception = false;
141
- let rendered_tree;
142
- let ssr_styles;
143
- let component;
144
- if (typeof global.Opal.global.MuiStyles !== 'undefined' && typeof global.Opal.global.MuiStyles.ServerStyleSheets !== 'undefined') {
145
- component = '#{component_name}'.split(".").reduce(function(o, x) {
146
- return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
147
- }, global.Opal.global)
148
- if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
149
- try {
150
- let sheets = new global.Opal.global.MuiStyles.ServerStyleSheets();
151
- let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
152
- if (#{static}) { rendered_tree = global.Opal.global.ReactDOMServer.renderToStaticMarkup(sheets.collect(app)); }
153
- else { rendered_tree = global.Opal.global.ReactDOMServer.renderToString(sheets.collect(app)); }
154
- ssr_styles = sheets.toString();
155
- } catch (e) {
156
- global.Exception = e;
157
- }
158
- } else if (typeof global.Opal.global.ReactJSS !== 'undefined' && typeof global.Opal.global.ReactJSS.SheetsRegistry !== 'undefined') {
159
- component = '#{component_name}'.split(".").reduce(function(o, x) {
160
- return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
161
- }, global.Opal.global)
162
- if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
163
- try {
164
- let sheets = new global.Opal.global.ReactJSS.SheetsRegistry();
165
- let generate_id = global.Opal.global.ReactJSS.createGenerateId();
166
- let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
167
- let element = global.Opal.global.React.createElement(global.Opal.global.ReactJSS.JssProvider, { registry: sheets, generateId: generate_id }, app);
168
- if (#{static}) { rendered_tree = global.Opal.global.ReactDOMServer.renderToStaticMarkup(element); }
169
- else { rendered_tree = global.Opal.global.ReactDOMServer.renderToString(element); }
170
- ssr_styles = sheets.toString();
171
- } catch (e) {
172
- global.Exception = e;
173
- }
174
- } else {
175
- try {
176
- if (#{static}) { rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_static_markup('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
177
- else { rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
178
- } catch (e) {
179
- global.Exception = e;
180
- }
181
- }
182
- let application_state = global.Opal.Isomorfeus.store.native.getState();
183
- if (typeof global.Opal.Isomorfeus.Transport !== 'undefined') { global.Opal.Isomorfeus.Transport.$disconnect(); }
184
- return [rendered_tree, application_state, ssr_styles, global.Opal.Isomorfeus['$ssr_response_status'](), global.Exception ? { message: global.Exception.message, stack: global.Exception.stack } : false];
185
- JAVASCRIPT
186
-
187
- # execute second render pass
188
- rendered_tree, application_state, @ssr_styles, @ssr_response_status, exception = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
189
- Isomorfeus.raise_error(message: exception['message'], stack: exception['stack']) if exception
190
-
191
- # build result
192
- unless static
193
- render_result << " data-iso-hydrated='true'" if rendered_tree
194
- if Isomorfeus.respond_to?(:current_user) && Isomorfeus.current_user && !Isomorfeus.current_user.anonymous?
195
- render_result << " data-iso-usid=#{Oj.dump(Isomorfeus.current_user.to_sid, mode: :strict)}"
196
- end
197
- render_result << " data-iso-nloc='#{props[:locale]}'>"
198
- end
199
- render_result << (rendered_tree ? rendered_tree : "SSR didn't work")
200
- else
201
- if Isomorfeus.respond_to?(:current_user) && Isomorfeus.current_user && !Isomorfeus.current_user.anonymous?
202
- render_result << " data-iso-usid=#{Oj.dump(Isomorfeus.current_user.to_sid, mode: :strict)}"
203
- end
204
- render_result << " data-iso-nloc='#{props[:locale]}'>" unless static
205
- end
206
- render_result << '</div>'
207
- if Isomorfeus.server_side_rendering && !static
208
- render_result = "<script type='application/javascript'>\nServerSideRenderingStateJSON = #{Oj.dump(application_state, mode: :strict)}\n</script>\n" << render_result
209
- end
210
- render_result
211
- end
212
-
213
- def mount_static_component(component_name, props = {}, asset = 'web_ssr.js')
214
- mount_component(component_name, props, asset, true)
215
- end
216
-
217
- def ssr_response_status
218
- @ssr_response_status || 200
219
- end
220
-
221
- def ssr_styles
222
- @ssr_styles || ''
223
- end
224
-
225
- private
226
-
227
- def component_cache
228
- @_component_cache ||= Isomorfeus.component_cache_init_block.call
229
- end
230
- end
231
- end
1
+ module Isomorfeus
2
+ module ReactViewHelper
3
+ def cached_mount_component(component_name, props = {}, asset = 'web_ssr.js', static = false)
4
+ key = "#{component_name}#{props}#{asset}"
5
+ if Isomorfeus.production?
6
+ render_result, @ssr_response_status, @ssr_styles = component_cache.fetch(key)
7
+ return render_result if render_result
8
+ end
9
+ render_result = mount_component(component_name, props, asset, static)
10
+ status = ssr_response_status
11
+ component_cache.store(key, render_result, status, ssr_styles) if status >= 200 && status < 300
12
+ render_result
13
+ end
14
+
15
+ def cached_mount_static_component(component_name, props = {}, asset = 'web_ssr.js')
16
+ cached_mount_component(component_name, props, asset, true)
17
+ end
18
+
19
+ def mount_component(component_name, props = {}, asset = 'web_ssr.js', static = false)
20
+ @ssr_response_status = nil
21
+ @ssr_styles = nil
22
+ thread_id_asset = "#{Thread.current.object_id}#{asset}"
23
+ render_result = if static
24
+ '<div>'
25
+ else
26
+ "<div data-iso-env=\"#{Isomorfeus.env}\" data-iso-root=\"#{component_name}\" data-iso-props='#{Oj.dump(props, mode: :strict)}'"
27
+ end
28
+ if Isomorfeus.server_side_rendering
29
+
30
+ if Isomorfeus.development?
31
+ # always create a new context, effectively reloading code
32
+ # delete the existing context first, saves memory
33
+ if Isomorfeus.ssr_contexts.key?(thread_id_asset)
34
+ uuid = Isomorfeus.ssr_contexts[thread_id_asset].instance_variable_get(:@uuid)
35
+ runtime = Isomorfeus.ssr_contexts[thread_id_asset].instance_variable_get(:@runtime)
36
+ runtime.vm.delete_context(uuid)
37
+ end
38
+ asset_path = "#{Isomorfeus.ssr_hot_asset_url}#{asset}"
39
+ begin
40
+ asset = Net::HTTP.get(URI(asset_path))
41
+ rescue Exception => e
42
+ Isomorfeus.raise_error(message: "Server Side Rendering: Failed loading asset #{asset_path} from webpack dev server. Error: #{e.message}", stack: e.backtrace )
43
+ end
44
+ if asset.strip.start_with?('<')
45
+ Isomorfeus.raise_error(message: "Server Side Rendering: Failed loading asset #{asset_path} from webpack dev server, asset is not javascript. Did the webpack build succeed?")
46
+ end
47
+ begin
48
+ Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(asset)
49
+ rescue Exception => e
50
+ Isomorfeus.raise_error(message: "Server Side Rendering: Failed creating context for #{asset_path}. Error: #{e.message}", stack: e.backtrace)
51
+ end
52
+ else
53
+ # initialize speednode context
54
+ unless Isomorfeus.ssr_contexts.key?(thread_id_asset)
55
+ asset_file_name = OpalWebpackLoader::Manifest.lookup_path_for(asset)
56
+ Isomorfeus.raise_error(message: "Server Side Rendering: Build asset file not found for #{asset}. Has it been build?") unless asset_file_name
57
+ asset_path = File.join('public', asset_file_name)
58
+ Isomorfeus.ssr_contexts[thread_id_asset] = ExecJS.permissive_compile(File.read(asset_path))
59
+ end
60
+ end
61
+
62
+ # build javascript for rendering first pass
63
+ # it will initialize buffers to guard against leaks, maybe caused by previous exceptions
64
+ javascript = <<~JAVASCRIPT
65
+ global.Opal.React.render_buffer = [];
66
+ global.Opal.React.active_components = [];
67
+ global.Opal.React.active_redux_components = [];
68
+ global.FirstPassFinished = false;
69
+ global.Exception = false;
70
+ global.IsomorfeusSessionId = '#{Thread.current[:isomorfeus_session_id]}';
71
+ global.Opal.Isomorfeus['$env=']('#{Isomorfeus.env}');
72
+ if (typeof global.Opal.Isomorfeus.$negotiated_locale === 'function') {
73
+ global.Opal.Isomorfeus["$negotiated_locale="]('#{props[:locale]}');
74
+ }
75
+ global.Opal.Isomorfeus['$force_init!']();
76
+ global.Opal.Isomorfeus['$ssr_response_status='](200);
77
+ global.Opal.Isomorfeus.TopLevel['$ssr_route_path=']('#{props[:location]}');
78
+ JAVASCRIPT
79
+
80
+ # if location_host and scheme are given and if Transport is loaded, connect and then render,
81
+ # otherwise do not render because only one pass is required
82
+ ws_scheme = props[:location_scheme] == 'https:' ? 'wss:' : 'ws:'
83
+ location_host = props[:location_host] ? props[:location_host] : 'localhost'
84
+ api_ws_path = Isomorfeus.respond_to?(:api_websocket_path) ? Isomorfeus.api_websocket_path : ''
85
+ transport_ws_url = ws_scheme + location_host + api_ws_path
86
+ javascript << <<~JAVASCRIPT
87
+ let api_ws_path = '#{api_ws_path}';
88
+ let exception;
89
+ if (typeof global.Opal.Isomorfeus.Transport !== 'undefined' && api_ws_path !== '') {
90
+ global.Opal.Isomorfeus.TopLevel["$transport_ws_url="]("#{transport_ws_url}");
91
+ global.Opal.send(global.Opal.Isomorfeus.Transport.$promise_connect(), 'then', [], ($$1 = function(){
92
+ try {
93
+ if (#{static}) { global.Opal.Isomorfeus.TopLevel.$render_component_to_static_markup('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
94
+ else { global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
95
+ global.FirstPassFinished = 'transport';
96
+ } catch (e) {
97
+ global.Exception = e;
98
+ global.FirstPassFinished = 'transport';
99
+ }
100
+ }, $$1.$$s = this, $$1.$$arity = 0, $$1))
101
+ } else { return global.FirstPassFinished = true; };
102
+ JAVASCRIPT
103
+
104
+ # execute first render pass
105
+ first_pass_skipped = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
106
+
107
+ # wait for first pass to finish
108
+ unless first_pass_skipped
109
+ first_pass_finished, exception = Isomorfeus.ssr_contexts[thread_id_asset].exec('return [global.FirstPassFinished, global.Exception ? { message: global.Exception.message, stack: global.Exception.stack } : false ]')
110
+ Isomorfeus.raise_error(message: "Server Side Rendering: #{exception['message']}", stack: exception['stack']) if exception
111
+ unless first_pass_finished
112
+ start_time = Time.now
113
+ while !first_pass_finished
114
+ break if (Time.now - start_time) > 10
115
+ sleep 0.01
116
+ first_pass_finished = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.FirstPassFinished')
117
+ end
118
+ end
119
+
120
+ # wait for transport requests to finish
121
+ if first_pass_finished == 'transport'
122
+ transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
123
+ if transport_busy
124
+ start_time = Time.now
125
+ while transport_busy
126
+ break if (Time.now - start_time) > 10
127
+ sleep 0.01
128
+ transport_busy = Isomorfeus.ssr_contexts[thread_id_asset].exec('return global.Opal.Isomorfeus.Transport["$busy?"]()')
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ # build javascript for second render pass
135
+ # guard against leaks from first pass, maybe because of a exception
136
+ javascript = <<~JAVASCRIPT
137
+ global.Opal.React.render_buffer = [];
138
+ global.Opal.React.active_components = [];
139
+ global.Opal.React.active_redux_components = [];
140
+ global.Exception = false;
141
+ let rendered_tree;
142
+ let ssr_styles;
143
+ let component;
144
+ if (typeof global.Opal.global.MuiStyles !== 'undefined' && typeof global.Opal.global.MuiStyles.ServerStyleSheets !== 'undefined') {
145
+ component = '#{component_name}'.split(".").reduce(function(o, x) {
146
+ return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
147
+ }, global.Opal.global)
148
+ if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
149
+ try {
150
+ let sheets = new global.Opal.global.MuiStyles.ServerStyleSheets();
151
+ let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
152
+ if (#{static}) { rendered_tree = global.Opal.global.ReactDOMServer.renderToStaticMarkup(sheets.collect(app)); }
153
+ else { rendered_tree = global.Opal.global.ReactDOMServer.renderToString(sheets.collect(app)); }
154
+ ssr_styles = sheets.toString();
155
+ } catch (e) {
156
+ global.Exception = e;
157
+ }
158
+ } else if (typeof global.Opal.global.ReactJSS !== 'undefined' && typeof global.Opal.global.ReactJSS.SheetsRegistry !== 'undefined') {
159
+ component = '#{component_name}'.split(".").reduce(function(o, x) {
160
+ return (o !== null && typeof o[x] !== "undefined" && o[x] !== null) ? o[x] : null;
161
+ }, global.Opal.global)
162
+ if (!component) { component = global.Opal.Isomorfeus.$cached_component_class('#{component_name}'); }
163
+ try {
164
+ let sheets = new global.Opal.global.ReactJSS.SheetsRegistry();
165
+ let generate_id = global.Opal.global.ReactJSS.createGenerateId();
166
+ let app = global.Opal.React.$create_element(component, global.Opal.Hash.$new(#{Oj.dump(props, mode: :strict)}));
167
+ let element = global.Opal.global.React.createElement(global.Opal.global.ReactJSS.JssProvider, { registry: sheets, generateId: generate_id }, app);
168
+ if (#{static}) { rendered_tree = global.Opal.global.ReactDOMServer.renderToStaticMarkup(element); }
169
+ else { rendered_tree = global.Opal.global.ReactDOMServer.renderToString(element); }
170
+ ssr_styles = sheets.toString();
171
+ } catch (e) {
172
+ global.Exception = e;
173
+ }
174
+ } else {
175
+ try {
176
+ if (#{static}) { rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_static_markup('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
177
+ else { rendered_tree = global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)}); }
178
+ } catch (e) {
179
+ global.Exception = e;
180
+ }
181
+ }
182
+ let application_state = global.Opal.Isomorfeus.store.native.getState();
183
+ if (typeof global.Opal.Isomorfeus.Transport !== 'undefined') { global.Opal.Isomorfeus.Transport.$disconnect(); }
184
+ return [rendered_tree, application_state, ssr_styles, global.Opal.Isomorfeus['$ssr_response_status'](), global.Exception ? { message: global.Exception.message, stack: global.Exception.stack } : false];
185
+ JAVASCRIPT
186
+
187
+ # execute second render pass
188
+ rendered_tree, application_state, @ssr_styles, @ssr_response_status, exception = Isomorfeus.ssr_contexts[thread_id_asset].exec(javascript)
189
+ Isomorfeus.raise_error(message: exception['message'], stack: exception['stack']) if exception
190
+
191
+ # build result
192
+ unless static
193
+ render_result << " data-iso-hydrated='true'" if rendered_tree
194
+ if Isomorfeus.respond_to?(:current_user) && Isomorfeus.current_user && !Isomorfeus.current_user.anonymous?
195
+ render_result << " data-iso-usid=#{Oj.dump(Isomorfeus.current_user.to_sid, mode: :strict)}"
196
+ end
197
+ render_result << " data-iso-nloc='#{props[:locale]}'>"
198
+ end
199
+ render_result << (rendered_tree ? rendered_tree : "SSR didn't work")
200
+ else
201
+ if Isomorfeus.respond_to?(:current_user) && Isomorfeus.current_user && !Isomorfeus.current_user.anonymous?
202
+ render_result << " data-iso-usid=#{Oj.dump(Isomorfeus.current_user.to_sid, mode: :strict)}"
203
+ end
204
+ render_result << " data-iso-nloc='#{props[:locale]}'>" unless static
205
+ end
206
+ render_result << '</div>'
207
+ if Isomorfeus.server_side_rendering && !static
208
+ render_result = "<script type='application/javascript'>\nServerSideRenderingStateJSON = #{Oj.dump(application_state, mode: :strict)}\n</script>\n" << render_result
209
+ end
210
+ render_result
211
+ end
212
+
213
+ def mount_static_component(component_name, props = {}, asset = 'web_ssr.js')
214
+ mount_component(component_name, props, asset, true)
215
+ end
216
+
217
+ def ssr_response_status
218
+ @ssr_response_status || 200
219
+ end
220
+
221
+ def ssr_styles
222
+ @ssr_styles || ''
223
+ end
224
+
225
+ private
226
+
227
+ def component_cache
228
+ @_component_cache ||= Isomorfeus.component_cache_init_block.call
229
+ end
230
+ end
231
+ end