Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mistercoffee66/7a44837abef5317c5a87508ec3d39662 to your computer and use it in GitHub Desktop.
Save mistercoffee66/7a44837abef5317c5a87508ec3d39662 to your computer and use it in GitHub Desktop.
Webpack 4 + Express + Hot module reloading without middleware
//*********server.js*********
const express = require('express')
const path = require('path')
const app = express()
const port = process.env.PORT || 3000
app.use(express.static(path.join(__dirname, 'public/build')))
app.use('/api', someApiRoutes)
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/build/index.html'));
});
app.listen(port, () => {
console.log(`Server Started at port ${port}`);
});
//*********webpack.config.js*********
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
//const ExtractTextPlugin = require('extract-text-webpack-plugin')
const autoprefixer = require('autoprefixer')
const chalk = require('chalk')
let config
let setPlugins
let setStyleRules
let isDev
let publicPath
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
isDev = process.env.NODE_ENV !== 'production'
publicPath = '/'
console.log('environment: ' + chalk.bgGreen(` ${process.env.NODE_ENV} `) + '\n')
//plugins assigned to a variable so dev and prod can each have their own plugins
setPlugins = () => {
if (!isDev) { //prod
return [
new webpack.DefinePlugin({
'process.env': { NODE_ENV: JSON.stringify('production') }
}),
new HtmlWebpackPlugin({
template: './app/index.tpl.html',
inject: 'head',
filename: 'index.html'
}),
new MiniCssExtractPlugin({
filename: path.join('css','[name].css')
}),
//new webpack.optimize.CommonsChunkPlugin({
// name: 'vendor',
// minChunks: function (module) {
// return module.context && (module.context.indexOf('node_modules') !== -1 || module.context.indexOf('_lib') !== -1);
// }
//})
]
}
else { //dev
return [
new HtmlWebpackPlugin({
template: './app/index.tpl.html',
inject: 'head',
filename: 'index.html'
})
]
}
}
// css/less rules object assigned to a variable so we can use it in both dev and prod flow
setStyleRules = () => {
let loaders = isDev ? ['style-loader'] : [MiniCssExtractPlugin.loader]
loaders.push(
{
loader: 'css-loader',
options: {
sourceMap: isDev,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: isDev,
},
},
{
loader: 'postcss-loader',
options: {
plugins: [autoprefixer],
minimize: !isDev
},
}
)
return loaders
}
//main config
config = {
mode: isDev ? 'development' : 'production',
entry: {
app: ['babel-polyfill', path.join(__dirname, 'app', 'index.jsx')]
},
resolve: {
extensions: ['.js', '.jsx']
},
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all',
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2
},
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
module: {
rules: [
//*** js
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
//*** styles
{
test: /\.s?css$/,
use: setStyleRules()
},
//*** bundled images
{
test: /\.(png|jpg|svg)$/,
use: {
loader: 'file-loader',
options: {
//outputPath: '',
name: 'img/[name].[ext]'
}
}
}
]
},
plugins: setPlugins(),
output: {
path: path.join(__dirname, 'public'),
filename: path.join('js', '[name].js'),
publicPath: publicPath
},
devServer: {
historyApiFallback: true,
publicPath: publicPath,
hot: true,
proxy: {
'/api': {
target: 'http://localhost:3000'
}
}
},
devtool: isDev ? 'eval-sourcemap' : false
}
module.exports = config
*********package.json*********
{
"name": "fullstack-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "export NODE_ENV=development && npm run devclient",
"build": "export NODE_ENV=production && rm -rf ./build && npm run client && npm run server",
"devserver": "nodemon server.js",
"devclient": "webpack-dev-server --open --hot",
"server": "node server.js",
"client": "webpack -p",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "samelwitt@gmail.com",
"license": "ISC",
"dependencies": {
"autoprefixer": "^8.6.3",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"body-parser": "^1.18.3",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^2.1.1",
"dotenv": "^6.0.0",
"express": "^4.16.3",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.4.0",
"mongodb": "^3.1.8",
"node-sass": "^4.12.0",
"postcss-loader": "^2.1.5",
"prop-types": "^15.6.2",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-hot-loader": "^4.3.3",
"react-json-view": "latest",
"react-router-dom": "^4.3.1",
"sass-loader": "^7.1.0",
"uuid": "^3.2.1",
"webpack": "^4.12.0",
"webpack-cli": "^3.0.8"
},
"devDependencies": {
"style-loader": "^0.21.0",
"webpack-dev-server": "^3.3.1"
}
}
*********babel.rc*********
...
"plugins": [
"react-hot-loader/babel"
]
...
*********components/App.jsx*********
import React from 'react'
import { hot } from 'react-hot-loader'
class App extends React.Component {
state = {}
// lifecycle
componentWillMount () {
//
}
// main render method
render () {
return (<div>App!</div>)
}
}
export default hot(module)(App)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment