isomorfeus-react 16.6.6 → 16.6.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,20 +3,27 @@ if RUBY_ENGINE == 'opal'
3
3
  require 'native'
4
4
  require 'active_support/core_ext/string'
5
5
  require 'react/active_support_support'
6
- require 'browser/support'
7
- require 'browser/event'
8
- require 'browser/event_source'
9
- require 'browser/screen'
10
- require 'browser/socket'
11
- require 'browser/window'
12
- require 'browser/dom/node'
13
- require 'browser/dom/element'
6
+ require 'isomorfeus/execution_environment'
7
+ if Isomorfeus.on_browser?
8
+ require 'browser/support'
9
+ require 'browser/event'
10
+ require 'browser/event_source'
11
+ require 'browser/screen'
12
+ require 'browser/socket'
13
+ require 'browser/window'
14
+ require 'browser/dom/node'
15
+ require 'browser/dom/element'
16
+ end
14
17
  require 'isomorfeus-redux'
15
18
  require 'react/version'
16
19
  require 'react'
17
20
  # require 'react/element' # usually not needed
18
21
  require 'react/synthetic_event'
19
- require 'react_dom'
22
+ if Isomorfeus.on_browser?
23
+ require 'react_dom'
24
+ else
25
+ require 'react_dom_server'
26
+ end
20
27
  # React::Component
21
28
  require 'react/component/api'
22
29
  require 'react/component/unsafe_api'
@@ -81,51 +88,42 @@ if RUBY_ENGINE == 'opal'
81
88
  require 'lucid_app/mixin'
82
89
  require 'lucid_app/base'
83
90
 
91
+ # LucidRouter
92
+ require 'lucid_router'
93
+
84
94
  # allow mounting of components
85
- require 'isomorfeus/top_level'
95
+ if Isomorfeus.on_browser?
96
+ require 'isomorfeus/top_level_browser'
97
+ else
98
+ require 'isomorfeus/top_level_ssr'
99
+ end
86
100
 
87
101
  # initalize Store, options, etc.
88
102
  Isomorfeus.init
89
103
  else
104
+ require 'oj'
90
105
  require 'opal'
91
106
  require 'opal-activesupport'
92
107
  require 'opal-browser'
93
108
  require 'isomorfeus-redux'
109
+ require 'isomorfeus-speednode'
94
110
  require 'react/version'
95
111
  require 'isomorfeus/config'
96
- require 'isomorfeus/view_helpers'
97
112
 
98
- Opal.append_path(__dir__.untaint)
113
+ Isomorfeus.env = ENV['RACK_ENV']
99
114
 
100
- if defined?(Rails)
101
- module Isomorfeus
102
- module Model
103
- class Railtie < Rails::Railtie
104
- def delete_first(a, e)
105
- a.delete_at(a.index(e) || a.length)
106
- end
115
+ if Isomorfeus.env == 'production'
116
+ Isomorfeus.server_side_rendering = true
117
+ else
118
+ Isomorfeus.server_side_rendering = false
119
+ end
107
120
 
108
- config.before_configuration do |_|
109
- Rails.configuration.tap do |config|
110
- # rails will add everything immediately below app to eager and auto load, so we need to remove it
111
- delete_first config.eager_load_paths, "#{config.root}/app/isomorfeus"
121
+ require 'isomorfeus/execution_environment'
122
+ require 'isomorfeus/react_view_helper'
112
123
 
113
- unless Rails.env.production?
114
- # rails will add everything immediately below app to eager and auto load, so we need to remove it
115
- delete_first config.autoload_paths, "#{config.root}/app/isomorfeus"
116
- end
117
- end
118
- end
119
- end
120
- end
121
- end
122
- end
124
+ Opal.append_path(__dir__.untaint)
123
125
 
124
- if Dir.exist?(File.join('app', 'isomorfeus'))
125
- # Opal.append_path(File.expand_path(File.join('app', 'isomorfeus', 'components')))
126
- Opal.append_path(File.expand_path(File.join('app', 'isomorfeus'))) unless Opal.paths.include?(File.expand_path(File.join('app', 'isomorfeus')))
127
- elsif Dir.exist?('isomorfeus')
128
- # Opal.append_path(File.expand_path(File.join('isomorfeus', 'components')))
126
+ if Dir.exist?('isomorfeus')
129
127
  Opal.append_path(File.expand_path('isomorfeus')) unless Opal.paths.include?(File.expand_path('isomorfeus'))
130
128
  end
131
129
  end
@@ -1,12 +1,19 @@
1
1
  module Isomorfeus
2
2
  if RUBY_ENGINE == 'opal'
3
3
  class << self
4
- attr_reader :initialized?
4
+ attr_reader :initialized
5
5
  attr_reader :store
6
+ attr_reader :env
6
7
 
7
8
  def init
8
- return if initialized?
9
+ return if initialized
9
10
  @initialized = true
11
+ # at least one reducer must have been added at this stage
12
+ # this happened in isomorfeus-react.rb, where the component reducers were added
13
+ force_init!
14
+ end
15
+
16
+ def force_init!
10
17
  # at least one reducer must have been added at this stage
11
18
  # this happened in isomorfeus-react.rb, where the component reducers were added
12
19
  @store = Redux::Store.init!
@@ -28,21 +35,75 @@ module Isomorfeus
28
35
  constant.constantize.send(:init)
29
36
  end
30
37
  end
38
+
39
+ def env=(env_string)
40
+ @env = env_string ? env_string.to_s : 'development'
41
+ @development = (@env == 'development') ? true : false
42
+ @production = (@env == 'production') ? true : false
43
+ @test = (@env == 'test') ? true : false
44
+ end
45
+
46
+ def development?
47
+ @development
48
+ end
49
+
50
+ def production?
51
+ @production
52
+ end
53
+
54
+ def test?
55
+ @test
56
+ end
57
+
58
+ def start_app!
59
+ Isomorfeus::TopLevel.mount!
60
+ end
61
+
62
+ def force_render
63
+ @render_trigger ||= 1
64
+ @render_trigger += 1
65
+ action = { type: 'APPLICATION_STATE', name: 'render_trigger', value: @render_trigger }
66
+ Isomorfeus.store.dispatch(action)
67
+ @render_trigger
68
+ end
31
69
  end
70
+
32
71
  self.add_client_option(:client_init_class_names, [])
33
72
  else
34
73
  class << self
35
- attr_accessor :prerendering
74
+ attr_accessor :server_side_rendering
75
+ attr_reader :env
36
76
 
37
77
  def configuration(&block)
38
78
  block.call(self)
39
79
  end
40
80
 
81
+ def env=(env_string)
82
+ @env = env_string ? env_string.to_s : 'development'
83
+ @development = (@env == 'development') ? true : false
84
+ @production = (@env == 'production') ? true : false
85
+ @test = (@env == 'test') ? true : false
86
+ end
87
+
88
+ def development?
89
+ @development
90
+ end
91
+
92
+ def production?
93
+ @production
94
+ end
95
+
96
+ def test?
97
+ @test
98
+ end
99
+
100
+ def ssr_contexts
101
+ @ssr_contexts ||= {}
102
+ end
103
+
41
104
  def version
42
105
  Isomorfeus::VERSION
43
106
  end
44
107
  end
45
-
46
- self.prerendering = :off
47
108
  end
48
109
  end
@@ -0,0 +1,31 @@
1
+ module Isomorfeus
2
+ if RUBY_ENGINE == 'opal'
3
+ class << self
4
+ def on_browser?
5
+ !on_ssr?
6
+ end
7
+
8
+ def on_ssr?
9
+ !!`((typeof process !== 'undefined') && (typeof process.release !== "undefined") && (process.release.name === 'node'))`
10
+ end
11
+
12
+ def on_server?
13
+ false
14
+ end
15
+ end
16
+ else
17
+ class << self
18
+ def on_browser?
19
+ false
20
+ end
21
+
22
+ def on_ssr?
23
+ false
24
+ end
25
+
26
+ def on_server?
27
+ true
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ module Isomorfeus
2
+ module ReactViewHelper
3
+ def mount_component(component_name, props = {}, asset = 'application_ssr.js')
4
+ render_result = "<div data-iso-env=\"#{Isomorfeus.env}\" data-iso-root=\"#{component_name}\" data-iso-props='#{Oj.dump(props, mode: :strict)}'>"
5
+ if Isomorfeus.server_side_rendering
6
+ unless Isomorfeus.ssr_contexts.key?(asset)
7
+ asset_file_name = OpalWebpackLoader::Manifest.lookup_path_for(asset)
8
+ asset_path = File.join('public', asset_file_name)
9
+ Isomorfeus.ssr_contexts[asset] = ExecJS.permissive_compile(File.read(asset_path))
10
+ end
11
+ javascript = ''
12
+ if props.key?(:location)
13
+ javascript << <<~JAVASCRIPT
14
+ global.Opal.Isomorfeus.TopLevel["$ssr_route_path="]('#{props[:location]}');
15
+ JAVASCRIPT
16
+ end
17
+ javascript << <<~JAVASCRIPT
18
+ return global.Opal.Isomorfeus.TopLevel.$render_component_to_string('#{component_name}', #{Oj.dump(props, mode: :strict)})
19
+ JAVASCRIPT
20
+ render_result << Isomorfeus.ssr_contexts[asset].exec(javascript)
21
+ end
22
+ render_result << '</div>'
23
+ render_result
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ module Isomorfeus
2
+ class TopLevel
3
+ def self.mount!
4
+ Isomorfeus.init
5
+ on_ready do
6
+ root_element = `document.querySelector('div[data-iso-root]')`
7
+ component_name = root_element.JS.getAttribute('data-iso-root')
8
+ component = component_name.constantize
9
+ props_json = root_element.JS.getAttribute('data-iso-props')
10
+ props = `Opal.Hash.$new(JSON.parse(props_json))`
11
+ hydrated = root_element.JS.getAttribute('data-iso-hydrated')
12
+ mount_component(component, props, root_element, hydrated)
13
+ end
14
+ end
15
+
16
+ def self.on_ready(&block)
17
+ # this looks a bit odd but works across _all_ browsers, and no event handler mess
18
+ %x{
19
+ function run() { block.$call() };
20
+ function ready_fun() {
21
+ /in/.test(document.readyState) ? setTimeout(ready_fun,5) : run();
22
+ }
23
+ ready_fun();
24
+ }
25
+ end
26
+
27
+ def self.on_ready_mount(component, props = nil, element_query = nil)
28
+ # init in case it hasn't been run yet
29
+ Isomorfeus.init
30
+ on_ready do
31
+ mount_component(component, props, element_query)
32
+ end
33
+ end
34
+
35
+ def self.mount_component(component, props, element_or_query, hydrated = false)
36
+ if `(typeof element_or_query.$class === 'function')` && element_or_query.class == String
37
+ element = `document.body.querySelector(element_or_query)`
38
+ elsif `(typeof element_or_query.$is_a === 'function')` && element_or_query.is_a?(Browser::DOM::Node)
39
+ element = element_or_query.to_n
40
+ else
41
+ element = element_or_query
42
+ end
43
+
44
+ if hydrated
45
+ ReactDOM.hydrate(React.create_element(component, props), element)
46
+ else
47
+ ReactDOM.render(React.create_element(component, props), element)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,18 @@
1
+ module Isomorfeus
2
+ class TopLevel
3
+ class << self
4
+ attr_accessor :ssr_route_path
5
+
6
+ def mount!
7
+ # nothing, but keep it for compatibility with browser
8
+ end
9
+
10
+ def render_component_to_string(component_name, props_json)
11
+ # for some reason props_json arrives already decoded
12
+ component = component_name.constantize
13
+ Isomorfeus.force_init!
14
+ ReactDOMServer.render_to_string(React.create_element(component, `Opal.Hash.$new(props_json)`))
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/lucid_app/api.rb CHANGED
@@ -12,7 +12,7 @@ module LucidApp
12
12
  Opal.React.active_redux_components.pop();
13
13
  Opal.React.active_components.pop();
14
14
  var children = Opal.React.render_buffer.pop();
15
- return React.createElement(LucidApplicationContext.Provider, { value: this.state.isomorfeus_store_state }, children);
15
+ return Opal.global.React.createElement(Opal.global.LucidApplicationContext.Provider, { value: this.state.isomorfeus_store_state }, children);
16
16
  }
17
17
  }
18
18
  end
@@ -7,7 +7,7 @@ module LucidApp
7
7
  component_name = base.to_s
8
8
  # language=JS
9
9
  %x{
10
- base.react_component = class extends React.Component {
10
+ base.react_component = class extends Opal.global.React.Component {
11
11
  constructor(props) {
12
12
  super(props);
13
13
  if (base.$default_state_defined()) {
@@ -42,7 +42,7 @@ module LucidApp
42
42
  }
43
43
  this[ref] = this[ref].bind(this);
44
44
  } else {
45
- this[ref] = React.createRef();
45
+ this[ref] = Opal.global.React.createRef();
46
46
  }
47
47
  }
48
48
  this.listener = this.listener.bind(this);
@@ -8,12 +8,12 @@ module LucidComponent
8
8
  # language=JS
9
9
  %x{
10
10
  base.react_component = function(props) {
11
- return React.createElement(LucidApplicationContext.Consumer, null, function(store) {
11
+ return Opal.global.React.createElement(Opal.global.LucidApplicationContext.Consumer, null, function(store) {
12
12
  var store_props = Object.assign({}, props, { isomorfeus_store: store });
13
- return React.createElement(base.lucid_react_component, store_props);
13
+ return Opal.global.React.createElement(base.lucid_react_component, store_props);
14
14
  });
15
15
  }
16
- base.lucid_react_component = class extends React.Component {
16
+ base.lucid_react_component = class extends Opal.global.React.Component {
17
17
  constructor(props) {
18
18
  super(props);
19
19
  if (base.$default_state_defined()) {
@@ -39,7 +39,7 @@ module LucidComponent
39
39
  }
40
40
  this[ref] = this[ref].bind(this);
41
41
  } else {
42
- this[ref] = React.createRef();
42
+ this[ref] = Opal.global.React.createRef();
43
43
  }
44
44
  }
45
45
  }
@@ -61,10 +61,13 @@ module LucidComponent
61
61
  var this_state_keys = Object.keys(this.state);
62
62
  if (next_state_keys.length !== this_state_keys.length) { return true; }
63
63
 
64
+ var used_store_result;
64
65
  for (var property in next_props) {
65
66
  if (property === "isomorfeus_store") {
66
- var res = this.scu_for_used_store_paths(this, this.state.isomorfeus_store, next_state.isomorfeus_store);
67
- if (res) { return true; }
67
+ used_store_result = this.scu_for_used_store_paths(this, this.state.isomorfeus_store, next_state.isomorfeus_store);
68
+ if (used_store_result) {
69
+ return true;
70
+ }
68
71
  } else if (next_props.hasOwnProperty(property)) {
69
72
  if (!this.props.hasOwnProperty(property)) { return true; };
70
73
  if (property == "children") { if (next_props.children !== this.props.children) { return true; }}
@@ -0,0 +1,18 @@
1
+ # language=JS
2
+ %x{
3
+ class LucidRouter extends Opal.global.React.Component {
4
+ constructor(props) {
5
+ super(props);
6
+ }
7
+ render() {
8
+ if (Opal.Isomorfeus["$on_ssr?"]()) {
9
+ var new_props = Object.assign({}, this.props, { location: Opal.Isomorfeus.TopLevel.$ssr_route_path() });
10
+ return Opal.global.React.createElement(Opal.global.StaticRouter, new_props);
11
+ } else {
12
+ return Opal.global.React.createElement(Opal.global.BrowserRouter, this.props);
13
+ }
14
+ }
15
+ }
16
+
17
+ Opal.global.LucidRouter = LucidRouter;
18
+ }
data/lib/react.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module React
2
2
  # to_native_react_props: the native_component params is used for event handlers, it keeps the event handlers
3
- # it does not need to be component, can be a object with the event handlers
3
+ # it does not need to be compone nt, can be a object with the event handlers
4
4
  # language=JS
5
5
  %x{
6
6
  self.render_buffer = [];
@@ -13,7 +13,7 @@ module React
13
13
  res += parts[i][0].toUpperCase() + parts[i].slice(1);
14
14
  }
15
15
  return res;
16
- }
16
+ };
17
17
 
18
18
  self.to_native_react_props = function(ruby_style_props) {
19
19
  var result = {};
@@ -35,7 +35,7 @@ module React
35
35
  }
36
36
  }
37
37
  return result;
38
- }
38
+ };
39
39
 
40
40
  self.internal_render = function(component, props, string_child, block) {
41
41
  var children;
@@ -58,7 +58,7 @@ module React
58
58
  else if (children.length == 0) { children = null; }
59
59
  }
60
60
  if (props) { native_props = Opal.React.to_native_react_props(props); }
61
- react_element = React.createElement(component, native_props, children);
61
+ react_element = Opal.global.React.createElement(component, native_props, children);
62
62
  Opal.React.render_buffer[Opal.React.render_buffer.length - 1].push(react_element);
63
63
  };
64
64
 
@@ -86,12 +86,12 @@ module React
86
86
  block_result = `null` unless block_result
87
87
  end
88
88
  native_props = props ? `Opal.React.to_native_react_props(props)` : `null`
89
- `React.cloneElement(ruby_react_element.$to_n(), native_props, block_result)`
89
+ `Opal.global.React.cloneElement(ruby_react_element.$to_n(), native_props, block_result)`
90
90
  end
91
91
 
92
92
  def self.create_context(const_name, default_value)
93
93
  %x{
94
- Opal.global[const_name] = React.createContext(default_value);
94
+ Opal.global[const_name] = Opal.global.React.createContext(default_value);
95
95
  var new_const = #{React::ContextWrapper.new(`Opal.global[const_name]`)};
96
96
  #{Object.const_set(const_name, `new_const`)};
97
97
  return new_const;
@@ -126,31 +126,31 @@ module React
126
126
  if (children.length == 1) { children = children[0]; }
127
127
  else if (children.length == 0) { children = null; }
128
128
  }
129
- return React.createElement(component, native_props, children);
129
+ return Opal.global.React.createElement(component, native_props, children);
130
130
  }
131
131
  end
132
132
 
133
133
  def self.create_factory(type)
134
- native_function = `React.createFactory(type)`
134
+ native_function = `Opal.global.React.createFactory(type)`
135
135
  proc { `native_function.call()` }
136
136
  end
137
137
 
138
138
 
139
139
  def self.create_ref
140
- React::Ref.new(`React.createRef()`)
140
+ React::Ref.new(`Opal.global.React.createRef()`)
141
141
  end
142
142
 
143
143
  def self.forwardRef(&block)
144
144
  # TODO whats the return here? A React:Element?, doc says a React node, whats that?
145
- `React.forwardRef( function(props, ref) { return block.$call().$to_n(); })`
145
+ `Opal.global.React.forwardRef( function(props, ref) { return block.$call().$to_n(); })`
146
146
  end
147
147
 
148
148
  def self.isValidElement(react_element)
149
- `React.isValidElement(react_element)`
149
+ `Opal.global.React.isValidElement(react_element)`
150
150
  end
151
151
 
152
152
  def self.lazy(import_statement_function)
153
- `React.lazy(import_statement_function)`
153
+ `Opal.global.React.lazy(import_statement_function)`
154
154
  end
155
155
 
156
156
  def self.memo(function_component, &block)
@@ -159,10 +159,10 @@ module React
159
159
  var fun = function(prev_props, next_props) {
160
160
  return #{block.call(::React::Component::Props.new(prev_props), ::React::Component::Props.new(next_props))};
161
161
  }
162
- return React.memo(function_component, fun);
162
+ return Opal.global.React.memo(function_component, fun);
163
163
  }
164
164
  else
165
- `React.memo(function_component)`
165
+ `Opal.global.React.memo(function_component)`
166
166
  end
167
167
  end
168
168
  end