Gautam
Gautam

Reputation: 825

Webpack React build doesn't work on production

My React application works perfectly fine on dev environment where it runs on webpack-dev-server but it doesn't work on production (on Apache). It fails without any error on console or anywhere. Here is the code structure of my project.

When I run command npm run dist then app.js and index.html files gets created in dist directory and those I am copying to prod environment in apache htdocs directory. Now when I open my domain I see blank index.html page with no react component rendered/mounted. I can see in network tab app.js fetched successfully by the browser.

enter image description here

package.json

{
  "private": true,
  "version": "0.0.1",
  "description": "YOUR DESCRIPTION - Generated by generator-react-webpack",
  "main": "",
  "scripts": {
    "clean": "rimraf dist/*",
    "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist",
    "dist": "npm run copy & webpack --env=dist",
    "lint": "eslint ./src",
    "posttest": "npm run lint",
    "release:major": "npm version major && npm publish && git push --follow-tags",
    "release:minor": "npm version minor && npm publish && git push --follow-tags",
    "release:patch": "npm version patch && npm publish && git push --follow-tags",
    "serve": "node server.js --env=dev",
    "serve:dist": "node server.js --env=dist",
    "start": "sudo node server.js --env=dev",
    "test": "karma start",
    "test:watch": "karma start --autoWatch=true --singleRun=false"
  },
  "repository": "",
  "keywords": [],
  "author": "Your name here",
  "devDependencies": {
    "babel-core": "^6.0.0",
    "babel-eslint": "^6.0.0",
    "babel-loader": "^6.0.0",
    "babel-polyfill": "^6.3.14",
    "babel-preset-es2015": "^6.0.15",
    "babel-preset-react": "^6.0.15",
    "babel-preset-stage-0": "^6.5.0",
    "bower-webpack-plugin": "^0.1.9",
    "chai": "^3.2.0",
    "copyfiles": "^1.0.0",
    "css-loader": "^0.23.0",
    "eslint": "^3.0.0",
    "eslint-loader": "^1.0.0",
    "eslint-plugin-react": "^6.0.0",
    "file-loader": "^0.9.0",
    "glob": "^7.0.0",
    "isparta-instrumenter-loader": "^1.0.0",
    "json-loader": "^0.5.4",
    "karma": "^1.0.0",
    "karma-chai": "^0.1.0",
    "karma-coverage": "^1.0.0",
    "karma-mocha": "^1.0.0",
    "karma-mocha-reporter": "^2.0.0",
    "karma-phantomjs-launcher": "^1.0.0",
    "karma-sourcemap-loader": "^0.3.5",
    "karma-webpack": "^1.7.0",
    "minimist": "^1.2.0",
    "mocha": "^3.0.0",
    "node-sass": "^4.0.0",
    "null-loader": "^0.1.1",
    "open": "0.0.5",
    "phantomjs-prebuilt": "^2.0.0",
    "react-addons-test-utils": "^15.0.0",
    "react-hot-loader": "^1.2.9",
    "rimraf": "^2.4.3",
    "sass-loader": "^4.1.0",
    "style-loader": "^0.13.0",
    "url-loader": "^0.5.6",
    "webpack": "^1.12.0",
    "webpack-dev-server": "^1.12.0"
  },
  "dependencies": {
    "jquery": "^3.1.1",
    "react": "^15.0.0",
    "react-datepicker": "^0.39.0",
    "react-dom": "^15.0.0",
    "react-router": "^3.0.0"
  }
}

webpack.config.js

const path = require('path');
const args = require('minimist')(process.argv.slice(2));

// List of allowed environments
const allowedEnvs = ['dev', 'dist', 'test'];

// Set the correct environment
let env;
if (args._.length > 0 && args._.indexOf('start') !== -1) {
  env = 'test';
} else if (args.env) {
  env = args.env;
} else {
  env = 'dev';
}
process.env.REACT_WEBPACK_ENV = env;

/**
 * Build the webpack configuration
 * @param  {String} wantedEnv The wanted environment
 * @return {Object} Webpack config
 */
function buildConfig(wantedEnv) {
  let isValid = wantedEnv && wantedEnv.length > 0 && allowedEnvs.indexOf(wantedEnv) !== -1;
  let validEnv = isValid ? wantedEnv : 'dev';
  let config = require(path.join(__dirname, 'cfg/' + validEnv));
  return config;
}

module.exports = buildConfig(env);

base.js

'use strict';
let path = require('path');
let defaultSettings = require('./defaults');

// Additional npm or bower modules to include in builds
// Add all foreign plugins you may need into this array
// @example:
// let npmBase = path.join(__dirname, '../node_modules');
// let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ];
let additionalPaths = [];

module.exports = {
  additionalPaths: additionalPaths,
  port: defaultSettings.port,
  debug: true,
  devtool: 'eval',
  output: {
    path: path.join(__dirname, '/../dist/assets'),
    filename: 'app.js',
    publicPath: defaultSettings.publicPath
  },
  devServer: {
    contentBase: './src/',
    historyApiFallback: true,
    hot: true,
    port: defaultSettings.port,
    publicPath: defaultSettings.publicPath,
    noInfo: false
  },
  resolve: {
    extensions: ['', '.js', '.jsx'],
    alias: {
      actions: `${defaultSettings.srcPath}/actions/`,
      components: `${defaultSettings.srcPath}/components/`,
      sources: `${defaultSettings.srcPath}/sources/`,
      stores: `${defaultSettings.srcPath}/stores/`,
      styles: `${defaultSettings.srcPath}/styles/`,
      config: `${defaultSettings.srcPath}/config/` + process.env.REACT_WEBPACK_ENV,
      'react/lib/ReactMount': 'react-dom/lib/ReactMount'
    }
  },
  module: {}
};

dist.js

    let path = require('path');
    let webpack = require('webpack');

    let baseConfig = require('./base');
    let defaultSettings = require('./defaults');

    // Add needed plugins here
    let BowerWebpackPlugin = require('bower-webpack-plugin');

    let config = Object.assign({}, baseConfig, {
      entry: path.join(__dirname, '../src/index'),
      cache: false,
      devtool: 'sourcemap',
      plugins: [
        new webpack.optimize.DedupePlugin(),
        new webpack.DefinePlugin({
          'process.env.NODE_ENV': '"production"'
        }),
        new BowerWebpackPlugin({
          searchResolveModulesDirectories: false
        }),
        new webpack.optimize.UglifyJsPlugin(),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.AggressiveMergingPlugin(),
        new webpack.NoErrorsPlugin()
      ],
      module: defaultSettings.getDefaultModules()
    });

    // Add needed loaders to the defaults here
    config.module.loaders.push({
      test: /\.(js|jsx)$/,
      loader: 'babel',
      include: [].concat(
        config.additionalPaths,
        [ path.join(__dirname, '/../src') ]
      )
    });

module.exports = config;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

import App from './components/Main';

// Render the main component into the dom

ReactDOM.render(
  <Router history = { browserHistory }>
    <Route path = { '/' } component = { App } />
  </Router>
, document.getElementById('app'));

Main.js

import React from 'react';

class AppComponent extends React.Component {
  render() {
    return (
      <div>
        Hello World............
      </div>
    )
  }
}

export default AppComponent;

index.html

<!doctype html>
<html>
<head>
  <title>Hi Lala</title>
</head>
<body>
<div id='app'></div>

<script src="/assets/app.js"></script>
</body>
</html>

.babelrc

{
  "presets": [
    "es2015",
    "stage-0",
    "react"
  ]
}

enter image description here

Upvotes: 3

Views: 9555

Answers (2)

Roger Trussell
Roger Trussell

Reputation: 1

I was having similar problems, and I used a solution similar to Paul S. I looked at the recent React Router documentation. I read about the "basename" property to the BrowserRouter component. I am hosting a production build in a subdirectory of my Apache server. I need that property when making production builds.

I also use an .htaccess file in that subdirectory and that file looks like this:

RewriteEngine On  
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]

Upvotes: 0

Paul S
Paul S

Reputation: 36787

Given the above comment where you provide the link to your website, you are not hosting the site at the root /, but rather at /salonathome. This means that you will need to specify a basename for your history instance.

import { createHistory } from 'history'

const history = useRouterHistory(createHistory)({
  basename: '/salonathome'
})

render((
  <Router history={history}>
    <Route ... />
  </Router>
), document.getElementById('app'))

Upvotes: 6

Related Questions