Why can I only hit my react/webpack app at root (/)? - reactjs

I spent today setting up react-transform with the hot-loader. I have an app with 2 routes, / and /reports. I can hit the / and then click on a Link to take me to /reports and it works fine, but it I try to go to /reports straight away, then I get a 404. Here is my webpack.config.js
module.exports = {
devtool: 'source-map',
entry: {
app: [
'./App.js',
'webpack-hot-middleware/client'
],
vendors: vendors
},
output: {
filename: './bundle.[hash].js',
chunkFilename: './[id].[hash].js',
path: __dirname + '/dist'
},
module: {
loaders: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}, {
test: /\.scss$/,
loaders: ["style", "css", "sass"]
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'index.ejs'
}),
new CommonsPlugin({
name: 'vendors',
filename: 'vendors.js',
minChunks: Infinity
}),
new CleanWebpackPlugin(['dist'], {
root: __dirname,
verbose: true,
dry: false
}),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
};
And here is my server.js
var app = express();
var compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('/'));
app.listen(3000);
I have tried adding a route with
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
But /dist/index.html is not there because of the hot-loader has it in memory. The exact error is
Error: ENOENT: no such file or directory, stat '/Users/jason/Developer/stack/webapp/dist/index.html'
at Error (native)
What do I need to change in my configuration?

My express server was set up wrong. I added this route to express:
app.get('*', function(req, res) {
var memoryFs = compiler.outputFileSystem;
var index = path.join(webpackConfig.output.path, 'index.html');
var html = memoryFs.readFileSync(index);
res.end(html)
});
And now I am working perfectly.

Related

Webpack gzip bundle - Uncaught SyntaxError: Unexpected token <

I am working on a react project using webpack for bundling. I wanted to reduce my bundle size so decided to use a compression plugin to serve a gzip file to get a nice small bundle size. The project builds fine and I get a nice small bundle size but here's my issues..when I go to serve my current project here is the error i get:
Looking into I realized that for whatever reason instead of serving the contents of main.js or vendor.js it's returning my index.html file
I am fairly certain my apache server is configured to handle gzip encoding this as I can see it in the response header:
Here is the webpack config I am using:
const appConstants = function() {
switch (process.env.NODE_ENV) {
case 'local':
const localConfig = require('./config/local');
return localConfig.config();
case 'development':
const devConfig = require('./config/development');
return devConfig.config();
case 'production':
default:
const prodConfig = require('./config/production');
return prodConfig.config();
}
};
const HtmlWebPackPlugin = require("html-webpack-plugin");
const webpack = require('webpack');
const CompressionPlugin = require('compression-webpack-plugin');
const htmlWebpackPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html",
hash: true
});
const compressionPlugin = new CompressionPlugin({
filename: "[path].gz[query]",
test: /\.(js|css)$/,
algorithm: 'gzip',
deleteOriginalAssets: true
});
let webpackConfig = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
exclude: [ /assets/, /node_modules/ ],
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]_[local]_[hash:base64]",
sourceMap: true,
minimize: true
}
}
]
},
{
test: /\.(pdf|jpg|png|gif|svg|ico)$/,
exclude: [/node_modules/],
use: [
{
loader: 'file-loader'
},
]
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
exclude: [/node_modules/],
use: {
loader: 'url-loader?limit100000'
}
}
]
},
entry: [ "#babel/polyfill", "./src/index.js"],
output: {
publicPath: appConstants().DS_BASENAME ? JSON.parse(appConstants().DS_BASENAME) : '/',
chunkFilename: '[name].[chunkhash].js'
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
htmlWebpackPlugin,
compressionPlugin,
new webpack.DefinePlugin({
'process.env': appConstants()
}),
new webpack.EnvironmentPlugin(['NODE_ENV']),
],
devServer: {
historyApiFallback: true
}
};
// configure source map per-env
if (process.env.NODE_ENV === 'local') {
webpackConfig.devtool = 'source-map';
} else {
webpackConfig.devtool = 'hidden-source-map';
}
module.exports = webpackConfig;
I cannot figure out why the gzip is not being read/recognized by the browser. I've tried several post with similar issues but no solutions.
You need to add middleware function to handle .js and .css file with suffix .gz
Like that
const app = express()
app.get('*.js', function(req, res, next) {
req.url = req.url + '.gz'
res.set('Content-Encoding', 'gzip')
res.set('Content-Type', 'text/javascript')
next()
})
app.get('*.css', function(req, res, next) {
req.url = req.url + '.gz'
res.set('Content-Encoding', 'gzip')
res.set('Content-Type', 'text/css')
next()
})
app.use('/dist', serve('./dist', true))
app.use(express.static('./dist'));
You have to add middleware function before express.static
Good luck!

webpack dev server not allowing POST fetch request - react app

I am developing my first webapack-react app using webpack-dev-server, however I am running into issues sending POST requests to my flask backend api as I keep getting a 400 bad request error. Here is my post request:
fetch('/api/login', {
method: 'post',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"first_name": "name"})
});
If I tweak the above to a GET request (and remove body), the request goes through fine and I the api returns data.
Digging a little deeper, it seems as though webpack-dev-server doesn't permit POST requests - have I understood that correctly and is there a workaround?
Here is my webpack dev configuration:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
host:'0.0.0.0',
hot: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://flaskapp:5090',
pathRewrite: {'^/api': ''},
secure: false,
}
},
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
});
This is by no means the accepted answer. I'm just sharing my Webpack file to how I got it to work.
Here is my webpack.config file. I had the same issue the other day with making fetch requests with POST. However, I looked online to see if I could remove the /api from my fetch requests so I tried the rewrite module and it didn't work for me so I just removed it. In my fetch requests if I wanted to fetch something for example it would be /api/signup for example and my fetch worked. I also set up my proxy in webpack not in package.json
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const HtmlWebPackPlugin = require('html-webpack-plugin');
var browserConfig = {
devServer: {
historyApiFallback: true,
proxy: {
"/api": "http://localhost:3012"
}
},
entry: ['babel-polyfill', __dirname + '/src/index.js'],
output: {
path: path.resolve(__dirname + '/public'),
filename: 'bundle.js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
query: {
presets: ['react', 'env', 'stage-0']
}
}
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: './public/index.html',
})
]
}
var serverConfig = {
target: 'node',
externals: [nodeExternals()],
entry: __dirname + '/server/main.js',
output: {
path: path.resolve(__dirname + '/public'),
filename: 'server.js',
publicPath: '/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
query: {
presets: ['react', 'env', 'stage-0']
}
}
}
]
}
}
module.exports = [browserConfig, serverConfig]

Images are not loading in browser with react, webpack and express

I am working on a site build in react. It was working fine until I have implemented express. After implementation of express, I am not able to load some resources, especially images in the browser. The image path is coming correct but images are not showing in the browser.
webpack.config.js is:
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
devtool: 'eval-source-map',
entry: [
'webpack-hot-middleware/client?reload=true',
path.join(__dirname, 'app/index.js')
],
output: {
path: path.join(__dirname, '/dist/'),
filename: '[name].js',
publicPath: '/'
},
plugins: [
new ExtractTextPlugin('/bundle.css', { allChunks: true }),
new HtmlWebpackPlugin({
template: 'app/index.html',
inject: 'body',
filename: 'index.html'
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
],
resolve: {
extensions: ['', '.scss', '.css', '.js', '.json'],
modulesDirectories: [
'node_modules',
path.resolve(__dirname, './node_modules')
]
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
"presets": ["react", "es2015", "stage-0", "react-hmre"]
}
}, {
test: /\.json?$/,
loader: 'json'
}, {
test: /\.scss$/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass')
}, {
test: /\.css$/,
loader: 'style-loader!css-loader'
},{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
loader: "url-loader?limit=10000"
},{
test: /\.less$/, loader: "style-loader!css-loader!less-loader"
},{
test: /\.(ttf|eot|svg|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "file-loader"
}]
}
};
server.js file content is as follows:
const path = require("path");
const express = require("express");
const webpack = require("webpack");
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackHotMiddleware = require("webpack-hot-middleware");
const config = require("./webpack.config.js");
const app = express(),
DIST_DIR = path.join(__dirname, "dist"),
HTML_FILE = path.join(DIST_DIR, "index.html"),
isDevelopment = process.env.NODE_ENV !== "production",
DEFAULT_PORT = 3000,
compiler = webpack(config);
app.set("port", process.env.PORT || DEFAULT_PORT);
if (isDevelopment) {
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}));
app.use(webpackHotMiddleware(compiler));
app.get("*", (req, res, next) => {
compiler.outputFileSystem.readFile(HTML_FILE, (err, result) => {
if (err) {
return next(err);
}
res.set('content-type', 'text/html');
res.send(result);
res.end();
});
});
}
else {
app.use(express.static(DIST_DIR));
app.get("*", (req, res) => res.sendFile(HTML_FILE));
}
app.listen(app.get("port"));
package.json is as follows:
"main": "server.js",
"script": {
"start": "babel-node server-es6.js",
"build:server": "babel server-es6.js --out-file server.js",
"build:client": "webpack -p --config webpack.config.js --progress"
},
Site is loading but some css are not loading. Console throw error:
GET http://bundle.css/ net::ERR_NAME_NOT_RESOLVED
Image path comes correctly as http://localhost:3000/img/img1.png but it's not showing in the browser. I think the issue is with webpack public path.
When I am using <img src={require('/images/image-name.png')} />, its working fine. But I don't want to do that because it's very heavy codebase and also I think it's not a nice solution.
I have taken help from webpack-express-boilerplate.
In chrome Network tool, image type is coming as text/html.
If your file path is static, you can import the file once and then provide it as a src
import image from '/path/to/images/image-name.png';
...
<img src={image} />

Webpack Dev Server with React Hot Loader

I have a webpack configuration that works perfect in itself. I'm trying to install React Hot Loader together with HMR as suggested, which require webpack-dev-server. Here I cannot get it to work. I can not find where my bundle is located. I want it to be just at localhost:3000.
My webpack.config.js:
var webpack = require('webpack');
var path = require('path');
module.exports = {
watch: true,
devtool: 'eval',
// entry: './src/main.js', This runs just for webpack bundling
entry:[
'webpack-dev-server/client?http:localhost:9000', // WebpackDevServer host and port
'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
'./src/main.js' // Your appʼs entry point
],
output: {
path: path.resolve(__dirname, 'public', 'dist'),
filename: 'main.js'/*,
publicPath: '/dist/'*/
},
module: {
loaders: [{
test: /\.js$/,
loaders: ['react-hot', 'babel-loader?cacheDirectory=true,presets[]=react,presets[]=es2015'],
exclude: function(path) {
var isModule = path.indexOf('node_modules') > -1;
var isJsaudio = path.indexOf('jsaudio') > -1;
if (isModule && !isJsaudio) {
return true;
}
}
}, {
test: /\.json$/,
loader: "json-loader"
}]
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
resolve: {
extensions: ['', '.js', '.json', 'index.js'],
root: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, 'node_modules', 'jsaudio', 'src')
]
},
target: 'web',
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
And the webpack-server.js:
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
publicPath: '/dist/',
hot: true,
historyApiFallback: true
}).listen(9000, 'localhost', function (err, result) {
if (err) {
return console.log(err);
}
console.log('Listening at http://localhost:9000/');
});
Update: The linked question does not help, especially since it does not even have a confirmed answer.
I would suggest trying out react-hot-loader v3 as the updates to get hot-reloading working have been simplified (in my opinion!).
in webpack then try point now only needs:
entry: {
app: [
'react-hot-loader/patch',
'webpack-hot-middleware/client',
`${SRC}/client-entry.js`
]
}
here is a link to an example app, react-lego that i've created to help people add react-hot-loader v3 to their apps. hope it helps

Specify destination folder in url-loader for webpack

I have images path in css like url(images/image1.jpg) and the loader is defined as
{ test: /\.(png|woff|woff2|eot|ttf|svg|jpg)$/, loader: 'url-loader?limit=1&name=images/[name].[ext]' },
The issue I am facing is that after build process the images are copied to respective folder but the path is pointing to images/image1.jpg relative to the css file location. I want it to be relative to the root directory.
I tried adding a leading / in the name query parameter and it partially fixed the issue
{ test: /\.(png|woff|woff2|eot|ttf|svg|jpg)$/, loader: 'url-loader?limit=1&name=images/[name].[ext]' },
but when I move my files into a sub folder it still points to the root of the domain instead of the folder.
Can you tell me what I am missing in the configuration?
Here is the webpack configuration
'use strict';
var webpack = require('webpack'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
OpenBrowserPlugin = require('open-browser-webpack-plugin'),
ExtractTextPlugin = require('extract-text-webpack-plugin'),
path = require('path'),
srcPath = path.join(__dirname, 'src'),
jsDestPath = 'scripts/';
module.exports = {
target: 'web',
cache: true,
entry: {
common: ['react', 'react-router', 'alt', 'es6-promise'],
offline: path.join(srcPath, 'libs/offline.min.js'),
materialize: path.join(srcPath, 'libs/materialize.js'),
css3_animate_it: path.join(srcPath, 'libs/css3-animate-it.js'),
app: path.join(srcPath, 'App.js')
},
resolve: {
root: srcPath,
extensions: ['', '.js'],
modulesDirectories: ['node_modules']
},
output: {
path: path.join(__dirname, 'build'),
publicPath: '',
filename: jsDestPath + '[name].js',
library: ['Example', '[name]'],
pathInfo: true
},
module: {
loaders: [
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel?cacheDirectory'
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
/*{
test: /\.(png|woff|woff2|eot|ttf|svg|jpg)$/,
loader: 'url-loader?limit=5' // inline base64 URLs for <=8k images, direct URLs for the rest
},*/
{ test: /\.(png|woff|woff2|eot|ttf|svg|jpg)$/, loader: 'url-loader?limit=1&name=images/[name].[ext]' },
{
test: /\.rt/,
loader: "react-templates-loader"
}
],
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
new webpack.optimize.CommonsChunkPlugin('common', jsDestPath + 'common.js'),
new webpack.optimize.UglifyJsPlugin({minimize: true}),
new HtmlWebpackPlugin({
inject: true,
template: 'src/index.html'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8080',
browser: 'Chrome'
}),
new webpack.NoErrorsPlugin(),
new ExtractTextPlugin("styles/style.css", {
allChunks: true
})
],
debug: true,
//devtool: 'eval-source-map',//'eval-cheap-module-source-map',
devServer: {
contentBase: './build',
historyApiFallback: true
}
};
Remove publicPath: '', or set it to publicPath: '/', and require images
like this let imgSrc = require('../../img/image.png'); via relative paths(from where you are trying to require images).
It should get resolved as {publicPath}/images/[name].[ext]
I hope it should work.
You need to prefix your image URL with ~, otherwise it will be treated as relative to the CSS path. The ~ triggers webpack's normal module resolution. More info in the CSS loader docs here.

Resources