Skip to content

Commit

Permalink
Merge pull request #398 from shakacode/feature/merge-server-client-we…
Browse files Browse the repository at this point in the history
…bpack

Feature/merge server client webpack
  • Loading branch information
justin808 committed May 11, 2016
2 parents 56c0c8d + 6286cdd commit 0118cea
Show file tree
Hide file tree
Showing 63 changed files with 529 additions and 1,161 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ npm-debug.*
spec/dummy/client/npm-debug.log.*

/gen-examples

.DS_Store
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ sudo: required
language: ruby

rvm:
- 2.2.4
- 2.3.1

services:
- docker
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,38 @@
All notable changes to this project will be documented in this file. Items under `Unreleased` is upcoming features that will be out in next version.

Contributors: please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version.

## [Unreleased]
##### Breaking Changes
- Added automatic compilation of assets at precompile is now done by ReactOnRails. Thus, you don't need to provide your own assets.rake file that does the precompilation.
[#398](https://github.com/shakacode/react_on_rails/pull/398).
- Migration: you can either

1. Specify a `config/react_on_rails` setting for `npm_build_production_command` to be nil to turn this feature off.
2. Specify the script command you want to run to build your production assets, and remove your assets.rake file.

Here is the addition to the generated config file:
```ruby
# This configures the script to run to build the production assets by webpack. Set this to nil
# if you don't want react_on_rails building this file for you.
config.npm_build_production_command = "npm run build:production"
```

##### Fixed
- Fixed errors when server rendered props contain \u2028 or \u2029 characters [#375](https://github.com/shakacode/react_on_rails/pull/375) by [mariusandra]

##### Added
- Non-digested version of assets in public folder [#413](https://github.com/shakacode/react_on_rails/pull/413) by [alleycat-at-git]

##### Changed
- Only one webpack config is generated for server and client config. Package.json files were changed to reflect this [#398](https://github.com/shakacode/react_on_rails/pull/398).
- Added npm_build_test_command to allow developers to change what npm command is automatically run from rspec [#398](https://github.com/shakacode/react_on_rails/pull/398).
- Replace URI with Addressable gem. See [#405](https://github.com/shakacode/react_on_rails/pull/405) by [lucke84]

##### Removed
- Server rendering is no longer an option in the generator and is always accessible [#398](https://github.com/shakacode/react_on_rails/pull/398).
- removed lodash, jquery, and loggerMiddleware from the generated code [#398](https://github.com/shakacode/react_on_rails/pull/398).

## [5.2.0] - 2016-04-08
##### Added
- Support for React 15.0 to react_on_rails. See [#379](https://github.com/shakacode/react_on_rails/pull/379) by [brucek](https://github.com/brucek).
Expand Down
29 changes: 12 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@ In most cases, you should use the `prerender: false` (default behavior) with the
Now the server will interpret your JavaScript using [ExecJS](https://github.com/rails/execjs) and pass the resulting HTML to the client. We recommend using [therubyracer](https://github.com/cowboyd/therubyracer) as ExecJS's runtime. The generator will automatically add it to your Gemfile for you.
Note that **server-rendering requires globally exposing your components by setting them to `global`, not `window`** (as is the case with client-rendering). If using the generator, you can pass the `--server-rendering` option to configure your application for server-side rendering.
In the following screenshot you can see the 3 parts of React on Rails rendering:
1. A hidden HTML div that contains the properties of the React component, such as the registered name and any props. A JavaScript function runs after the page loads to convert take this data and build initialize React components.
Expand Down Expand Up @@ -199,7 +197,9 @@ and for a store:
reduxStore = MyReduxStore(props, railsContext);
```
Note, you never make these calls. This is what React on Rails does when either server or client rendering.
Note, you never make these calls. This is what React on Rails does when either server or client rendering. You'll be definining functions that take take these params and return a React component or a Redux Store.
(Note, see below [section](#multiple-react-components-on-a-page-with-one-store) on how to setup redux stores that allow multiple components to talk to the same store.)
The `railsContext` has: (see implementation in file react_on_rails_helper.rb for method rails_context for the definitive list).
Expand Down Expand Up @@ -233,6 +233,9 @@ Suppose you want to display a nav bar with the current navigation link highlight
##### Needing the I18n.locale
Suppose you want to server render your react components with localization applied given the current Rails locale. The `railsContext` contains the I18n.locale.

##### Configuring different code for server side rendering
Suppose you want to turn off animation when doing server side rendering. The `serverSide` value is just what you need.

#### Customization of the rails_context
You can customize the values passed in the `railsContext` in your `config/initializers/react_on_rails.rb`. Here's how.

Expand Down Expand Up @@ -265,28 +268,20 @@ In this case, a prop and value for `somethingUseful` will go into the railsConte
### Globally Exposing Your React Components
Place your JavaScript code inside of the provided `client/app` folder. Use modules just as you would when using webpack alone. The difference here is that instead of mounting React components directly to an element using `React.render`, you **expose your components globally and then mount them with helpers inside of your Rails views**.

+ *Normal Mode (JavaScript is Rendered on client):*

If you are not server rendering, `clientRegistration.jsx` will have

```javascript
import HelloWorld from '../components/HelloWorld';
import ReactOnRails from 'react-on-rails';
ReactOnRails.register({ HelloWorld });
```
+ *Server-Side Rendering:*

If you are server rendering, `serverRegistration.jsx` will have this. Note, you might be initializing HelloWorld with version specialized for server rendering.
This is an example of how to expose a component to the `react_component` view helper.

```javascript
// client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx
import HelloWorld from '../components/HelloWorld';
import ReactOnRails from 'react-on-rails';
ReactOnRails.register({ HelloWorld });
```

In general, you may want different initialization for your server rendered components.
#### Different Server-Side Rendering Code (and a Server Specific Bundle)

See below section on how to setup redux stores that allow multiple components to talk to the same store.
You may want different initialization for your server rendered components. For example, if you have animation that runs when a component is displayed, you might need to turn that off when server rendering. However, the `railsContext` will tell you if your JavaScript code is running client side or server side. So code that required a different server bundle previously may no longer require this!

If you do want different code to run, you'd setup a separate webpack compilation file and you'd specify a different, server side entry file. ex. 'serverHelloWorldApp.jsx'. Note, you might be initializing HelloWorld with version specialized for server rendering.

## ReactOnRails View Helpers API
Once the bundled files have been generated in your `app/assets/webpack` folder and you have exposed your components globally, you will want to run your code in your Rails views using the included helper method.
Expand Down
4 changes: 3 additions & 1 deletion app/helpers/react_on_rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# 1. The white spacing in this file matters!
# 2. Keep all #{some_var} fully to the left so that all indentation is done evenly in that var
require "react_on_rails/prerender_error"
require "addressable/uri"

module ReactOnRailsHelper
# The env_javascript_include_tag and env_stylesheet_link_tag support the usage of a webpack
Expand Down Expand Up @@ -270,7 +271,8 @@ def server_rendered_react_component_html(props, react_component_name, dom_id,
# On server `location` option is added (`location = request.fullpath`)
# React Router needs this to match the current route

# Make sure that we use up-to-date server-bundle
# Make sure that we use up-to-date bundle file used for server rendering, which is defined
# by config file value for config.server_bundle_js_file
ReactOnRails::ServerRenderingPool.reset_pool_if_server_bundle_was_modified

# Since this code is not inserted on a web page, we don't need to escape props
Expand Down
33 changes: 0 additions & 33 deletions docs/additional-reading/heroku-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,36 +64,3 @@ bundle
bin/rake db:migrate
bin/rake db:setup
```

#### 3. Create a rake file to add webpack compilation to asset precompilation. You may already have this file if you used the React on Rails generator.

```ruby
# lib/tasks/assets.rake
# The webpack task must run before assets:environment task.
# Otherwise Sprockets cannot find the files that webpack produces.
# This is the secret sauce for how a Heroku deployment knows to create the webpack generated JavaScript files.
Rake::Task["assets:precompile"]
.clear_prerequisites
.enhance(["assets:compile_environment"])

namespace :assets do
# In this task, set prerequisites for the assets:precompile task
task compile_environment: :webpack do
Rake::Task["assets:environment"].invoke
end

desc "Compile assets with webpack"
task :webpack do
sh "cd client && npm run build:client"
# If you are doing server rendering
# sh "cd client && npm run build:server"
end

task :clobber do
rm_r Dir.glob(Rails.root.join("app/assets/webpack/*"))
end
end
```



19 changes: 16 additions & 3 deletions docs/additional-reading/node-server-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,28 @@
### Warning: this is an experimental feature

The default server rendering exploits ExecJS to render react components.
Node server rendering allows you to use separate NodeJS process as a renderer. The process loads server-bundle.js and
Node server rendering allows you to use separate NodeJS process as a renderer. The process loads your configured server_bundle_js file and
then executes javascript to render the component inside its environment. The communication between rails and node occurs
via socket (`client/node/node.sock`)

### Getting started

To use node process just set `server_render_method = "NodeJS"` in `config/initializers/react_on_rails.rb`. To change back
to ExecJS set `server_render_method = "ExecJS"`

### Configuration

To change the name of server bundle adjust npm start script in `client/node/package.json`
You need to configure the name of the server bundle in two places:

1. JavaScript: Change the name of server bundle adjust npm start script in `client/node/package.json`
2. Ruby: The configured server bundle file is defined in `config/react_on_rails.rb`, and you'll have a webpack file that creates this. You maybe using the same file for client rendering.

```ruby
# This is the file used for server rendering of React when using `(prerender: true)`
# If you are never using server rendering, you may set this to "".
# If you are using the same file for client and server rendering, having this set probably does
# not affect performance.
config.server_bundle_js_file = "webpack-bundle.js"
```


42 changes: 38 additions & 4 deletions docs/additional-reading/rspec-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,50 @@ RSpec.configure do |config|
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
```

You can pass an RSpec metatag as an optional second parameter to this helper method if you want this helper to run on examples other than where `js: true` (default). The helper will compile webpack files at most once per test run. The helper will not compile the webpack files unless they are out of date (stale).
You can pass an RSpec metatag as an optional second parameter to this helper method if you want this helper to run on examples other than where `js: true` (default). The helper will compile webpack files at most once per test run. The helper will not compile the webpack files unless they are out of date (stale). The helper is configurable in terms of what command is used to prepare the files.

Please take note of the following:
- This utility assumes your build tasks for the static generated files are `npm run build:client` and `npm run build:server` and do not have the `--watch` option enabled.
- By default, the webpack processes look for the `app/assets/webpack` folders. If this folder is missing, is empty, or contains files with `mtime`s older than any of the files in your `client` folder, the helper will recompile your assets. You can override this inside of `config/initializers/react_on_rails.rb` by passing a filepath (relative to the root of the app) to the `generated_assets_dir` configuration option.
- This utility uses your `npm_build_test_command' to build the static generated files. This command **must** not include the `--watch` option. If you have different server and client bundle files, this command **must** create all the bundles.
- By default, the webpack processes look for the `app/assets/webpack` folders (configured as setting `webpack_generated_files` in the `config/react_on_rails.rb`. If this folder is missing, is empty, or contains files in the `config.webpack_generated_files` list with `mtime`s older than any of the files in your `client` folder, the helper will recompile your assets. You can override the location of these files inside of `config/initializers/react_on_rails.rb` by passing a filepath (relative to the root of the app) to the `generated_assets_dir` configuration option.

If you want to speed up the re-compiling process, you can call `npm run build:dev:client` (and `npm run build:dev:server` if doing server rendering) to have webpack run in "watch" mode and recompile these files in the background, which will be much faster when making incremental changes than compiling from scratch.
The following `config/react_on_rails.rb` settings **must** match your setup:
```ruby
# Directory where your generated assets go. All generated assets must go to the same directory.
# Configure this in your webpack config files. This relative to your Rails root directory.
config.generated_assets_dir = File.join(%w(app assets webpack))

# Define the files we need to check for webpack compilation when running tests.
config.webpack_generated_files = %w( webpack-bundle.js )

# If you are using the ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
# with rspec then this controls what npm command is run
# to automatically refresh your webpack assets on every test run.
config.npm_build_test_command = "npm run build:test"
```

If you want to speed up the re-compiling process, you can call `npm run build:development` (per below script) to have webpack run in "watch" mode and recompile these files in the background, which will be much faster when making incremental changes than compiling from scratch, assuming you have your setup like this:

```
"scripts": {
"build:test": "webpack --config webpack.config.js",
"build:production": "NODE_ENV=production webpack --config webpack.config.js",
"build:development": "webpack -w --config webpack.config.js"
},
```

[spec/dummy](../../spec/dummy) contains examples of how to set the proc files for this purpose.

## Hot Reloading
If you're using the hot reloading setup, you'll be running a Webpack development server to provide the JavaScript and mabye CSS assets to your Rails app. If you're doing server rendering, you'll also have a webpack watch process to refresh the server rendering file. **If your server and client bundles are different**, and you run specs, the client bundle files will not be created until React on Rails detects it's out of date. Then your script to create all bundle files will redo the server bundle file. There's a few simple remedies for this situation:

1. Switch to using static compilation of the webpack bundle files. This way, you're running a Webpack watch process to generate the same files used for tests as well as developmentment. You should have a separate Procfile for doing development using a "static" setup, rather than a "hot" setup.
2. Change your Procfile for starting the hot reloading server to also run a webpack process to automatically rebuild a the client assets while also doing hot reloading for browser testing.

Note, none of these issues matter if you're using the same file for both server and client side rendering.

## Using RubyMine and other IDEs that save files right before invoking tests
We had previously tried to allow using a process check to see if a Webpack watch process would already be automatically compiling the files. However, we removed this as it proved to be fragile with various setups. Thus, you want to hit the save keystroke when you save a JavaScript file, and then run your tests. Yes, you might not do this in time, and the test runner may be building the same files as you Webpack watch processes. This will probably happen infrequently, but if this does become an issue, we can look into bring back this functionality ([#398](https://github.com/shakacode/react_on_rails/pull/398)).

If you want to use a testing framework other than RSpec, please submit let us know on the changes you need to do and we'll update the docs.

![2016-01-27_02-36-43](https://cloud.githubusercontent.com/assets/1118459/12611951/7c56d070-c4a4-11e5-8a80-9615f99960d9.png)
Expand Down
10 changes: 0 additions & 10 deletions docs/additional-reading/webpack.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ You need both include `react-dom/server` and `react` as values for `entry`, like
// See use of 'vendor' in the CommonsChunkPlugin inclusion below.
vendor: [
'babel-core/polyfill',
'jquery',
'jquery-ujs',
'react',
'react-dom',
],
Expand All @@ -25,14 +23,6 @@ and you need to expose them:
// React is necessary for the client rendering:
{test: require.resolve('react'), loader: 'expose?React'},
{test: require.resolve('react-dom'), loader: 'expose?ReactDOM'},
{test: require.resolve('jquery'), loader: 'expose?jQuery'},
{test: require.resolve('jquery'), loader: 'expose?$'},
```

`webpack.server.config.js` is similar, but substitute:

```
entry: ['./yourCode', 'react-dom/server', 'react'],
```

and use this line rather than `{test: require.resolve('react-dom'), loader: 'expose?ReactDOM'},`:
Expand Down
5 changes: 3 additions & 2 deletions docs/api/ruby-api-hot-reload-view-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ static vs. hot is picked based on whether `ENV["REACT_ON_RAILS_ENV"] == "HOT"`
<!-- These do not use turbolinks, so no data-turbolinks-track -->
<!-- This is to load the hot assets. -->
<%= env_javascript_include_tag(hot: ['http://localhost:3500/vendor-bundle.js',
'http://localhost:3500/app-bundle.js']) %>
<!-- Note, you can have multiple files here. It's an array. -->
<%= env_javascript_include_tag(hot: ['http://localhost:3500/webpack-bundle.js]') %>
<!-- These do use turbolinks -->
<%= env_javascript_include_tag(static: 'application_static',
Expand Down
Loading

0 comments on commit 0118cea

Please sign in to comment.