npm package: @openovate/webpack-react-router
Description: Webpack powered React router that supports virtual modules, virtual folders, and code splitting.
Author: Christian Blanquera
Latest version: 0.0.3
Deprecated: moved to reactus

# webpack-react-router

Webpack powered React router that supports virtual modules, virtual folders,
and code splitting.

## Install

```bash
$ npm install --save-dev @openovate/webpack-react-router
$ npm install --save-dev history
```

Additionally you may want to make sure your react environment is properly setup.
If your doing this from scratch, install the following modules as well.

```bash
$ npm i --save-dev @babel/core @babel/plugin-syntax-dynamic-import  @babel/plugin-transform-react-jsx @babel/polyfill @babel/preset-env @babel/preset-react @babel/register babel-loader react react-dom webpack webpack-cli webpack-dev-server webpack-hot-middleware
```

Also make sure you add the following scripts in `package.json`

```json
{
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server --mode development --no-inline"
  }
}
```

## Usage

Create a file called `webpack.config.js` in your project root.

```js
//#FILE: webpack.config.js
const path = require('path');
const ReactRouterPlugin = require('@openovate/webpack-react-router');

module.exports = {
  mode: 'development',
  entry: {
    index: './client/index.js'
  },
  output: {
    filename: '[name].bundle.js',
    publicPath: '/',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: { presets: ['@babel/env'] }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: false,
    port: 3000,
    hot: false
  },
  resolve: {
    extensions: ['*', '.js', '.jsx']
  },
  plugins: [
    new ReactRouterPlugin({
      router: 'client/router.js',
      routes: {
        '/': './Hello.jsx',
        '/about': './About.jsx'
      }
    })
  ]
};
```

You will also need to create a `.babelrc` file in your project root.

```json
{
  "presets": ["@babel/env", "@babel/preset-react"],
  "plugins": [
    "@babel/plugin-transform-react-jsx",
    "@babel/plugin-syntax-dynamic-import"
  ]
}
```

Create a file called `dist/index.html` in your project root.

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charSet="utf-8" />
    <title>Webpack React Router</title>
  </head>
  <body>
    <div id="root"></div>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <script src="/index.bundle.js"></script>
  </body>
</html>
```

Create a file called `client/index.js` in your project root.

```js
//#FILE: client/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import router from './router'

const { createBrowserHistory } = require('history');

ReactDOM.render(
  router(createBrowserHistory()),
  document.getElementById('root')
)
```

`./router` is a virtual React component generated by this plugin. It requires
that you use the [history](https://www.npmjs.com/package/history) module or
equivalent interface.

Create a component file called `client/Hello.jsx`.

```js
//#FILE: client/Hello.jsx
import React from 'react'
import Link from './Link'

export default class Hello extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello World</h1>
        <ul>
          <li><Link to="/" history={this.props.history}>Home</Link></li>
          <li><Link to="/about" history={this.props.history}>About Us</Link></li>
        </ul>
      </div>
    )
  }
}
```

The router will pass 2 properties to `this.props`.

 - `history` - which is the initial history was passed in `client/index.js`
 - `route` - which will have the information of the route matched

Create a component file called `client/About.jsx`

```js
//#FILE: client/About.jsx
import React from 'react'
import Link from './Link'

export default class About extends React.Component {
  render() {
    return (
      <div>
        <h1>About World</h1>
        <ul>
          <li><Link to="/" history={this.props.history}>Home</Link></li>
          <li><Link to="/about" history={this.props.history}>About Us</Link></li>
        </ul>
      </div>
    )
  }
}
```

Lastly, we need to create a component file called `client/Link.jsx`

```js
//#FILE: client/Link.jsx
import React from 'react';

export default class Link extends React.Component {
  constructor(props) {
    super(props);
    //quirk that is recommended by react. lame.
    this.handle = this.handle.bind(this);
  }

  handle(event) {
    const { to, history } = this.props
    const props = this.props.props || {}

    event.preventDefault();

    history.push(to, props);
    return false;
  }

  render() {
    const { to, children } = this.props;
    const props = { href: to, onClick: this.handle };
    return React.createElement('a', props, children);
  }
}
```

Run `$ npm start` in terminal and open `127.0.0.1:3000` in your browser.

### More Usage

see `test/env` in this repo for advance usage.

## Route Paths

This plugin uses [path-to-regexp](https://www.npmjs.com/package/path-to-regexp)
which is used by [react router](https://www.npmjs.com/package/react-router) and
[express](https://www.npmjs.com/package/express)

## Why

Needed a react router that considered the following.

 - Un-opinionated file structure which [Next.js](https://github.com/zeit/next.js/issues/4789) and [Razzle](https://github.com/jaredpalmer/razzle-unrouted) suffer from.
 - [Virtual Modules](https://www.npmjs.com/package/webpack-virtual-modules)
 - [Virtual Folders](https://www.npmjs.com/package/@openovate/webpack-virtual-folders)
 - [Code Splitting](https://webpack.js.org/guides/code-splitting/)

### Dynamic Imports with Dynamic Pathing

The following example underlies this topic.

```js
const routes = {
  '/': './screens/Hello.jsx',
  '/about': './screens/About.jsx'
}

import(routes['/']).then(component => {
  console.log(component.default)
})
```

Using dynamic `import()` in webpack 4 enables code splitting. When using dynamic
pathing like the above, `import(routes['/'])` simply won't work because webpack
could not predetermine the actual value when compiling.

For some reason, wrapping a JS template seems to work like the following.

```js
import(`${routes['/']}`).then(component => {
  console.log(component.default)
})
```

But for [Virtually Defined Modules](https://www.npmjs.com/package/webpack-virtual-modules),
this does not. Virtual Modules powers [Virtual Folders](https://www.npmjs.com/package/@openovate/webpack-virtual-folders). Virtual Folders allows folder resolution where webpack cannot find
files at their specified location. With Virtual Folders, we could simply tell
Webpack other places where it could be. This is ideal in the case where you may
want to modularize your project using a particular file structure in an
unopinionated way.

For the reasons above, a plugin that generates a virtual router was decided.