Last active
May 9, 2019 18:02
-
-
Save mistercoffee66/7a44837abef5317c5a87508ec3d39662 to your computer and use it in GitHub Desktop.
Webpack 4 + Express + Hot module reloading without middleware
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//*********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": "[email protected]", | |
"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