Create React App: How to use historyApiFallback: true in production - create-react-app

From what I understand if you were to create a react app without a starter kit e.g create-react-app, one of things you have full control of is webpack.
This is comes in particularly handy when you need to solve the problem when your app hits a route before the app is fully loaded.
https://your-app.com/login
You'll get an error because your routes haven't been created.
So you do something like this:
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
var webpack = require('webpack');
var config = {
output: {
publicPath: '/',
}
devServer: {
historyApiFallback: true,
}
mode: 'development',
};
if (process.env.NODE_ENV === 'production') {
config.plugins.push(
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
new UglifyJSPlugin()
);
}
module.exports = config;
Specifically:
devServer: {
historyApiFallback: true,
}
So I found this enter link description here based on this related question
Any ideas, if there is package or TUT to solve this problem?

Related

Avoid browser caching after deploy a Vuejs app

My Vuejs App did not update after deployment for production, every time require "Empty cache and hard reload" to get the updates, I tried a lot of solutions to apply versioning to generated files after build but none of them worked for me, I need a solution to apply new hash for all files after every single build, not just the updated ones.
My vue.config.js file content:
const path = require("path");
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
runtimeCompiler: true,
configureWebpack: {
resolve: {
alias: {
// If using the runtime only build
// vue$: "vue/dist/vue.runtime.esm.js" // 'vue/dist/vue.runtime.common.js' for webpack 1
// Or if using full build of Vue (runtime + compiler)
vue$: 'vue/dist/vue.esm.js', // 'vue/dist/vue.common.js' for webpack 1
'#': path.resolve('src'),
src: path.resolve('src'),
assets: path.resolve('src/assets'),
components: path.resolve('src/components'),
services: path.resolve('src/services'),
}
},
output: {
filename: '[name].[hash].js',
},
},
chainWebpack: config => {
config.module
.rule("eslint")
.use("eslint-loader")
.tap(options => {
options.configFile = path.resolve(__dirname, ".eslintrc.js");
return options;
});
},
};
Thanks in advance.
Welcome to the Vue JS cache nightmare. Did you try changing the version value in your package.json? I use to increment the value on each release as per x.y.z semantinc versioning. Maybe doing something like this:
{
"name": "My app",
"version": "1.0.15",
"private": true,
...
}

Uncaught ReferenceError: webpackJsonp in Vue Js

I have suddenly started hitting Uncaught ReferenceError: webpackJsonp in Vue Js. I am fairly new to Js and have just started with Vue applications. I tried the various solutions that were mentioned in the Git and stackoverflow but they have not worked. Can someone help me out.
To give some context, the issue suddenly appeared without having made much of a change. The code is working when it was in the state earlier but making any change in the components even modifying the css has started causing the issue.
Based on what I read about the issue, it is supposed to be controlled by the webpack.prod.conf.js (Attached mine below). Incase any other info is needed please let me know.
"use strict";
const path = require("path");
const utils = require("./utils");
const webpack = require("webpack");
const config = require("../config");
const merge = require("webpack-merge");
const baseWebpackConfig = require("./webpack.base.conf");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const env = require("../config/prod.env");
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath("js/[name].[chunkhash].js"),
chunkFilename: utils.assetsPath("js/[id].[chunkhash].js")
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
"process.env": env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath("css/[name].[contenthash].css"),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap ?
{ safe: true, map: { inline: false } } :
{ safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: "index.html",
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: "dependency"
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
async: false,
minChunks(module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, "../node_modules")) === 0
);
// return module.context && module.context.includes("node_modules");
}
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: "app",
async: "vendor-async",
children: true,
minChunks: 3
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
async: false,
minChunks: Infinity
}),
// copy custom static assets
new CopyWebpackPlugin([{
from: path.resolve(__dirname, "../static"),
to: config.build.assetsSubDirectory,
ignore: [".*"]
}])
]
});
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require("compression-webpack-plugin");
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp(
"\\.(" + config.build.productionGzipExtensions.join("|") + ")$"
),
threshold: 10240,
minRatio: 0.8
})
);
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
module.exports = webpackConfig;
The entry points are missing in the configuration, i.e. you are not informing webpack from where the application should start.
Also this issue is happening because you have mentioned vendor
please refer this link

How to debug babel.config.js

I've noticed that there are virtually no info from babel on incorrect configuration. For example I've created new app with react-native-cli, installed decorators plugin and filled my babel.config.js as follows:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['#babel/plugin-proposal-decorators', { legacy: true }],
};
And there were the same complain as if no plugin is installed. Correct config would be:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [['#babel/plugin-proposal-decorators', { legacy: true }]],
};
Now I'm trying to install jsx-control-statements and have the same silent fail causing
ReferenceError: Can't find variable: Choose as if no such plugin is installed at all. My babel.config.js is:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'jsx-control-statements',
['#babel/plugin-proposal-decorators', { legacy: true }],
],
};
So the question is: How do I debug this configuration? How can I get some diagnostic from babel about incorrect configuration/not found packages etc.?
For instance if the presets/plugins are missing or missconfigured, you'll get an error when webpack takes over and try to load all the config. But I think your best shot is to use progressPlugin where you could display each step and see for yourself what is happening.
new webpack.ProgressPlugin((percentage, message, ...args) => {
console.log(percentage, message, ...args);
}),
Another approach, would be using debug module, as nearly all other plugins, modules use it.
DEBUG=* webpack [-c webpack.something.js]
Hope it helps
If you use babel.config.js you could do the following:
module.exports = function(api) {
if (api) {
api.cache(true);
api.debug = process.env.NODE_ENV === 'development' || false;
}
const presets = ['#babel/preset-env'];
const plugins = [
'#babel/plugin-proposal-class-properties',
...
];
return {
presets,
plugins,
};
};

How do you enable source maps in Webpack?

I want to enable source maps in my webpack.config.js. I'm adding onto some opensource and their webpack.config.js looks weird.
webpack.config.js
// Entry point webpack config that delegates to different environments depending on the --env passed in.
module.exports = function(env) {
process.env.NODE_ENV = env;
return require(`./webpack.${env}.js`);
};
Here is what it returns if env = development
/**
* Webpack configuration for development.
*
* Contains plugins and settings that make development a better experience.
*/
const webpack = require("webpack");
const merge = require("webpack-merge");
const fs = require("fs");
const { execSync } = require("child_process");
const path = require("path");
const argv = require("yargs").argv;
const commonConfig = require("./webpack.common.js");
const DashboardPlugin = require("webpack-dashboard/plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
if (!fs.existsSync(path.resolve(__dirname, "vendor", "vendor-manifest.json"))) {
// This _should_ exist, since we run the command for you when you run `npm run dev`
console.log(
"Vendor files not found. Running 'npm run build:dev-dll' for you..."
);
execSync("npm run build:dev-dll");
console.log("Done generating vendor files.");
}
const devConfig = {
mode: "development",
main: {
devtool: "source-map",
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify("development")
}),
new webpack.DllReferencePlugin({
context: ".",
manifest: require("./vendor/vendor-manifest.json")
}),
new ForkTsCheckerWebpackPlugin({
tslint: true,
async: false,
silent: true
})
]
}
};
// Only enable the dashboard plugin if --watch is specified
// The dashboard plugin annoyingly doesn't allow webpack to exit,
// so we only enable it with --watch, which doesn't exit anyways
if (argv.watch) {
devConfig.main.plugins.push(new DashboardPlugin());
}
module.exports = merge.multiple(commonConfig, devConfig);
I don't know if source map should be added to webpack.config.js or maybe just the development version and I don't know how to add it cause these config files look odd to me.
The line... "devtool": "source-map" is correct, but it appears to be at the wrong depth.
Should be:
const devConfig = {
mode: "development",
devtool: "source-map",
main: {
...
}
};

Correctly configure webpack-dev-middleware with vuejs project

I'm new to webpack so it's probably a stupid mistake on my part.
This is my project setup (root, atleast the relevant part):
+-- public
| |
| +-- index.html
|
+-- src
| |
| +-- App.vue
| +-- main.js
| +-- assets
|
+-- package.json
|
+-- webpack.config.js
Now I would like to use the webpack-dev(and hot)-middleware to serve my index.html and create a bundle in memory from my src folder. Now I can get the middleware set up (via the npm page) and I see the bundle gets created (through logging in console) but two things are not clear to me:
How to serve index.html
How to use the bundle thats created in memory?
Can somebody please explain how that middleware works exactly? This is my webpack config file (which is needed to plug into the middleware, it's just a copy of the webpack config file that gets created through the vue-cli):
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
I know this is probably not the correct configuration, can someone maybe point out some things?
Thanks in advance for your help!
Edit (this setup works):
My server.js now:
var express = require('express');
var logger = require('morgan');
var bodyParser = require('body-parser');
var exphbs = require('express-handlebars');
var helmet = require('helmet');
var redis = require('redis');
var redisAdapter = require('socket.io-redis');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
// use the redis adapter to create sticky sessions, this is needed because of the different clusters
io.adapter(redisAdapter( require('./app/lib/config').credentials.redis.url ));
//setup security ===============================================================
require('./app/lib/security-setup')(app, helmet);
// configuration ===============================================================
app.use(logger('dev')); // log every request to the console
// set up our express application ==============================================
// Make the body object available on the request
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//set handlebars as default templating engine
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// serve the static content ====================================================
if (app.settings.env === 'development') {
var webpackConfig = require('./webpack.config.js')
var compiler = require('webpack')(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
})
var hotMiddleware = require('webpack-hot-middleware')(compiler)
app.use(devMiddleware)
app.use(hotMiddleware)
} else {
app.use(express.static(__dirname + '/public'));
}
// set up global variables =====================================================
app.use(function(req, res, next) {
//set the io object on the response, so we can access it in our routes
res.io = io;
next();
});
// routes ======================================================================
require('./app/routes.js')(app); // load our routes and pass in our app
// export so bin/www can launch ================================================
module.exports = {app: app, server: server};
my ./bin/www:
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../server').app;
var cluster = require('cluster');
var debug = require('debug')('temp:server');
var http = require('http');
var numCPUs = process.env.WORKERS || require('os').cpus().length;
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
// If a worker dies, log it to the console and start another worker.
cluster.on('exit', function(worker, code, signal) {
console.log('Worker ' + worker.process.pid + ' died.');
cluster.fork();
});
// Log when a worker starts listening
cluster.on('listening', function(worker, address) {
console.log('Worker started with PID ' + worker.process.pid + '.');
});
} else {
/**
* Create HTTP server.
*/
var server = require('../server').server;
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
}
// The rest of the bin/www file.....
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Listening on ' + bind);
}
My working webpack.config.js:
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: [
'webpack/hot/dev-server',
'webpack-hot-middleware/client',
'./src/main.js'
],
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// vue-loader options go here
}
},
{
test: /\.less$/,
loader: "style!css!less"
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
devtool: '#eval-source-map',
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, 'public/index.html'),
inject: true
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
How to serve index.html
To serve the index.html file you need to have a dev server running. It sounds like you are successfully doing this based on your logging of the creation of your bundle in memory. However I can't see a file for it in your set up? I've assumed another file below called: dev-server.js and it would be your entry point to serving your app, i.e. npm run dev:
package.json
"scripts": {
"dev": "node dev-server.js",
...
In webpack this dev server is generally going to be express and it's the config that you pass to your express server that will serve your index.html. As you are wanting hot-reloading you will pass express your webpack config through your middleware layers.
For hot reloading you'll need two main middlewares:
webpack-dev-middleware
webpack-hot-middleware
Then you will need to pass your config to webpack and pass your webpack compiler to the middleware, i.e.
dev-server.js
var app = require('express')()
var webpackConfig = require('./webpack.config.js')
var compiler = require('webpack')(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
})
var hotMiddleware = require('webpack-hot-middleware')(compiler)
app.use(devMiddleware)
app.use(hotMiddleware)
Now the key file becomes your webpack config, referenced above as: ./webpack.config.js
This leads to your next question: How to use the bundle thats created in memory?
The file you posted above looks to be about right and the key parts in regards to using the bundle are held within the output, you have:
webpack.config.js
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
You are creating the bundle at dist/build.js relative to the current working directory. This is essentially where you need to point any references to that file within your index.html, i.e. <script src="/dist/build.js"></script>
How you do this can be manual however we'd often add a further webpack plugin to automatically build this script tag within your output html (again in this case in memory):
webpack.config.js
plugins: [
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, 'public/index.html'),
inject: true
}),
...
The HtmlWebpackPlugin is now essentially how you reference what the output file is called: filename and crucially where it is stored: template so if you wanted to move your index.html file this is where you tell webpack where to find it. The HtmlWebpackPlugin will now place the output 'index.html' at the publicPath referenced earlier, so to get to this file you would call /dist/index.html.
Finally you'll need some further plugins for hot reloading so your entire webpack plugins array will look like:
webpack.config.js
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'bar/index.html',
inject: true
})
]
Now we return to the dev-server.js file - or whatever yours is called that you are configuring express within - and fire up the configured express server:
dev-server.js
module.exports = app.listen(8080, function (err) {
if (err) {
console.log(err)
return
}
})
so with the above config you'd open the following uri: localhost:8080/dist/index.html