isomorfeus-react 16.6.6 → 16.6.7

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.
@@ -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