Skip to content

Commit fbe18b8

Browse files
authored
Merge pull request #683 from reactjs/webpacker-ssr
Webpacker Server Rendering
2 parents d3d82a0 + 339799a commit fbe18b8

30 files changed

+402
-90
lines changed

lib/assets/javascripts/react_ujs.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,16 @@ module.exports = function(ujs) {
125125
// Assume className is simple and can be found at top-level (window).
126126
// Fallback to eval to handle cases like 'My.React.ComponentName'.
127127
// Also, try to gracefully import Babel 6 style default exports
128+
var topLevel = typeof window === "undefined" ? this : window;
129+
128130
module.exports = function(className) {
129131
var constructor;
130-
var topLevel = typeof window === "undefined" ? this : window;
131132
// Try to access the class globally first
132133
constructor = topLevel[className];
133134

134135
// If that didn't work, try eval
135136
if (!constructor) {
136-
constructor = eval.call(topLevel, className);
137+
constructor = eval(className);
137138
}
138139

139140
// Lastly, if there is a default attribute try that
@@ -308,6 +309,12 @@ if (typeof window !== "undefined") {
308309
detectEvents(ReactRailsUJS)
309310
}
310311

312+
// It's a bit of a no-no to populate the global namespace,
313+
// but we really need it!
314+
// We need access to this object for server rendering, and
315+
// we can't do a dynamic `require`, so we'll grab it from here:
316+
this.ReactRailsUJS = ReactRailsUJS
317+
311318
module.exports = ReactRailsUJS
312319

313320

lib/react/rails/railtie.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class Railtie < ::Rails::Railtie
2020
# Changing files with these extensions in these directories will cause the server renderer to reload:
2121
config.react.server_renderer_directories = ["/app/assets/javascripts/"]
2222
config.react.server_renderer_extensions = ["jsx"]
23+
if defined?(Webpacker)
24+
config.react.server_renderer_directories << "app/javascript"
25+
config.react.server_renderer_extensions << "js"
26+
end
2327
# View helper implementation:
2428
config.react.view_helper_implementation = nil # Defaults to ComponentMount
2529

@@ -30,6 +34,7 @@ class Railtie < ::Rails::Railtie
3034
memo[app_dir] = config.react.server_renderer_extensions
3135
memo
3236
end
37+
3338
app.reloaders << ActiveSupport::FileUpdateChecker.new([], reload_paths) do
3439
React::ServerRendering.reset_pool
3540
end

lib/react/server_rendering/exec_js_renderer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def render_from_parts(before, main, after)
4141

4242
def main_render(component_name, props, prerender_options)
4343
render_function = prerender_options.fetch(:render_function, "renderToString")
44-
"ReactRailsUJS.serverRender('#{render_function}', #{component_name}, #{props})"
44+
"this.ReactRailsUJS.serverRender('#{render_function}', '#{component_name}', #{props})"
4545
end
4646

4747
def compose_js(before, main, after)

lib/react/server_rendering/sprockets_renderer.rb

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "react/server_rendering/environment_container"
22
require "react/server_rendering/manifest_container"
3+
require "react/server_rendering/webpacker_manifest_container"
34
require "react/server_rendering/yaml_manifest_container"
45

56
module React
@@ -57,19 +58,7 @@ class << self
5758
#
5859
# @return [#find_asset(logical_path)] An object that returns asset contents by logical path
5960
def asset_container
60-
@asset_container ||= if self.class.asset_container_class.present?
61-
self.class.asset_container_class.new
62-
elsif assets_precompiled? && ManifestContainer.compatible?
63-
ManifestContainer.new
64-
elsif assets_precompiled? && YamlManifestContainer.compatible?
65-
YamlManifestContainer.new
66-
else
67-
EnvironmentContainer.new
68-
end
69-
end
70-
71-
def assets_precompiled?
72-
!::Rails.application.config.assets.compile
61+
@asset_container ||= asset_container_class.new
7362
end
7463

7564
private
@@ -97,6 +86,29 @@ def render_function(opts)
9786
def prepare_props(props)
9887
props.is_a?(String) ? props : props.to_json
9988
end
89+
90+
def assets_precompiled?
91+
!::Rails.application.config.assets.compile
92+
end
93+
94+
def asset_container_class
95+
if self.class.asset_container_class.present?
96+
self.class.asset_container_class
97+
elsif WebpackerManifestContainer.compatible?
98+
WebpackerManifestContainer
99+
elsif assets_precompiled?
100+
if ManifestContainer.compatible?
101+
ManifestContainer
102+
elsif YamlManifestContainer.compatible?
103+
YamlManifestContainer
104+
else
105+
# Even though they are precompiled, we can't find them :S
106+
EnvironmentContainer
107+
end
108+
else
109+
EnvironmentContainer
110+
end
111+
end
100112
end
101113
end
102114
end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require "open-uri"
2+
3+
module React
4+
module ServerRendering
5+
# Get a compiled file from Webpacker
6+
class WebpackerManifestContainer
7+
def find_asset(logical_path)
8+
asset_path = Webpacker::Manifest.lookup(logical_path) # raises if not found
9+
if asset_path.start_with?("http")
10+
# TODO: this includes webpack-dev-server code which causes ExecJS to 💥
11+
dev_server_asset = open(asset_path).read
12+
else
13+
full_path = File.join(
14+
# TODO: using `.parent` here won't work for nonstandard configurations
15+
Webpacker::Configuration.output_path.parent,
16+
asset_path
17+
)
18+
File.read(full_path)
19+
end
20+
end
21+
22+
def self.compatible?
23+
!!defined?(Webpacker)
24+
end
25+
end
26+
end
27+
end

lib/react/server_rendering/yaml_manifest_container.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module React
22
module ServerRendering
3-
# Get asset content by reading the compiled file from disk using the generated maniftest.yml file
3+
# Get asset content by reading the compiled file from disk using the generated manifest.yml file
44
#
55
# This is good for Rails production when assets are compiled to public/assets
66
# but sometimes, they're compiled to other directories (or other servers)

react_ujs/dist/react_ujs.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,16 @@ module.exports = function(ujs) {
125125
// Assume className is simple and can be found at top-level (window).
126126
// Fallback to eval to handle cases like 'My.React.ComponentName'.
127127
// Also, try to gracefully import Babel 6 style default exports
128+
var topLevel = typeof window === "undefined" ? this : window;
129+
128130
module.exports = function(className) {
129131
var constructor;
130-
var topLevel = typeof window === "undefined" ? this : window;
131132
// Try to access the class globally first
132133
constructor = topLevel[className];
133134

134135
// If that didn't work, try eval
135136
if (!constructor) {
136-
constructor = eval.call(topLevel, className);
137+
constructor = eval(className);
137138
}
138139

139140
// Lastly, if there is a default attribute try that
@@ -308,6 +309,12 @@ if (typeof window !== "undefined") {
308309
detectEvents(ReactRailsUJS)
309310
}
310311

312+
// It's a bit of a no-no to populate the global namespace,
313+
// but we really need it!
314+
// We need access to this object for server rendering, and
315+
// we can't do a dynamic `require`, so we'll grab it from here:
316+
this.ReactRailsUJS = ReactRailsUJS
317+
311318
module.exports = ReactRailsUJS
312319

313320

react_ujs/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,10 @@ if (typeof window !== "undefined") {
109109
detectEvents(ReactRailsUJS)
110110
}
111111

112+
// It's a bit of a no-no to populate the global namespace,
113+
// but we really need it!
114+
// We need access to this object for server rendering, and
115+
// we can't do a dynamic `require`, so we'll grab it from here:
116+
self.ReactRailsUJS = ReactRailsUJS
117+
112118
module.exports = ReactRailsUJS

react_ujs/src/getConstructor/fromGlobal.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
// Assume className is simple and can be found at top-level (window).
22
// Fallback to eval to handle cases like 'My.React.ComponentName'.
33
// Also, try to gracefully import Babel 6 style default exports
4+
var topLevel = typeof window === "undefined" ? this : window;
5+
46
module.exports = function(className) {
57
var constructor;
6-
var topLevel = typeof window === "undefined" ? this : window;
78
// Try to access the class globally first
89
constructor = topLevel[className];
910

1011
// If that didn't work, try eval
1112
if (!constructor) {
12-
constructor = eval.call(topLevel, className);
13+
constructor = eval(className);
1314
}
1415

1516
// Lastly, if there is a default attribute try that

react_ujs/yarn.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -993,8 +993,8 @@ ms@0.7.2:
993993
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
994994

995995
nan@^2.3.0:
996-
version "2.5.1"
997-
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"
996+
version "2.6.1"
997+
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01"
998998

999999
node-libs-browser@^2.0.0:
10001000
version "2.0.0"
@@ -1252,8 +1252,8 @@ randombytes@^2.0.0, randombytes@^2.0.1:
12521252
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec"
12531253

12541254
rc@^1.1.7:
1255-
version "1.2.0"
1256-
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.0.tgz#c7de973b7b46297c041366b2fd3d2363b1697c66"
1255+
version "1.2.1"
1256+
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
12571257
dependencies:
12581258
deep-extend "~0.4.0"
12591259
ini "~1.3.0"
@@ -1275,7 +1275,7 @@ read-pkg@^1.0.0:
12751275
normalize-package-data "^2.3.2"
12761276
path-type "^1.0.0"
12771277

1278-
"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.0, readable-stream@^2.1.4:
1278+
"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.4, readable-stream@^2.2.6:
12791279
version "2.2.6"
12801280
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.6.tgz#8b43aed76e71483938d12a8d46c6cf1a00b1f816"
12811281
dependencies:
@@ -1447,12 +1447,12 @@ stream-browserify@^2.0.1:
14471447
readable-stream "^2.0.2"
14481448

14491449
stream-http@^2.3.1:
1450-
version "2.6.3"
1451-
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3"
1450+
version "2.7.0"
1451+
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.0.tgz#cec1f4e3b494bc4a81b451808970f8b20b4ed5f6"
14521452
dependencies:
14531453
builtin-status-codes "^3.0.0"
14541454
inherits "^2.0.1"
1455-
readable-stream "^2.1.0"
1455+
readable-stream "^2.2.6"
14561456
to-arraybuffer "^1.0.0"
14571457
xtend "^4.0.0"
14581458

0 commit comments

Comments
 (0)