How to serve my React webapp on static server after using Webpack? - javascript

I have a React Redux app and am trying to put it on a static file server (so the user should just be able to go to http://myurl.com/thePath/index.html and the app should load). Right now I'm using webpack to bundle my assets. Here is my webpack build file:
webpack.config.prod.js:
import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import WebpackMd5Hash from 'webpack-md5-hash';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import autoprefixer from 'autoprefixer';
import path from 'path';
const GLOBALS = {
'process.env.NODE_ENV': JSON.stringify('production'),
__DEV__: false
};
export default {
resolve: {
extensions: ['', '.js', '.jsx']
},
debug: true,
devtool: 'source-map', // more info:https://webpack.github.io/docs/build-performance.html#sourcemaps and https://webpack.github.io/docs/configuration.html#devtool
noInfo: true, // set to false to see a list of every file being bundled.
entry: path.resolve(__dirname, 'src/index'),
target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/thePath/',
filename: '[name].[chunkhash].js'
},
plugins: [
// Hash the files using MD5 so that their names change when the content changes.
new WebpackMd5Hash(),
// Optimize the order that items are bundled. This assures the hash is deterministic.
new webpack.optimize.OccurenceOrderPlugin(),
// Tells React to build in prod mode. https://facebook.github.io/react/downloads.html
new webpack.DefinePlugin(GLOBALS),
// Generate an external css file with a hash in the filename
new ExtractTextPlugin('[name].[contenthash].css'),
// Generate HTML file that contains references to generated bundles. See here for how this works: https://github.com/ampedandwired/html-webpack-plugin#basic-usage
new HtmlWebpackPlugin({
template: 'src/index.ejs',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
},
inject: true,
// Note that you can add custom options here if you need to handle other custom logic in index.html
// To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below.
trackJSToken: ''
}),
// Eliminate duplicate packages when generating bundle
new webpack.optimize.DedupePlugin(),
// Minify JS
new webpack.optimize.UglifyJsPlugin()
],
module: {
loaders: [
{test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel'},
{test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/, loader: "file-loader" },
{test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'},
{test: /\.ico$/, loader: 'file?name=[name].[ext]'},
{test: /(\.css|\.scss)$/, loader: ExtractTextPlugin.extract('css?sourceMap!postcss!sass?sourceMap')}
]
},
postcss: ()=> [autoprefixer]
};
So webpack bundles everything fine, but when I put the files it outputs into /thePath/ and then go to http://myurl.com/thePath/index.html, I get an empty white screen. It loads the CSS and JS resources but nothing shows up. It loads fine when it is served through webpack's dev server. How can I resolve this?

Answer can be found here: Why is React Webpack production build showing Blank page?
Long story short, when using browserHistory with react-router, your server must be configured to support it. If your server is not configured to support it, you must use hashHistory.

Related

webpack-dev-server not recompiling (JS files AND SCSS files)

Good morning,
Rise and shine, the sun is already high in the sky and webpack is ruining my day!
I'm using webpack-dev-server (through a script in packages.json):
"scripts": {
"dev-server": "webpack-dev-server",
}
That I run with yarn run dev-server
What I want is the code to recompile and the browser to refresh whenever I save a file. I can live with the fact that it doesn't work with SCSS files, but recompiling "manually" on each change in my components is just physically painful. I tried a lot of solution found online (non-exhaustive list coming) before asking here, but the result is always the same:
ℹ 「wdm」: Compiled successfully
And nothing happens when I modify a file (JS or SCSS).
This is a simple React app, with SCSS for styling.
Here is my webpack config:
const path = require('path');
const MiniCSSExtractPlugin = require('mini-css-extract-plugin');
const mode = process.env.NODE_ENV || 'development';
module.exports = env => {
return {
entry: ['babel-polyfill', './src/app.js'],
output: {
path: path.join(__dirname, 'public', 'dist'),
filename: 'bundle.js'
},
module: {
rules: [{
loader: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/
}, {
test: /\.s?css$/,
loader: [
mode === 'development' ? 'style-loader' : MiniCSSExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
},
plugins: [
new MiniCSSExtractPlugin({ filename: 'styles.css' })
],
devtool: env === 'production' ? 'source-map' : 'cheap-module-eval-source-map',
devServer: {
contentBase: path.join(__dirname, 'public'),
publicPath: '/dist/'
}
};
};
Here a list of things I tried:
Add --output-public-path=/dist/ to the script
Use the following content to the devServer config in webpack.config.js:
host: '0.0.0.0',
contentBase: path.join(__dirname, 'public'),
publicPath: '/dist/',
historyApiFallback: true,
compress: true,
port: 8080,
watchContentBase: true,
inline: true,
hot: true
Use HtmlWebpackConfig with the following config:
new HtmlWebpackPlugin({
title: 'Prognostic',
filename: './public/dist/index.html',
template: './public/index.html'
})
Remove / add webpack and webpack-dev-server
Use a global webpack-dev-server instead of the project one (npm i -g webpack-dev-server)
Certainly more things but I don't remember... Whoops
For information, here are the version I use:
Babel-loader#7
react#^16.8.6,
webpack-dev-server#^3.9.0
webpack#^4.41.2
So, I'd like two things to happen:
Automatic recompile when JS file changed
Automatic recompile when SCSS file changed (if possible)
If you can help me do that, I'll nominate you my Santa Dev of the year (yes, you can add that to your CV)
Thank you!
PS: great laugh when Grammarly told me that my text sounds "friendly"
Webpack dev server adds a watcher on your files to trigger the compilation when they have been modified.
Sometimes though, depending on the text editor you are using, this won't trigger at all.
I had the same problem, using sublimetext : when I saved my code the webpack dev server wouldn't rebuild.
So instead of using the default triggering mechanism, I'm using another option of webpack :
devServer: {
hot: true,
watchOptions: {
aggregateTimeout: 300,
poll: true
},
}
Every 300ms the server will check if files have changed and if so, rebuild.
I hope I am your Santa Dev of the year :]
I don't think you can do this by webpack you need to use library like react-hot-loader

Sourcemap to original SCSS file

I'm using Webpack 4 with the plugins MiniCssExtractPlugin and OptimizeCSSAssetsPlugin to extract SCSS files and minify the main.css file in production. I have source mapping enabled in my config file like so:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
devtool: "source-map",
entry: {
style: './src/scss/main.scss'
},
output: {
path: path.resolve(__dirname, "dist"),
filename: '[name].bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } }
]
},
{
test: /\.scss$/,
use: [
'style-loader',
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false, sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
sourceMap: true,
filename: "main.css"
}),
new UglifyJsPlugin({
sourceMap: true,
test: /\.js($|\?)/i,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
map: {
inline: true
}
}
}) ,
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
In the console, it does give the line where a CSS rules originates from, but in the bundled main.css file, rather than invididual scss files i.e. _layout.scss, _buttons.scss.
Question
What configuration do I add in order to display where the CSS rule originates from, rather then where it is located in the bundled main.css file?
As i can see on your sass-loader configuration, you're already getting the source maps for yous scss files (to verify that you need to have a file with the same name as you css, but with a .map on its name), so the only thing you will need is to peroperly configure your browser to load and use the source maps. This example uses Google Chrome:
Open dev tools
Click the gear icon or three dots to open settings (top right corner)
Under General (or Preferences), look for the “Sources” section. In that section, select “Enable CSS source maps”.
Make sure the accompanying “Auto-reload generated CSS” is also enabled (if available). This last step helps to reload the CSS when it changes. Think of it like a live reload feature for the CSS alone.
Navigate to your localhost server, inspect any element on your page.
In the developer tools, choose the Sources tab.
In the file tree on the left hand side, right-click your stylesheet and select “Map to file system resource…”. This will bring up the file search dialog. Select the appropriate file (your stylesheet). In some newer versions of Chrome, the file should be loaded automatically using the /*# sourceMappingURL=style.css.map */ reference
Restart the developer tools.
That should work. Let me know. Otherwise i will fine tune the quick tutorial.

react-dom blowing out webpack bundle size MASSIVELY

This has got to be one of the strangest issues with webpack i have ever come across...
Check out this bundle breakdown:
react 116.01KB - fair enough
react-dom 533.24KB - seriously WTF
I thought it may be a corruption in my dependencies but nuking node_modules and reinstalling doesn't have any effect. I guess it's something to do with the way webpack is bundling it but i'm lost for ideas. The way i'm handing .js imports is pretty stock standard.
// webpack.config.js
const path = require('path');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const Dashboard = require('webpack-dashboard');
const DashboardPlugin = require('webpack-dashboard/plugin');
const dashboard = new Dashboard();
module.exports = {
context: path.join(__dirname, 'src'),
entry: {
bundle: './index.js',
},
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'build'),
},
module: {
rules: [
{
test: /\.html$/,
use: 'file-loader?name=[name].[ext]',
},
{
test: /.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader',
],
}),
},
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader',
},
],
},
plugins: [
// new BundleAnalyzerPlugin(),
new ExtractTextPlugin('styles.css'),
new DashboardPlugin(dashboard.setData),
],
devServer: {
quiet: true,
},
};
// .babelrc
{
"presets": [
"react",
"es2015"
],
"plugins": ["transform-object-rest-spread"]
}
http://elijahmanor.com/react-file-size/
In v15.4.0 the file size of react-dom grew from 1.17kB to 619.05kB. Which means my webpack setup isn't doing anything wrong bundling files. The reason why this module grew so large is because code was transferred from the react module.
I had to change my webpack.config.js, from
devtool: 'inline-source-map'
to
devtool: 'source-map'
Now it generates a much smaller .js + a separate .js.map file, for each of the chunks.
Notice the JS size is even less than react-dom.production.min.js in node_modules:
If you look into the corresponding folders under the node_modules folder, and note the file sizes, you'll see that there's nothing to be surprised about:
That is, the size of the bundle grows noticeably because the size of react-dom.js is large.
Add this following commands at plugins to minify your imports:
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.DefinePlugin(GLOBALS),
new webpack.optimize.UglifyJsPlugin(),
You should create a file or option to production bundle to use this plugins

optimize image inside desired folder

I am using webpack for bundling. I am using reactjs and django. I want the static files used by Django and reactjs be separate. I could minified image but the minified images are saved to the folder where the output file is bundled. I want all the minified images to be saved inside frontend -> assets folder. How can i do it so?
The project structure looks like following
app - its a directory where static files are kept for Django. Webpack bundles the react files to app.js and is placed over here because Django template need it to render in its template as <script src='app/bundle/js/app.js'></script>.
frontend - It's a directory where all the react files reside. I want the images to be inside this directory(assets/images/). Images that will be used in reactjs.
How can i do it so?
my webpack right now is configured this way
const path = require("path");
if(!process.env.NODE_ENV) {
process.env.NODE_ENV = 'development';
}
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: path.join("../app/static/build/", "js"),
filename: "app.js",
publicPath: "../app/static/build/"
},
devtoo: 'source-map',
debug: true,
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-1']
}
},
{test: /\.(jpe?g|png|gif|svg)$/i, loader: "file-loader?name=images/[name].[ext]"},
]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
You can specify custom output and public paths by using the outputPath and publicPath query name parameters:
loader: "file-loader?name=[name].[ext]&publicPath=assets/foo/&outputPath=app/images/"
But this feature isn't published to NPM yet. So unfortunatly you'll need to wait while it be published or clone and use this loader from github repo

How do you extract css imports from submodules with webpack?

I'm trying to create a React application with multiple entries using webpack and extract-text-webpack-plugin.
My config file looks like this,
const commonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const extractTextPlugin = require('extract-text-webpack-plugin');
let config = {
entry: {
app: './client/app.entry.js',
signIn: './client/sign-in.entry.js',
},
output: {
path: './server/public',
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015']
}
},
{
test: /\.css$/,
loader: extractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
}
]
},
resolve: {
modulesDirectories: ['node_modules', 'client'],
extensions: ['', '.js']
},
plugins: [
new commonsChunkPlugin('common', 'common.js'),
new extractTextPlugin('styles.css', { allChunks: true })
]
};
module.exports = config;
My problem is that extract-text-webpack-plugin only includes imported css files from the entry chunks, and not from submodules of the entry chunks.
So if app.entry.js has
import "./app-style.css";
import "./sub-module"; // This module has import "./sub-style.css";
then the styles from app-style.css gets bundled but not the styles from sub-style.css.
I haven't had this issue before when there's only been one entry file, so I'm wondering if having multiple entries requires another setup?
Something to also take into consideration is the use of CSSModules by the way the css-loader is used, which also could be a factor.
Any ideas?
I'm trying to solve similar problem, and i think it will be nice idea to document the solution and thoughts for those who has the same questions.
TextExtract plugin can work with chunks that have to be configured with commonchunks plugin, enable chunks support:
// Configuration of the extract plugin with chunks and naming
new ExtractTextPlugin("[name].css", { allChunks: true })
It's all ) Next thing is just configuration of the chunks (webpack is very flexible tool, everyone configure it for own needs. For an instance i'll show how i configure "vendour.css" and "application.css" build configuration based on "imports")
// Vendour chunks definition for vendor css
entry: {
vendor : ['./css/vendour.sass']
Example of entrypoint file.js
import "./css/vendor.sass"
import "./css/application.sass"
After build, webpack will create vendor.css (where you export vendour things with #import "~vendormodules/sass/alla") and application.css files.
Thanks,

Categories