isomorfeus-react 16.13.11 → 16.13.12

Sign up to get free protection for your applications and to get access to all the features.
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