-
-
Save ukazap/c208b3d05520b6244b244082ca4ad155 to your computer and use it in GitHub Desktop.
Simple webpacker server side rendering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const webpack = require('webpack') | |
const { environment } = require('@rails/webpacker') | |
// Don't use commons chunk for server_side_render chunk | |
const entries = environment.toWebpackConfig().entry | |
const commonsChunkEligible = Object.keys(entries).filter(name => name !== 'server_side_render') | |
environment.plugins.set('CommonsChunkVendor', new webpack.optimize.CommonsChunkPlugin({ | |
name: 'vendor', | |
minChunks: (module, count) => { | |
// this assumes your vendor imports exist in the node_modules directory | |
return module.context && module.context.indexOf('node_modules') !== -1; | |
}, | |
chunks: commonsChunkEligible | |
})) | |
environment.plugins.set('CommonsChunkManifest', new webpack.optimize.CommonsChunkPlugin({ | |
name: 'manifest', | |
minChunks: Infinity | |
})) | |
module.exports = environment |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# this is heavily influenced by react-rails: | |
# https://github.com/reactjs/react-rails/blob/master/lib/react/server_rendering/exec_js_renderer.rb | |
class ExecJsRenderer | |
JS_TEMPLATE = <<~JS | |
var execJsGlobal = {}; | |
var self = self || this; | |
var window = window || this; | |
JS | |
def initialize(js_code) | |
@context = ExecJS.compile(JS_TEMPLATE + js_code) | |
end | |
def render(component_name, props) | |
js_code = <<~JS | |
(function() { | |
var component = execJsGlobal["#{component_name}"]; | |
var sheet = new execJsGlobal.ServerStyleSheet(); | |
var element = execJsGlobal.React.createElement(component, #{props.to_json}); | |
var html = execJsGlobal.ReactDOMServer.renderToString(sheet.collectStyles(element)); | |
var styles = sheet.getStyleTags(); | |
return html + styles; | |
})() | |
JS | |
@context.eval(js_code).html_safe | |
end | |
private | |
def compose_js(js) | |
<<~JS | |
(function() { | |
var result = #{js}; | |
return result; | |
})() | |
JS | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'exec_js_renderer' | |
module ReactServerHelper | |
def react_server_component(name, props = {}) | |
server_pack = WebpackerManifestContainer.find_asset('server_side_render.js') | |
manifest_pack = WebpackerManifestContainer.find_asset('manifest.js') | |
renderer = ExecJsRenderer.new "#{manifest_pack}\n\n#{server_pack}" | |
renderer.render name, props | |
end | |
# from https://github.com/reactjs/react-rails/blob/master/lib/react/server_rendering/webpacker_manifest_container.rb | |
class WebpackerManifestContainer | |
# This pattern matches the code that initializes the dev-server client. | |
CLIENT_REQUIRE = %r{__webpack_require__\(.*webpack-dev-server\/client\/index\.js.*\n} | |
class << self | |
def find_asset(logical_path) | |
asset_path = Webpacker.manifest.lookup(logical_path).to_s | |
if Webpacker.dev_server.running? | |
ds = Webpacker.dev_server | |
dev_server_asset = open("#{ds.protocol}://#{ds.host_with_port}#{asset_path}").read | |
dev_server_asset.sub!(CLIENT_REQUIRE, '//\0') | |
dev_server_asset | |
else | |
File.read(file_path(logical_path)) | |
end | |
end | |
def file_path(path) | |
::Rails.root.join('public', Webpacker.manifest.lookup(path)[1..-1]) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a normal JS pack but serves as the single entry point | |
// for server side rendering | |
import React from 'react' | |
import ReactDOMServer from 'react-dom/server' | |
import { ServerStyleSheet } from 'styled-components' | |
import Footer from 'footer' | |
// This is purposely not defined yet. See the exec_js_renderer.rb | |
execJsGlobal.React = React | |
execJsGlobal.ReactDOMServer = ReactDOMServer | |
execJsGlobal.ServerStyleSheet = ServerStyleSheet | |
execJsGlobal.Footer = Footer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment