# Rails webpack configuration

If you choose to use `kitten` Rails engine, you will have to setup your
application with [React on Rails](https://github.com/shakacode/react_on_rails)
and [webpack](https://webpack.github.io/).

If you are familiar with React on Rails, you can use your own workflow and skip
the following installation. You simply have to:
- add `kitten` Sass paths in your Sass path loader;
- add `kitten` JavaScript modules in your webpack resolving paths.

React on Rails proposes a simple implementation of webpack into the existing
Rails sprockets system:
- Webpack generates JavaScript and CSS bundles.
- Then, these latters are included in the asset pipeline.

This way, the responsability of fingerprinting (on production environment) is
still delegated to the asset pipeline and does not require a new management of
assets compilation.

## Table of content

- [File architecture](#file-architecture)
- [Webpack dependencies](#webpack-dependencies)
- [Build configuration](#build-configuration)
- [Web-dev server](#web-dev-server)
- [React server-side rendering build](#react-server-side-rendering-build)

## File architecture

This Rails project structure is not mandatory, but recommended. It is based on
the [structure proposed by React on
Rails](https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/recommended-project-structure.md).

```
|_ app
| |_ assets
|   |_ javascripts
|   |_ stylesheets
|   |_ webpack
|
|_ client
| |_ bin
| | |_ webpack-dev-server.js
| |
| |_ config
| |  |_ webpack
| |    |_ some_webpack_config
| |
| |_ javascripts
| |
| |_ entries
| | |_ app-kitten.js
| |
| |_ stylesheets
| |
| |_ .babelrc
| |_ node_modules
| |_ package.json
| |_ webpack.client.build.babel.js
| |_ webpack.client.dev.babel.js
|
|_ package.json
|
|_ others Rails project directories…
```

- `/app/assets/webpack`: All assets generated by webpack should be output inside
  this directory.
- `/client`: All client-side related files goes under this directory.
- `/client/.babelrc`: Babel configuration file to enable webpack configuration
  and web-dev server files to be written in es6.
- `/client/bin/webpack-dev-server.js`: File to start web-dev server for assets
  hot reloading.
- `/client/javascripts`: The application's js assets.
- `/client/stylesheets`: The application's css assets.
- `/client/config/webpack`: Your webpack common configuration files.
- `/client/package.json`: This `package.json` lists all the dependencies that
  are needed by your webpack configuration.
- `/client/webpack.***.babel.js`: your webpack configuratiocn files.
- `package.json`: This `package.json` enable you to run npm command from your
  project root.

You can find an example of all these files in `spec/dummy`.

## Webpack dependencies

The `client/package.json` file lists all dependencies that the webpack
compilation needs.

These modules can be categorized as follow:
- **babel modules**: Babel loader and presets to parse es6 files;
- **webpack loaders**: loaders that enables webpack to require styles, images
  and jsx files;
- **webpack plugins**: modules that enables webpack to process files others than
  JavaScript files.

You can check the file in `spec/dummy/client/package.json` to have the
exhaustive list.

## Build configuration

Configure webpack so it can fetch your assets files as entries and output the
files in the directory you want:

```js
const config = {
  entry: {
    application: [
      // Fetch files to create the output bundle.
      // The path is relative to the present configuration file.
      './app/javascripts/application',
    ]
  },
  output: {
    // Output bundles in `app/assets/webpack` directory.
    path: path.join(__dirname, 'app', 'assets','webpack'),
    filename: '[name]-bundle.js',
  },
}
```

Make sure to explicitly declare the directories to resolve modules and loaders
with an absolute path. This prevents webpack from failing to load required
dependencies.

```js
import path from 'path'
import kittenComponents from 'kitten-components'

const nodeModulesPath = path.resolve(__dirname, '../../node_modules')

const config = {
  …,
  resolve: {
    // Adds kitten paths to webpack.
    // Make sure `node_modules` directory is declared with an absolute path.
    root: nodeModulesPath.concat(kittenComponents.jsPaths)
                         .concat(kittenComponents.imagesPaths)
                         .concat(kittenComponents.fontsPaths)
  },
  resolveLoader: {
    // Add the absolute path to the `node_modules` directory to make sure
    // webpack can use loaders in every file it requires.
    root: nodeModulesPath
  },
}
```

Add the following loaders and plugins to load and parse correctly files
different from simple js files:

```js
const config = {
  …,
  plugins: [
    // The loaders matching scss files will be extracted by this plugin.
    new ExtractTextPlugin('application-bundle.css', { allChunks: true }),

    // Search for equal or similar files and deduplicate them in the output.
    new webpack.optimize.DedupePlugin(),
  ],
  module: {
    loaders: [
      {
        test: /\.(svg|png|jpe?g)$/,
        loader: 'file?name=images/[name].[ext]',
        include: /icons/, // Add images folders.
      },
      {
        test: /\.(woff|woff2|eot|ttf|svg)$/,
        loader: 'file?name=fonts/[name].[ext]',
        include: /maax/, // Add fonts folders.
      },
      {
        test: /\.jsx?$/,
        loader: 'babel',
        query: {
          presets: [
            'babel-preset-es2015',
            'babel-preset-react',
            'babel-preset-stage-0',
            'babel-preset-stage-2',
          ].map(require.resolve) // prevents resolving issues
        }
      },
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('css-loader!sass-loader'),
      },
    ],
  },
  sassLoader: {
    includePaths: resolve_paths.sass,
  }
}
```

## Web-dev server

Webpack has a feature called Hot Module Replacement (HMR) that helps replace old
modules with the newer ones without reloading the browser.

In the development environment, you can use it and setup a web-dev server to do
it.

As the updates are done only through the output js bundle, we need another webpack
configuration to manage it (no separate css file anymore).

You can check these files in the dummy app:
- the web-dev server:
  [spec/dummy/client/bin/webpack-dev-server.js](spec/dummy/client/bin/webpack-dev-server.js)
- the webapck config for hot reloading:
  [spec/dummy/client/webpack.client.dev.babel.js](spec/dummy/client/webpack.client.dev.babel.js)

## React server-side rendering build

For the moment, there is no need to create a specific build for React components
server-side rendering.
