react_on_rails 1.2.2 → 2.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- metadata +5 -126
- data/.babelrc +0 -3
- data/.coveralls.yml +0 -1
- data/.dockerignore +0 -2
- data/.eslintignore +0 -5
- data/.eslintrc +0 -48
- data/.gitignore +0 -26
- data/.jscsrc +0 -26
- data/.rspec +0 -2
- data/.rubocop.yml +0 -70
- data/.scss-lint.yml +0 -205
- data/.travis.yml +0 -41
- data/CHANGELOG.md +0 -42
- data/Dockerfile_tests +0 -12
- data/Gemfile +0 -38
- data/README.md +0 -363
- data/Rakefile +0 -5
- data/app/assets/javascripts/react_on_rails.js +0 -241
- data/app/helpers/react_on_rails_helper.rb +0 -224
- data/docker-compose.yml +0 -11
- data/docs/LICENSE +0 -21
- data/docs/additional_reading/heroku_deployment.md +0 -23
- data/docs/additional_reading/manual_installation.md +0 -142
- data/docs/additional_reading/node_dependencies_and_npm.md +0 -29
- data/docs/additional_reading/optional_configuration.md +0 -34
- data/docs/additional_reading/react-and-redux.md +0 -36
- data/docs/additional_reading/react_router.md +0 -45
- data/docs/additional_reading/server_rendering_tips.md +0 -16
- data/docs/additional_reading/tips.md +0 -10
- data/docs/additional_reading/webpack.md +0 -46
- data/docs/code_of_conduct.md +0 -13
- data/docs/coding-style/linters.md +0 -64
- data/docs/coding-style/style.md +0 -42
- data/docs/contributing.md +0 -62
- data/docs/generator_testing.md +0 -20
- data/docs/install_and_releasing.md +0 -24
- data/docs/sample_generated_js/README.md +0 -4
- data/docs/sample_generated_js/client-generated.js +0 -12
- data/docs/sample_generated_js/server-generated.js +0 -25
- data/lib/generators/USAGE +0 -99
- data/lib/generators/react_on_rails/base_generator.rb +0 -198
- data/lib/generators/react_on_rails/bootstrap_generator.rb +0 -91
- data/lib/generators/react_on_rails/dev_tests_generator.rb +0 -30
- data/lib/generators/react_on_rails/generator_errors.rb +0 -15
- data/lib/generators/react_on_rails/generator_helper.rb +0 -58
- data/lib/generators/react_on_rails/heroku_deployment_generator.rb +0 -30
- data/lib/generators/react_on_rails/install_generator.rb +0 -111
- data/lib/generators/react_on_rails/js_linters_generator.rb +0 -19
- data/lib/generators/react_on_rails/react_no_redux_generator.rb +0 -41
- data/lib/generators/react_on_rails/react_with_redux_generator.rb +0 -52
- data/lib/generators/react_on_rails/ruby_linters_generator.rb +0 -33
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt +0 -4
- data/lib/generators/react_on_rails/templates/base/base/REACT_ON_RAILS.md +0 -16
- data/lib/generators/react_on_rails/templates/base/base/app/controllers/hello_world_controller.rb +0 -5
- data/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt +0 -5
- data/lib/generators/react_on_rails/templates/base/base/client/.babelrc +0 -3
- data/lib/generators/react_on_rails/templates/base/base/client/REACT_ON_RAILS_CLIENT_README.md +0 -3
- data/lib/generators/react_on_rails/templates/base/base/client/app/bundles/HelloWorld/startup/globals.jsx.tt +0 -5
- data/lib/generators/react_on_rails/templates/base/base/client/index.jade +0 -15
- data/lib/generators/react_on_rails/templates/base/base/client/npm-shrinkwrap.json +0 -2907
- data/lib/generators/react_on_rails/templates/base/base/client/package.json.tt +0 -98
- data/lib/generators/react_on_rails/templates/base/base/client/server.js +0 -64
- data/lib/generators/react_on_rails/templates/base/base/client/webpack.client.base.config.js.tt +0 -67
- data/lib/generators/react_on_rails/templates/base/base/client/webpack.client.hot.config.js.tt +0 -67
- data/lib/generators/react_on_rails/templates/base/base/client/webpack.client.rails.config.js +0 -41
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +0 -28
- data/lib/generators/react_on_rails/templates/base/base/lib/tasks/assets.rake.tt +0 -26
- data/lib/generators/react_on_rails/templates/base/base/lib/tasks/linters.rake.tt +0 -88
- data/lib/generators/react_on_rails/templates/base/base/package.json +0 -31
- data/lib/generators/react_on_rails/templates/base/server_rendering/client/app/bundles/HelloWorld/startup/serverGlobals.jsx +0 -3
- data/lib/generators/react_on_rails/templates/base/server_rendering/client/webpack.server.rails.config.js +0 -40
- data/lib/generators/react_on_rails/templates/bootstrap/app/assets/stylesheets/_bootstrap-custom.scss +0 -63
- data/lib/generators/react_on_rails/templates/bootstrap/client/assets/stylesheets/_post-bootstrap.scss +0 -10
- data/lib/generators/react_on_rails/templates/bootstrap/client/assets/stylesheets/_pre-bootstrap.scss +0 -8
- data/lib/generators/react_on_rails/templates/bootstrap/client/assets/stylesheets/_react-on-rails-sass-helper.scss +0 -19
- data/lib/generators/react_on_rails/templates/bootstrap/client/bootstrap-sass.config.js +0 -89
- data/lib/generators/react_on_rails/templates/dev_tests/.rspec +0 -2
- data/lib/generators/react_on_rails/templates/dev_tests/spec/features/hello_world_spec.rb +0 -25
- data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +0 -57
- data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +0 -21
- data/lib/generators/react_on_rails/templates/dev_tests/spec/spec_helper.rb +0 -95
- data/lib/generators/react_on_rails/templates/heroku_deployment/.buildpacks +0 -2
- data/lib/generators/react_on_rails/templates/heroku_deployment/Procfile +0 -1
- data/lib/generators/react_on_rails/templates/heroku_deployment/config/puma.rb +0 -15
- data/lib/generators/react_on_rails/templates/js_linters/client/.eslintignore +0 -1
- data/lib/generators/react_on_rails/templates/js_linters/client/.eslintrc +0 -48
- data/lib/generators/react_on_rails/templates/js_linters/client/.jscsrc +0 -18
- data/lib/generators/react_on_rails/templates/no_redux/base/client/app/bundles/HelloWorld/components/HelloWorldWidget.jsx +0 -39
- data/lib/generators/react_on_rails/templates/no_redux/base/client/app/bundles/HelloWorld/containers/HelloWorld.jsx +0 -33
- data/lib/generators/react_on_rails/templates/no_redux/base/client/app/bundles/HelloWorld/startup/HelloWorldAppClient.jsx.tt +0 -12
- data/lib/generators/react_on_rails/templates/no_redux/server_rendering/client/app/bundles/HelloWorld/startup/HelloWorldAppServer.jsx +0 -11
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/actions/helloWorldActionCreators.jsx +0 -8
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/components/HelloWorldWidget.jsx +0 -48
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/constants/helloWorldConstants.jsx +0 -8
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/containers/HelloWorld.jsx +0 -43
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/reducers/helloWorldReducer.jsx +0 -21
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/reducers/index.jsx +0 -14
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/startup/HelloWorldAppClient.jsx.tt +0 -21
- data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/store/helloWorldStore.jsx +0 -35
- data/lib/generators/react_on_rails/templates/redux/base/client/app/lib/middlewares/loggerMiddleware.js +0 -20
- data/lib/generators/react_on_rails/templates/redux/server_rendering/client/app/bundles/HelloWorld/startup/HelloWorldAppServer.jsx +0 -21
- data/lib/generators/react_on_rails/templates/ruby_linters/.rubocop.yml +0 -26
- data/lib/generators/react_on_rails/templates/ruby_linters/.scss-lint.yml +0 -205
- data/lib/generators/react_on_rails/templates/ruby_linters/lib/tasks/brakeman.rake +0 -17
- data/lib/generators/react_on_rails/templates/ruby_linters/lib/tasks/ci.rake +0 -33
- data/lib/generators/react_on_rails/templates/ruby_linters/ruby-lint.yml +0 -20
- data/lib/react_on_rails.rb +0 -6
- data/lib/react_on_rails/configuration.rb +0 -53
- data/lib/react_on_rails/engine.rb +0 -7
- data/lib/react_on_rails/prerender_error.rb +0 -31
- data/lib/react_on_rails/server_rendering_pool.rb +0 -111
- data/lib/react_on_rails/version.rb +0 -3
- data/rakelib/docker.rake +0 -33
- data/rakelib/dummy_apps.rake +0 -29
- data/rakelib/example_type.rb +0 -160
- data/rakelib/examples.rake +0 -103
- data/rakelib/examples_config.yml +0 -19
- data/rakelib/lint.rake +0 -37
- data/rakelib/run_rspec.rake +0 -65
- data/rakelib/task_helpers.rb +0 -44
- data/react_on_rails.gemspec +0 -31
- data/ruby-lint.yml +0 -24
data/Rakefile
DELETED
@@ -1,241 +0,0 @@
|
|
1
|
-
(function() {
|
2
|
-
this.ReactOnRails = {};
|
3
|
-
var turbolinksInstalled = (typeof Turbolinks !== 'undefined');
|
4
|
-
|
5
|
-
ReactOnRails.serverRenderReactComponent = function(options) {
|
6
|
-
var componentName = options.componentName;
|
7
|
-
var domId = options.domId;
|
8
|
-
var props = options.props;
|
9
|
-
var trace = options.trace;
|
10
|
-
var generatorFunction = options.generatorFunction;
|
11
|
-
var location = options.location;
|
12
|
-
var htmlResult = '';
|
13
|
-
var consoleReplay = '';
|
14
|
-
var hasErrors = false;
|
15
|
-
|
16
|
-
try {
|
17
|
-
var reactElementOrRouterResult = createReactElementOrRouterResult(componentName, props,
|
18
|
-
domId, trace, generatorFunction, location);
|
19
|
-
if (isRouterResult(reactElementOrRouterResult)) {
|
20
|
-
|
21
|
-
// We let the client side handle any redirect
|
22
|
-
// Set hasErrors in case we want to throw a Rails exception
|
23
|
-
hasErrors = !!reactElementOrRouterResult.routeError;
|
24
|
-
if (hasErrors) {
|
25
|
-
console.error('React Router ERROR: ' +
|
26
|
-
JSON.stringify(reactElementOrRouterResult.routeError));
|
27
|
-
} else {
|
28
|
-
if (trace) {
|
29
|
-
redirectLocation = reactElementOrRouterResult.redirectLocation;
|
30
|
-
redirectPath = redirectLocation.pathname + redirectLocation.search;
|
31
|
-
console.log('ROUTER REDIRECT: ' + componentName + ' to dom node with id: ' + domId +
|
32
|
-
', redirect to ' + redirectPath);
|
33
|
-
}
|
34
|
-
}
|
35
|
-
} else {
|
36
|
-
htmlResult = provideServerReact().renderToString(reactElementOrRouterResult);
|
37
|
-
}
|
38
|
-
}
|
39
|
-
catch (e) {
|
40
|
-
hasErrors = true;
|
41
|
-
htmlResult = ReactOnRails.handleError({
|
42
|
-
e: e,
|
43
|
-
componentName: componentName,
|
44
|
-
serverSide: true,
|
45
|
-
});
|
46
|
-
}
|
47
|
-
|
48
|
-
consoleReplayScript = ReactOnRails.buildConsoleReplay();
|
49
|
-
|
50
|
-
return JSON.stringify({
|
51
|
-
html: htmlResult,
|
52
|
-
consoleReplayScript: consoleReplayScript,
|
53
|
-
hasErrors: hasErrors,
|
54
|
-
});
|
55
|
-
};
|
56
|
-
|
57
|
-
// Passing either componentName or jsCode
|
58
|
-
ReactOnRails.handleError = function(options) {
|
59
|
-
var e = options.e;
|
60
|
-
var componentName = options.componentName;
|
61
|
-
var jsCode = options.jsCode;
|
62
|
-
var serverSide = options.serverSide;
|
63
|
-
|
64
|
-
var lineOne =
|
65
|
-
'ERROR: You specified the option generator_function (could be in your defaults) to be\n';
|
66
|
-
var lastLine =
|
67
|
-
'A generator function takes a single arg of props and returns a ReactElement.';
|
68
|
-
|
69
|
-
console.error('Exception in rendering!');
|
70
|
-
|
71
|
-
var msg = '';
|
72
|
-
if (componentName) {
|
73
|
-
var shouldBeGeneratorError = lineOne +
|
74
|
-
'false, but the React component \'' + componentName + '\' seems to be a generator function.\n' +
|
75
|
-
lastLine;
|
76
|
-
var reMatchShouldBeGeneratorError = /Can't add property context, object is not extensible/;
|
77
|
-
if (reMatchShouldBeGeneratorError.test(e.message)) {
|
78
|
-
msg += shouldBeGeneratorError + '\n\n';
|
79
|
-
console.error(shouldBeGeneratorError);
|
80
|
-
}
|
81
|
-
|
82
|
-
var shouldBeGeneratorError = lineOne +
|
83
|
-
'true, but the React component \'' + componentName + '\' is not a generator function.\n' +
|
84
|
-
lastLine;
|
85
|
-
var reMatchShouldNotBeGeneratorError = /Cannot call a class as a function/;
|
86
|
-
if (reMatchShouldNotBeGeneratorError.test(e.message)) {
|
87
|
-
msg += shouldBeGeneratorError + '\n\n';
|
88
|
-
console.error(shouldBeGeneratorError);
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
if (jsCode) {
|
93
|
-
console.error('JS code was: ' + jsCode);
|
94
|
-
}
|
95
|
-
|
96
|
-
if (e.fileName) {
|
97
|
-
console.error('location: ' + e.fileName + ':' + e.lineNumber);
|
98
|
-
}
|
99
|
-
|
100
|
-
console.error('message: ' + e.message);
|
101
|
-
console.error('stack: ' + e.stack);
|
102
|
-
if (serverSide) {
|
103
|
-
msg += 'Exception in rendering!\n' +
|
104
|
-
(e.fileName ? '\nlocation: ' + e.fileName + ':' + e.lineNumber : '') +
|
105
|
-
'\nMessage: ' + e.message + '\n\n' + e.stack;
|
106
|
-
var reactElement = React.createElement('pre', null, msg);
|
107
|
-
return provideServerReact().renderToString(reactElement);
|
108
|
-
}
|
109
|
-
};
|
110
|
-
|
111
|
-
ReactOnRails.wrapInScriptTags = function(scriptBody) {
|
112
|
-
if (!scriptBody) {
|
113
|
-
return '';
|
114
|
-
}
|
115
|
-
|
116
|
-
return '\n<script>' + scriptBody + '\n</script>';
|
117
|
-
};
|
118
|
-
|
119
|
-
ReactOnRails.buildConsoleReplay = function() {
|
120
|
-
var consoleReplay = '';
|
121
|
-
|
122
|
-
var history = console.history;
|
123
|
-
if (history && history.length > 0) {
|
124
|
-
history.forEach(function(msg) {
|
125
|
-
consoleReplay += '\nconsole.' + msg.level + '.apply(console, ' +
|
126
|
-
JSON.stringify(msg.arguments) + ');';
|
127
|
-
});
|
128
|
-
}
|
129
|
-
|
130
|
-
return ReactOnRails.wrapInScriptTags(consoleReplay);
|
131
|
-
};
|
132
|
-
|
133
|
-
function forEachComponent(fn) {
|
134
|
-
var els = document.getElementsByClassName('js-react-on-rails-component');
|
135
|
-
for (var i = 0; i < els.length; i++) {
|
136
|
-
fn(els[i]);
|
137
|
-
};
|
138
|
-
}
|
139
|
-
|
140
|
-
function reactOnRailsPageLoaded() {
|
141
|
-
forEachComponent(render);
|
142
|
-
}
|
143
|
-
|
144
|
-
function reactOnRailsPageUnloaded() {
|
145
|
-
forEachComponent(unmount);
|
146
|
-
}
|
147
|
-
|
148
|
-
function unmount(el) {
|
149
|
-
var domId = el.getAttribute('data-dom-id');
|
150
|
-
var domNode = document.getElementById(domId);
|
151
|
-
provideClientReact().unmountComponentAtNode(domNode);
|
152
|
-
}
|
153
|
-
|
154
|
-
function render(el) {
|
155
|
-
var componentName = el.getAttribute('data-component-name');
|
156
|
-
var domId = el.getAttribute('data-dom-id');
|
157
|
-
var props = JSON.parse(el.getAttribute('data-props'));
|
158
|
-
var trace = JSON.parse(el.getAttribute('data-trace'));
|
159
|
-
var generatorFunction = JSON.parse(el.getAttribute('data-generator-function'));
|
160
|
-
var expectTurboLinks = JSON.parse(el.getAttribute('data-expect-turbo-links'));
|
161
|
-
|
162
|
-
if (!turbolinksInstalled && expectTurboLinks) {
|
163
|
-
console.warn('WARNING: NO TurboLinks detected in JS, but it is in your Gemfile');
|
164
|
-
}
|
165
|
-
|
166
|
-
try {
|
167
|
-
var domNode = document.getElementById(domId);
|
168
|
-
if (domNode) {
|
169
|
-
var reactElementOrRouterResult = createReactElementOrRouterResult(componentName, props,
|
170
|
-
domId, trace, generatorFunction);
|
171
|
-
if (isRouterResult(reactElementOrRouterResult)) {
|
172
|
-
throw new Error('You returned a server side type of react-router error: ' +
|
173
|
-
JSON.stringify(reactElementOrRouterResult) +
|
174
|
-
'\nYou should return a React.Component always for the client side entry point.');
|
175
|
-
} else {
|
176
|
-
provideClientReact().render(reactElementOrRouterResult, domNode);
|
177
|
-
}
|
178
|
-
}
|
179
|
-
}
|
180
|
-
catch (e) {
|
181
|
-
ReactOnRails.handleError({
|
182
|
-
e: e,
|
183
|
-
componentName: componentName,
|
184
|
-
serverSide: false,
|
185
|
-
});
|
186
|
-
}
|
187
|
-
};
|
188
|
-
|
189
|
-
function createReactElementOrRouterResult(componentName, props, domId, trace, generatorFunction, location) {
|
190
|
-
if (trace) {
|
191
|
-
console.log('RENDERED ' + componentName + ' to dom node with id: ' + domId);
|
192
|
-
}
|
193
|
-
|
194
|
-
if (generatorFunction) {
|
195
|
-
return this[componentName](props, location);
|
196
|
-
} else {
|
197
|
-
return React.createElement(this[componentName], props);
|
198
|
-
}
|
199
|
-
}
|
200
|
-
|
201
|
-
function provideClientReact() {
|
202
|
-
if (typeof ReactDOM === 'undefined') {
|
203
|
-
if (React.version >= '0.14') {
|
204
|
-
console.warn('WARNING: ReactDOM is not configured in webpack.server.rails.config.js file as an entry.\n' +
|
205
|
-
'See: https://github.com/shakacode/react_on_rails/blob/master/docs/webpack.md for more detailed hints.');
|
206
|
-
}
|
207
|
-
|
208
|
-
return React;
|
209
|
-
}
|
210
|
-
|
211
|
-
return ReactDOM;
|
212
|
-
}
|
213
|
-
|
214
|
-
function provideServerReact() {
|
215
|
-
if (typeof ReactDOMServer === 'undefined') {
|
216
|
-
if (React.version >= '0.14') {
|
217
|
-
console.warn('WARNING: `react-dom/server` is not configured in webpack.server.rails.config.js file as an entry.\n' +
|
218
|
-
'See: https://github.com/shakacode/react_on_rails/blob/master/docs/webpack.md for more detailed hints.');
|
219
|
-
}
|
220
|
-
|
221
|
-
return React;
|
222
|
-
}
|
223
|
-
|
224
|
-
return ReactDOMServer;
|
225
|
-
}
|
226
|
-
|
227
|
-
function isRouterResult(reactElementOrRouterResult) {
|
228
|
-
return !!(reactElementOrRouterResult.redirectLocation ||
|
229
|
-
reactElementOrRouterResult.error);
|
230
|
-
}
|
231
|
-
|
232
|
-
// Install listeners when running on the client.
|
233
|
-
if (typeof document !== 'undefined') {
|
234
|
-
if (!turbolinksInstalled) {
|
235
|
-
document.addEventListener('DOMContentLoaded', reactOnRailsPageLoaded);
|
236
|
-
} else {
|
237
|
-
document.addEventListener('page:before-unload', reactOnRailsPageUnloaded);
|
238
|
-
document.addEventListener('page:change', reactOnRailsPageLoaded);
|
239
|
-
}
|
240
|
-
}
|
241
|
-
}.call(this));
|
@@ -1,224 +0,0 @@
|
|
1
|
-
# NOTE:
|
2
|
-
# For any heredoc JS:
|
3
|
-
# 1. The white spacing in this file matters!
|
4
|
-
# 2. Keep all #{some_var} fully to the left so that all indentation is done evenly in that var
|
5
|
-
require "react_on_rails/prerender_error"
|
6
|
-
|
7
|
-
module ReactOnRailsHelper
|
8
|
-
# react_component_name: can be a React component, created using a ES6 class, or
|
9
|
-
# React.createClass, or a
|
10
|
-
# `generator function` that returns a React component
|
11
|
-
# using ES6
|
12
|
-
# let MyReactComponentApp = (props) => <MyReactComponent {...props}/>;
|
13
|
-
# or using ES5
|
14
|
-
# var MyReactComponentApp = function(props) { return <YourReactComponent {...props}/>; }
|
15
|
-
# Exposing the react_component_name is necessary to both a plain ReactComponent as well as
|
16
|
-
# a generator:
|
17
|
-
# For client rendering, expose the react_component_name on window:
|
18
|
-
# window.MyReactComponentApp = MyReactComponentApp;
|
19
|
-
# For server rendering, export the react_component_name on global:
|
20
|
-
# global.MyReactComponentApp = MyReactComponentApp;
|
21
|
-
# See spec/dummy/client/app/startup/serverGlobals.jsx and
|
22
|
-
# spec/dummy/client/app/startup/ClientApp.jsx for examples of this
|
23
|
-
# props: Ruby Hash or JSON string which contains the properties to pass to the react object
|
24
|
-
#
|
25
|
-
# options:
|
26
|
-
# generator_function: <true/false> default is false, set to true if you want to use a
|
27
|
-
# generator function rather than a React Component.
|
28
|
-
# prerender: <true/false> set to false when debugging!
|
29
|
-
# trace: <true/false> set to true to print additional debugging information in the browser
|
30
|
-
# default is true for development, off otherwise
|
31
|
-
# replay_console: <true/false> Default is true. False will disable echoing server rendering
|
32
|
-
# logs to browser. While this can make troubleshooting server rendering difficult,
|
33
|
-
# so long as you have the default configuration of logging_on_server set to
|
34
|
-
# true, you'll still see the errors on the server.
|
35
|
-
# raise_on_prerender_error: <true/false> Default to false. True will raise exception on server
|
36
|
-
# if the JS code throws
|
37
|
-
# Any other options are passed to the content tag, including the id.
|
38
|
-
def react_component(component_name, props = {}, options = {})
|
39
|
-
# Create the JavaScript and HTML to allow either client or server rendering of the
|
40
|
-
# react_component.
|
41
|
-
#
|
42
|
-
# Create the JavaScript setup of the global to initialize the client rendering
|
43
|
-
# (re-hydrate the data). This enables react rendered on the client to see that the
|
44
|
-
# server has already rendered the HTML.
|
45
|
-
# We use this react_component_index in case we have the same component multiple times on the page.
|
46
|
-
react_component_index = next_react_component_index
|
47
|
-
react_component_name = component_name.camelize # Not sure if we should be doing this (JG)
|
48
|
-
if options[:id].nil?
|
49
|
-
dom_id = "#{component_name}-react-component-#{react_component_index}"
|
50
|
-
else
|
51
|
-
dom_id = options[:id]
|
52
|
-
end
|
53
|
-
|
54
|
-
# Setup the page_loaded_js, which is the same regardless of prerendering or not!
|
55
|
-
# The reason is that React is smart about not doing extra work if the server rendering did its job.
|
56
|
-
turbolinks_loaded = Object.const_defined?(:Turbolinks)
|
57
|
-
|
58
|
-
component_specification_tag =
|
59
|
-
content_tag(:div,
|
60
|
-
"",
|
61
|
-
class: "js-react-on-rails-component",
|
62
|
-
style: "display:none",
|
63
|
-
data: {
|
64
|
-
component_name: react_component_name,
|
65
|
-
props: props,
|
66
|
-
trace: trace(options),
|
67
|
-
generator_function: generator_function(options),
|
68
|
-
expect_turbolinks: turbolinks_loaded,
|
69
|
-
dom_id: dom_id
|
70
|
-
})
|
71
|
-
|
72
|
-
# Create the HTML rendering part
|
73
|
-
result = server_rendered_react_component_html(options, props, react_component_name, dom_id)
|
74
|
-
|
75
|
-
server_rendered_html = result["html"]
|
76
|
-
console_script = result["consoleReplayScript"]
|
77
|
-
|
78
|
-
content_tag_options = options.except(:generator_function, :prerender, :trace,
|
79
|
-
:replay_console, :id, :react_component_name,
|
80
|
-
:server_side, :raise_on_prerender_error)
|
81
|
-
content_tag_options[:id] = dom_id
|
82
|
-
|
83
|
-
rendered_output = content_tag(:div,
|
84
|
-
server_rendered_html.html_safe,
|
85
|
-
content_tag_options)
|
86
|
-
|
87
|
-
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
|
88
|
-
<<-HTML.html_safe
|
89
|
-
#{component_specification_tag}
|
90
|
-
#{rendered_output}
|
91
|
-
#{replay_console(options) ? console_script : ''}
|
92
|
-
HTML
|
93
|
-
end
|
94
|
-
|
95
|
-
def sanitized_props_string(props)
|
96
|
-
props.is_a?(String) ? json_escape(props) : props.to_json
|
97
|
-
end
|
98
|
-
|
99
|
-
# Helper method to take javascript expression and returns the output from evaluating it.
|
100
|
-
# If you have more than one line that needs to be executed, wrap it in an IIFE.
|
101
|
-
# JS exceptions are caught and console messages are handled properly.
|
102
|
-
def server_render_js(js_expression, options = {})
|
103
|
-
wrapper_js = <<-JS
|
104
|
-
(function() {
|
105
|
-
var htmlResult = '';
|
106
|
-
var consoleReplayScript = '';
|
107
|
-
var hasErrors = false;
|
108
|
-
|
109
|
-
try {
|
110
|
-
htmlResult =
|
111
|
-
(function() {
|
112
|
-
return #{js_expression};
|
113
|
-
})();
|
114
|
-
} catch(e) {
|
115
|
-
htmlResult = ReactOnRails.handleError({e: e, componentName: null,
|
116
|
-
jsCode: '#{escape_javascript(js_expression)}', serverSide: true});
|
117
|
-
hasErrors = true;
|
118
|
-
}
|
119
|
-
|
120
|
-
consoleReplayScript = ReactOnRails.buildConsoleReplay();
|
121
|
-
|
122
|
-
return JSON.stringify({
|
123
|
-
html: htmlResult,
|
124
|
-
consoleReplayScript: consoleReplayScript,
|
125
|
-
hasErrors: hasErrors
|
126
|
-
});
|
127
|
-
|
128
|
-
})()
|
129
|
-
JS
|
130
|
-
|
131
|
-
result = ReactOnRails::ServerRenderingPool.server_render_js_with_console_logging(wrapper_js)
|
132
|
-
|
133
|
-
# IMPORTANT: To ensure that Rails doesn't auto-escape HTML tags, use the 'raw' method.
|
134
|
-
html = result["html"]
|
135
|
-
console_log_script = result["consoleLogScript"]
|
136
|
-
raw("#{html}#{replay_console(options) ? console_log_script : ''}")
|
137
|
-
rescue ExecJS::ProgramError => err
|
138
|
-
# rubocop:disable Style/RaiseArgs
|
139
|
-
raise ReactOnRails::PrerenderError.new(component_name: "N/A (server_render_js called)",
|
140
|
-
err: err,
|
141
|
-
js_code: wrapper_js)
|
142
|
-
# rubocop:enable Style/RaiseArgs
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
def next_react_component_index
|
148
|
-
@react_component_index ||= -1
|
149
|
-
@react_component_index += 1
|
150
|
-
end
|
151
|
-
|
152
|
-
# Returns Array [0]: html, [1]: script to console log
|
153
|
-
# NOTE, these are NOT html_safe!
|
154
|
-
def server_rendered_react_component_html(options, props, react_component_name, dom_id)
|
155
|
-
return { "html" => "", "consoleReplayScript" => "" } unless prerender(options)
|
156
|
-
|
157
|
-
# On server `location` option is added (`location = request.fullpath`)
|
158
|
-
# React Router needs this to match the current route
|
159
|
-
|
160
|
-
# Make sure that we use up-to-date server-bundle
|
161
|
-
ReactOnRails::ServerRenderingPool.reset_pool_if_server_bundle_was_modified
|
162
|
-
|
163
|
-
# Since this code is not inserted on a web page, we don't need to escape.
|
164
|
-
props_string = props.is_a?(String) ? props : props.to_json
|
165
|
-
|
166
|
-
wrapper_js = <<-JS
|
167
|
-
(function() {
|
168
|
-
var props = #{props_string};
|
169
|
-
return ReactOnRails.serverRenderReactComponent({
|
170
|
-
componentName: '#{react_component_name}',
|
171
|
-
domId: '#{dom_id}',
|
172
|
-
props: props,
|
173
|
-
trace: #{trace(options)},
|
174
|
-
generatorFunction: #{generator_function(options)},
|
175
|
-
location: '#{request.fullpath}'
|
176
|
-
});
|
177
|
-
})()
|
178
|
-
JS
|
179
|
-
|
180
|
-
result = ReactOnRails::ServerRenderingPool.server_render_js_with_console_logging(wrapper_js)
|
181
|
-
|
182
|
-
if result["hasErrors"] && raise_on_prerender_error(options)
|
183
|
-
# We caught this exception on our backtrace handler
|
184
|
-
# rubocop:disable Style/RaiseArgs
|
185
|
-
fail ReactOnRails::PrerenderError.new(component_name: react_component_name,
|
186
|
-
# Sanitize as this might be browser logged
|
187
|
-
props: sanitized_props_string(props),
|
188
|
-
err: nil,
|
189
|
-
js_code: wrapper_js,
|
190
|
-
console_messages: result["consoleReplayScript"])
|
191
|
-
# rubocop:enable Style/RaiseArgs
|
192
|
-
end
|
193
|
-
result
|
194
|
-
rescue ExecJS::ProgramError => err
|
195
|
-
# This error came from execJs
|
196
|
-
# rubocop:disable Style/RaiseArgs
|
197
|
-
raise ReactOnRails::PrerenderError.new(component_name: react_component_name,
|
198
|
-
# Sanitize as this might be browser logged
|
199
|
-
props: sanitized_props_string(props),
|
200
|
-
err: err,
|
201
|
-
js_code: wrapper_js)
|
202
|
-
# rubocop:enable Style/RaiseArgs
|
203
|
-
end
|
204
|
-
|
205
|
-
def raise_on_prerender_error(options)
|
206
|
-
options.fetch(:raise_on_prerender_error) { ReactOnRails.configuration.raise_on_prerender_error }
|
207
|
-
end
|
208
|
-
|
209
|
-
def trace(options)
|
210
|
-
options.fetch(:trace) { ReactOnRails.configuration.trace }
|
211
|
-
end
|
212
|
-
|
213
|
-
def generator_function(options)
|
214
|
-
options.fetch(:generator_function) { ReactOnRails.configuration.generator_function }
|
215
|
-
end
|
216
|
-
|
217
|
-
def prerender(options)
|
218
|
-
options.fetch(:prerender) { ReactOnRails.configuration.prerender }
|
219
|
-
end
|
220
|
-
|
221
|
-
def replay_console(options)
|
222
|
-
options.fetch(:replay_console) { ReactOnRails.configuration.replay_console }
|
223
|
-
end
|
224
|
-
end
|