Test Coverage React, Istanbul -_registerComponent(...): Target container is not a DOM element - javascript

I am writing an app with react / redux / webpack. I am building out my testing with karma, mocha and want to use istanbul for test coverage. In an attempt to get coverage to work with karma-coverage I have set up the following karma.config.js
var argv = require('yargs').argv;
var path = require('path');
var webpack = require('webpack');
const PATHS = {
test: path.join(__dirname, 'test'),
app: path.join(__dirname, 'app'),
}
module.exports = function(config) {
config.set({
// only use PhantomJS for our 'test' browser
browsers: ['PhantomJS'],
// just run once by default unless --watch flag is passed
singleRun: !argv.watch,
// which karma frameworks do we want integrated
frameworks: ['mocha', 'chai'],
// include some polyfills for babel and phantomjs
files: [
'node_modules/babel-polyfill/dist/polyfill.js',
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
// './test/**/*.js', // specify files to watch for tests,
'test/index.js',
],
preprocessors: {
// these files we want to be precompiled with webpack
// also run tests through sourcemap for easier debugging
// 'test/*.spec.js': ['webpack'],
'test/index.js': ['webpack', 'sourcemap']
},
// A lot of people will reuse the same webpack config that they use
// in development for karma but remove any production plugins like UglifyJS etc.
// I chose to just re-write the config so readers can see what it needs to have
webpack: {
devtool: 'inline-source-map',
resolve: {
// allow us to import components in tests like:
// import Example from 'components/Example';
root: PATHS.app,
// allow us to avoid including extension name
extensions: ['', '.js', '.jsx'],
// required for enzyme to work properly
alias: {
'sinon': 'sinon/pkg/sinon'
}
},
module: {
// don't run babel-loader through the sinon module
noParse: [
/node_modules\/sinon\//
],
preLoaders: [
// instrument only testing sources with Istanbul
// {
// test: /\.js$/,
// include: path.resolve('app/'),
// exclude: /node_modules/,
// loader: 'istanbul-instrumenter'
// }
{
test: /\.jsx?$/,
exclude: [/node_modules/, /test/],
loader: 'isparta-instrumenter-loader'
},
],
// run babel loader for our tests
loaders: [
{
test: /\.css$/,
loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
},
{
test: /\.jsx?$/,
loader: 'babel',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react', 'survivejs-kanban']
}
},
],
},
// required for enzyme to work properly
externals: {
'jsdom': 'window',
'cheerio': 'window',
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': 'window'
},
},
// displays tests in a nice readable format
reporters: ['spec', 'coverage'],
webpackMiddleware: {
noInfo: true
},
// tell karma all the plugins we're going to be using to prevent warnings
plugins: [
'karma-mocha',
'karma-chai',
'karma-webpack',
'karma-phantomjs-launcher',
'karma-spec-reporter',
'karma-sourcemap-loader',
'karma-coverage'
]
});
};
karma's entry point is just test/index.js. Which looks like this
// require all the tests so they will run.
const testsContext = require.context('.', true, /spec/);
testsContext.keys().forEach(testsContext);
// require all the .js and .jsx files in app so they will be included in coverage
const componentsContext = require.context('../app/', true, /jsx?$/);
// Date: April 16 2016
// Author: Benjamin Conant
// componentsContext.keys() is an array that includes file paths for all the
// .js and .jsx files in ./app .... karma fails with
// PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
// Invariant Violation: _registerComponent(...): Target container is not a DOM element.
// at /Users/benconant/Dev/MyFin/my-fin-front-end/test/index.js:15283 <- webpack:///~/react/~/fbjs/lib/invariant.js:45:0
// if the entry point index.jsx file is included. Seems to have somthing
// to do with trying to actually write to the DOM. So, I filter out index.jsx and the tests run very well.
// This means that we will probubly not be able to test index.jsx until this is solved.
let componentsContextKeysWithoutIndexJsx = componentsContext.keys().filter(function (filePath) { return filePath !== './index.jsx' });
componentsContextKeysWithoutIndexJsx.forEach(componentsContext);
// componentsContext.keys().forEach(componentsContext); --- the way it should be if we did not have to remove ./index.jsx
As you can see from the dated comment. If index.jsx is included, when I run the tests I get ...
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
Invariant Violation: _registerComponent(...): Target container is not a DOM element.
at /Users/benconant/Dev/MyFin/my-fin-front-end/test/index.js:15283 <- webpack:///~/react/~/fbjs/lib/invariant.js:45:0
here is my index.jsx for reference
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';;
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { Router, Route, Link, browserHistory, hashHistory, IndexRoute } from 'react-router';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
import configureStore from './store/configureStore';
import todoApp from './reducers';
import App from './containers/app/App';
import IntroSlides from './containers/IntroSlides';
import LandingPage from './containers/LandingPage';
let store = configureStore();
const history = process.env.HASH_ROUTING ? syncHistoryWithStore(hashHistory, store) : syncHistoryWithStore(browserHistory, store);
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={LandingPage} />
<Route path="intro" component={IntroSlides}/>
</Route>
</Router>
</Provider>,
document.getElementById('app')
)
I am about one week into the react ecosystem and so am almost certainly doing something silly but this has taken up many hours and help would greatly appreciated!

I had that same problem, which in my case occurred because React couldn't find the element in which it needed to render the html.
I found a quick fix by adding the following if statement into my main js file:
if ($('#app').length <= 0) {
$('body').prepend('<div id="app"></div>');
}
ReactDom.render(
<App />,
document.getElementById('app')
);
I'm aware this must not be the best way of fixing it, but at least it works for now. If anyone knows of a better way, please let us know!
I've also posted this solution on the thread you mentioned in your comment.

Related

react-three/postprocessing. Module parse failed

As soon as import Effect Composer into an active document, I get this error message. Could there be a possibility of my model/material causing this?? I am not even using the Effect Composer in Script. Or could it be that I need some kind of background??
*./node_modules/screen-space-reflections/dist/index.js 675:33
Module parse failed: Unexpected token (675:33)
You may need an appropriate loader to handle this file type.
| var boneMatrices = material[boneMatricesName];
|
if (material[boneMatricesName]?.length !== skeleton.boneMatrices.length) {
| delete material[boneMatricesName];
| boneMatrices = new Float32Array(skeleton.boneMatrices.length);*
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import './styles.css'
import { EffectComposer } from '#react-three/postprocessing'
//I just inserted it here to see if this causes the failure and it does
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Have you tried to install the es2015 preset??
REF: "You may need an appropriate loader to handle this file type" with Webpack and Babel
npm install babel-preset-es2015
You need to install the es2015 preset:
npm install babel-preset-es2015
and then configure babel-loader:
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015']
}
}
Maybe you need to add the file-loader under your modules.
https://www.npmjs.com/package/file-loader
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
},
],
},
};

webpack can't parse material ui button in a child component

I'm trying to bundle some React components with webpack.
But when I launch webpack --mode=development, i get this error :
Module parse failed: Unexpected token (6:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| const OwnButton = () => {
| return ( <Button color='secondary' variant='outlined'
This is my webpack config :
const path = require('path');
module.exports = {
entry: './src/app.js', // relative path
output: {
path: path.join(__dirname, 'public'), // absolute path
filename: 'js/bundle.js' // file name
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
};
This is my app.js config :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Button from '#material-ui/core/Button';
import OwnButton from './Components/OwnButton';
const template = <Button>Hello from react jsx</Button>;
ReactDOM.render(template, document.getElementById('root'));
ReactDOM.render(<OwnButton/>, document.getElementById('React-OwnButton'));
And the wanted component (index.jsx) :
import Button from '#material-ui/core/Button';
const OwnButton = () => {
return (
<Button color='secondary' variant='outlined'>
Hello from my own component
</Button>
);
};
export default OwnButton;
And my .babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
I don't understand how the call l.6 on app.js can work but not the on index.jsx l.6.
Where must I put the loader for this, in babel?
The problem is that you configured the Babel Loader to only intervene for .js files.
Try changing: test: /\.js$/ -> test: /\.((js)|(jsx))$/
This will make the loader work for .jsx files too.

React-hot-loader doesn't work with React-router-dom

So I've finally setup a working project with:
Electron (2.0.2)
React (16.4.0)
React-router-dom (4.2.2)
Webpack (4.11.0)
React-hot-loader (4.2.0)
And just when I started to develop some react components I noticed my project won't hot reload correctly. If I adjust something on the base url (/) it is updated correctly, but if I update something on a secondary url, say /test the webpack compiles, but I get the message Cannot GET /test.
I've tried a lot and I cannot seem to figure out what I am doing wrong. I looked into react-router-dom, since hot-reloading was an issue back in version 3.x, but they say it should be resolved now (in 4.x --> It works here..). Also i've added <base href="/"/> in my index.html so that is not it.
Can anyone tell me what I am doing wrong?
Webpack.common.js (This is merged into Webpack.dev.js)
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
},
resolve: {
modules: [path.resolve(__dirname), 'node_modules']
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
cacheDirectory: true,
presets: ['env', 'react'],
plugins: ['transform-runtime'],
env: {
development: {
plugins: ['react-hot-loader/babel']
},
production: {
presets: ['react-optimize']
}
}
}
}
]
}
};
Webpack.dev.js
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
entry: {
'app': [
'babel-polyfill',
'react-hot-loader/patch',
path.join(__dirname, 'src', 'index.js')
]
},
plugins: [
new webpack.HotModuleReplacementPlugin() // Enable hot module replacement
]
});
Index.js
import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import { App } from "./app";
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component/>
</AppContainer>,
document.getElementById("root")
);
};
render(App);
if (module.hot) {
module.hot.accept("./app", () => {
render(App);
});
}
App.js (my main entry point for my app, thus where I define my base routing)
import React, { Component } from 'react';
import { BrowserRouter, Route, NavLink } from 'react-router-dom';
import { Test } from './components/test';
import { Test2 } from './components/test2';
export class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<NavLink to="/">Home</NavLink>
<NavLink to="/test">Test</NavLink>
<div>
<Route exact path="/" component={Test}/>
<Route path="/test" component={Test2}/>
</div>
</div>
</BrowserRouter>
);
}
}
And the components 'test' and 'test2' are just plain simple react components with a 'hello world' text.
Anyone who sees anything that I am missing or doing wrong?
Thanks to this tutorial I found a way to adapt my project and get hot loading to work. It even made my code a bit cleaner and my build scripts simpeler.
Webpack.common.js
The first thing I needed to change was the babel-loader. I stole it from some tutorial, and it worked, but I did not know exactly what it did so I got rid of that code. I've also made the compilation of my code faster through the webpack.DllReferencePlugin.
Here is the updated webpack.common.js:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: {
app: [
'babel-polyfill',
'./src/index.js',
],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
plugins: ['react-hot-loader/babel'],
cacheDirectory: true,
presets: ['env', 'react'],
},
}
],
},
plugins: [
new webpack.DllReferencePlugin({
context: path.join(__dirname),
manifest: require('../dist/vendor-manifest.json'),
}),
new HtmlWebpackPlugin({
title: '<my-app-name>',
filename: 'index.html',
template: './public/index.html',
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, '../dist/*.dll.js'),
includeSourcemap: false // add this parameter
})
],
};
The AddAssetHtmlPlugin is required since the index.html is dynamically created (by the HtmlWebpackPlugin) for the dev server and you cannot hardcode the correct bundle import for the vendor.dll and the app.bundle (more here).
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const path = require('path');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
hot: true,
contentBase: path.resolve(__dirname, 'dist'),
historyApiFallback: true // Allow refreshing of the page
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // Enable hot reloading
]
});
What did I change:
I moved the entry point up to webpack.common.
I Removed the 'react-hot-loader/patch' from the entry
(optional) I've added some config options for the webpack-dev-server.
Index.js
This is the file that caused the hot-reload to fail. Especially the if(module.hot) part caused it to fail. So I've changed it to the following:
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { App } from './app';
const render = () => {
ReactDOM.render(
<AppContainer>
<App/>
</AppContainer>,
document.getElementById('app'),
);
};
render(App);
if (module.hot) {
module.hot.accept('./app', () => {
const NextApp = require('./app').default; // Get the updated code
render(NextApp);
});
}
The reason it works now is because now I fetch the new app and replace the old one, thus telling the hot-loader there has been a change. I could also just use module.hot.accept(), but that would make the react-hot-loader useless (you make use of the webpack hot-reloader) and this way I would also lose the state within my components every time I updated some code.
So there you go. I hope this will help anyone (other then myself).

Fail to improve site first time loading performance

I am creating a site using HTML5, JavaScript and React.
I am using Google Chrome Lighthouse to check the performance of the site first load.
This is the Lighthouse Runtime environment:
User agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Device Emulation Nexus 5X: Enabled
Network Throttling 562.5ms RTT, 1.4Mbps down, 0.7Mbps up: Enabled
CPU Throttling 4x slowdown: Enabled
After many iteration and fixes these are the scores I have on Lighthouse so far:
Performance 57
Progressive Web App 100
Accessibility 100
Best Practices 88
SEO 100
The best Practices is not at 100 because:
the host I have does not provide HTTP/2 (I might change if that's really an issue)
I have Google Ads script with has document.write() (can't change anything about that)
My big problem is with the Performance Metrics.
I have a white blank screen for more than 3.3s and the is fully showing after 8.3s!
The First meaningful paint is at 5,750ms, the first Interactive is at 6,720ms (same for Consistent Interactive)
The Perceptual Speed Index is 4,228 (score 64)
And the Estimated Input Latency is 16ms (score 100)
This is the Critical Chain Request:
This is the Network overview:
This is what I have done/tried so far:
Moved all components in the start page to async load using react-code-splitting
Optimized the images to use the new J2P, JXR and WEBP format, and made them the correct size so the browser does not have to resize them.
Moved the content of App.js into index.js (before the loading would get index.js and vendor.js in parallel, then a js file with all the css files inside and app.js, and after that it would load in parallel the components that are used inside app.js
Moved some css import to the components they are associated with.
Used webpack-bundle-analyzer to try to find common libraries and how they are splitted. As you can see in the following screenshot there are a lot of duplicated modules in my bundles that I cannot find how to extract for efficiency. (lodash, react-overlays,...)
This is my webpack.config.js
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const WebpackChunkHash = require('webpack-chunk-hash');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
//var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const OfflinePlugin = require('offline-plugin');
// To handle the regeneratorRuntime exception
require('babel-polyfill');
require('file-loader');
require('css-loader');
require('style-loader');
require('html-webpack-template');
/* Shared Dev & Production */
const config = {
context: path.resolve(__dirname, 'src'),
entry: {
index: [
// To handle the regeneratorRuntime exception
'babel-polyfill',
'./index.js'
],
vendor: ['offline-plugin/runtime', 'react', 'react-dom', 'react-router', 'react-redux', 'history', 'react-router-dom', 'redux', 'react-router-redux', 'redux-form', 'lodash'],
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.(ico|jpg|png|gif|eot|otf|jp2|jxr|webp|svg|ttf|woff|woff2)(\?.*)?$/,
exclude: /\/favicon.ico$/,
use: [
{
loader: 'file-loader',
query: {
name: '[path][name][hash].[ext]',
publicPath: '/'
}
}
]
},
{
test: /\.css$/,
include: path.join(__dirname, 'src/style'),
use: [
'style-loader',
{
loader: 'css-loader',
options: {
minimize: true
}
}
]
},
{
test: /\.(ico)(\?.*)?$/,
exclude: /node_modules/,
use: {
loader: 'file-loader',
options: { name: '[name].[ext]' },
},
},
{
test: /\.xml/,
use: {
loader: 'file-loader',
options: { name: '[name].[ext]' },
},
},
],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
publicPath: '/',
},
resolve: {
extensions: ['.js'],
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
plugins: [
// New moment.js optimization
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: 2
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new HtmlWebpackPlugin({
appMountId: 'app-root',
inlineManifestWebpackName: 'webpackManifest',
template: 'templateIndex.html',
title: 'Our Site',
}),
new OfflinePlugin({
AppCache: false,
ServiceWorker: { events: true },
}),
new BundleAnalyzerPlugin({analyzerMode: 'static'}),
],
devServer: {
historyApiFallback: true,
},
};
//if (process.env.NODE_ENV === 'production') {
config.output.filename = '[name].[chunkhash].js';
config.plugins = [
...config.plugins,
new webpack.HashedModuleIdsPlugin(),
new WebpackChunkHash(),
/*new ChunkManifestPlugin({
filename: 'chunk-manifest.json',
manifestVariable: 'webpackManifest',
inlineManifest: true,
}),*/
];
//}
module.exports = config;
my .babelrc
{
"presets": [
[
"env",
{ "modules": false }
],
"react",
"stage-0",
"stage-2"
],
"plugins": [ "syntax-dynamic-import", "transform-react-jsx", "transform-decorators-legacy" ]
}
And my index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import {Redirect, Route, Switch, withRouter} from 'react-router';
import { Provider } from 'react-redux';
import Async from 'react-code-splitting';
const TopBar = () => (<Async load={import('./containers/TopBar/TopBarAsync')} />);
const NavBar = () => (<Async load={import('./containers/NavBar/NavBarAsync')} />);
const BottomBar = () => (<Async load={import('./containers/BottomBar/BottomBarAsync')} />);
const UserHelpers = (props) => <Async load={import('./containers/UserHelpers')} componentProps={props} />
const Main = () => (<Async load={import('./containers/Main')} />);
const Blog = () => (<Async load={import('./containers/Blog')} />);
const PublicPage = () => (<Async load={import('./containers/PublicPage')} />);
import configureStore from './store/store';
const store = configureStore();
import './styles/font-awesome.min.css';
import './styles/app.css';
import './styles/TeamBrowser.css';
let googleTracking = null;
if(process.env.NODE_ENV==='production') {
// https://web-design-weekly.com/2016/07/08/adding-google-analytics-react-application/
ReactGA.initialize([my key here]);
googleTracking = function fireTracking() {
let page=window.location.pathname+window.location.search;
ReactGA.set({ page: page });
ReactGA.pageview(page);
}
const runtime=require('offline-plugin/runtime');
runtime.install({
onUpdateReady() {
runtime.applyUpdate();
},
onUpdated() {
window.location.reload();
},
});
}
render((
<Provider
store={store}>
<BrowserRouter
onUpdate={googleTracking}>
<div className="body">
<header>
<TopBar/>
<NavBar />
</header>
<main>
<Switch>
<Route path="/" exact component={Main} />
<Route path="/main" component={Main} />
<Route path="/Feed" component={Blog} />
<Route path="/Blog" component={Blog} />
<Route path="/:id" component={PublicPage} />
<Redirect to="/" />
</Switch>
</main>
<footer
className="footer">
<BottomBar />
</footer>
<UserHelpers />
</div>
</BrowserRouter>
</Provider>),
document.getElementById('app'));
Right now I am out of ideas about how to improve the loading speed to get a better Lighthouse score.
Can anyone spot anything that I do not know about?
To me it looks like chunk-manifest-webpack-plugin is not doing its job properly because it is not extracting all the common modules.
And second, the index.[hash].js bundle contains files that it should not (ex: utils.js, files under the 'actions' folder,... These are my own files, but they are not referenced in my index.js so why does it put them there?

Receiving 404s in my React app despite setting historyApiFallback to true for webpack-dev-server

I'm developing a React application, which is currently served at http://localhost:3000/, and I was hoping to get some basic routing working on it. I've been loosely following the Redux guide however I can't seem to set up a fallback URL correctly. After applying the webpack changes described in the guide, accessing any link such as http://localhost:3000/test results in a 404 response and the "Cannot GET /test" error. The index.jsx file which defines the route looks as follows:
index.jsx
import 'babel-polyfill';
import React from 'react';
import { Router, Route, browserHistory } from 'react-router';
import { createStore, applyMiddleware, compose } from 'redux';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import thunkMiddleware from 'redux-thunk';
import App from './components/App';
import rootReducer from './reducers/';
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
/* eslint-enable */
const store = createStore(rootReducer, {}, composeEnhancers(
applyMiddleware(thunkMiddleware),
));
if (module.hot) {
module.hot.accept();
}
render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/(:filter)" component={App} />
</Router>
</Provider>,
document.getElementById('root'),
);
Within the webpack config I've tried using historyApiFallback: true as well as historyApiFallback: { index: 'index.html' } and a few variations for the value of the index property. This is the full configuration:
webpack.config.js
var webpack = require('webpack');
var path = require('path');
module.exports = {
debug: true,
devtool: '#eval-source-map',
context: path.join(__dirname, 'src'),
resolve: {
root: path.resolve('./src'),
extensions: ['', '.webpack.js', '.web.js', '.js', '.jsx'],
},
devServer: {
historyApiFallback: {
index: '/',
},
},
entry: [
'webpack/hot/dev-server',
'webpack-hot-middleware/client',
'./index',
],
output: {
path: path.join(__dirname, 'src'),
publicPath: '/',
filename: 'bundle.js',
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
],
module: {
loaders: [
{ test: /\.jsx?$/, exclude: /node_modules/, loaders: ['react-hot', 'babel'] },
],
},
};
Any ideas for what might be going wrong?
Edit: There's also some BrowserSync configuration which may be related:
app.js
/**
* Require Browsersync along with webpack and middleware for it
*/
var browserSync = require('browser-sync');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
/**
* Require ./webpack.config.js and make a bundler from it
*/
var webpackConfig = require('./webpack.config');
var bundler = webpack(webpackConfig);
/**
* Run Browsersync and use middleware for Hot Module Replacement
*/
browserSync({
server: {
baseDir: 'src',
middleware: [
webpackDevMiddleware(bundler, {
// IMPORTANT: dev middleware can't access config, so we should
// provide publicPath by ourselves
publicPath: webpackConfig.output.publicPath,
// pretty colored output
stats: { colors: true },
// for other settings see
// http://webpack.github.io/docs/webpack-dev-middleware.html
}),
// bundler should be the same as above
webpackHotMiddleware(bundler),
],
},
// no need to watch '*.js' here, webpack will take care of it for us,
// including full page reloads if HMR won't work
files: [
'src/css/*.css',
'src/*.html',
],
});
When using browserHistory, you must configure your server appropriately to serve at all routed paths:
https://github.com/ReactTraining/react-router/blob/v2.0.0-rc5/docs/guides/basics/Histories.md#configuring-your-server
But if you don't want to put express server additionally just to make hot reloading work, use this:
webpack-dev-server -d --history-api-fallback --hot --inline --progress --colors
Though I was unable to get browserHistory working in my local dev environment, switching to hashHistory is a good enough fix for the short-term.

Categories

Resources