How to get webpack hot reload to detect changes in pug + express? - javascript

I have an Express app with pug and stylus. I've configured the HMR middleware and it reloads on stylus changes but not for pug changes.
I'm wondering if I'm missing a specific configuration. I also tried adding the pug-html-loader but that didn't work either.
// server.js
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
const webpackDevMiddleware = require('./hmr').dev;
const webpackHotMiddleware = require('./hmr').hot;
app.use(webpackDevMiddleware);
app.use(webpackHotMiddleware);
// hmr.js
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpack = require('webpack');
const webpackConfig = require('./webpack.config.js');
const compiler = webpack(webpackConfig);
exports.dev = webpackDevMiddleware(compiler, {
noInfo: true,
filename: webpackConfig.output.filename,
publicPath: webpackConfig.output.publicPath,
stats: {
colors: true
}
});
exports.hot = webpackHotMiddleware(compiler, {
log: console.log,
path: '/__webpack_hmr',
heartbeat: 10000
});
// webpack.config.js
const javascriptRule = {
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['env']
}
}
]
};
const stylesRule = {
test: /\.styl$/,
use: ['style-loader', 'css-loader', 'stylus-loader']
};
module.exports = {
context: path.join(__dirname, 'javascripts'),
entry: [
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000',
'./index.js'
],
devtool: 'source-map',
output: {
path: path.join(__dirname, 'public', 'dist'),
filename: 'bundle.js',
publicPath: '/dist/',
library: 'aux'
},
module: {
rules: [javascriptRule, stylesRule]
},
plugins: [new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin()]
}

You need install raw-loader: https://webpack.js.org/loaders/raw-loader/
Webpack 3 config:
module: {
rules: [
{
test: /\.pug$/,
use: [
{loader: 'raw-loader'},
{loader: 'pug-html-loader'}
]
}
]
},
plugins: [
// Templates HTML
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/templates/index.pug'
}),
new HtmlWebpackPlugin({
filename: 'contact.html',
template: './src/templates/contact.pug'
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
app.js
// import all template pug
import 'raw-loader!./templates/index.pug'
import 'raw-loader!./templates/contact.pug'
...
That makes webpack listen the changes in the pug files, but it also adds this js code to the bundle.js, then you need to process app.js to clean the bundle.js.

Related

Remove empty bundle file from the HTML template

I have a simple Webpack configuration to compile a static HTML file with the Bootstrap's CSS asset:
const path = require('path');
const glob = require('glob')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PurgeCssPlugin = require('purgecss-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const mode = process.env.NODE_ENV || 'development';
const isProduction = 'production' === mode;
console.log(`Building mode: ${mode}`);
module.exports = {
mode: mode,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, '..'),
filename: isProduction ? 'bundle.[contenthash].js' : 'bundle.js',
clean: false,
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
inject: 'body',
template: 'index.ejs',
minify: isProduction,
}),
new MiniCssExtractPlugin({
filename: isProduction ? 'style.[contenthash].css' : 'style.css',
}),
new PurgeCssPlugin({
paths: glob.sync(path.join(__dirname, '..') + '/*', {nodir: true}),
}),
],
};
The src/index.js file is as simple as:
import "./scss/main.scss"
This configuration works really well, however since there're no JS files included, the generated bundle.js file is empty though it's still injected to the HTML template:
<script defer src="bundle.js"></script>
How can I remove this file completely from the template if it's empty?

angular2 bundle node_modules only and not application code

Angular v.4 and webpack
i am looking for answer of very simple question.
How can i bundle node_module folder as vendor.js but not your application code. on page load seems like there are more then 300 requests from angular code to load internal files.
if i am able to merge all node_modules libraries i will save lot of browser requests.
I followed this example was able to get it run but it bundles libraries and your application code as well. i was able to successfully generate the vendor.js only but i am not sure after i add vendor.js in index.html how my application code will recognize #angular lib references. currently it is being resolved using systemjs.config.js some thing like this
#angular/code : 'npm:#angular/code/bundle/core.umd.js'
..
..
and exactly thats what happen as i thought application crashed it can't find references to #angular from my application code.
is it possible to just bundle node_module folder into vendor.js and still able to use in my application code without bundling app code ?
adding webpack config
webpack.config.js
module.exports = require('./config/webpack.prod.js');
webpack.common.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');
module.exports = {
entry: {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'app': './src/main.ts'
},
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
loaders: [
{
loader: 'awesome-typescript-loader',
options: { configFileName: helpers.root('src', 'tsconfig.json') }
} , 'angular2-template-loader'
]
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=assets/[name].[hash].[ext]'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw-loader'
}
]
},
plugins: [
// Workaround for angular/angular#11580
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
helpers.root('./src'), // location of your src
{} // a map of your routes
),
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: 'src/index.html'
})
]
};
webpack.prod.js
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',
output: {
path: helpers.root('dist'),
publicPath: '/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
mangle: {
keep_fnames: true
}
}),
new ExtractTextPlugin('[name].[hash].css'),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
}),
new webpack.LoaderOptionsPlugin({
htmlLoader: {
minimize: false // workaround for ng2
}
})
]
});

How to Hot Reload Sass Using Webpack 2?

I'm working on setting up a React application that uses Webpack2, webpack-dev-middleware and HMR for development. Whenever I make a change on a React component, it updates in the browser as intended. The issue I am running into is that when I modify my .scss files, the browser does not update. What happens instead, is that in the console it gives me the following:
[HMR] bundle rebuilding
client.js:207 [HMR] bundle rebuilt in 1567ms
process-update.js:27 [HMR] Checking for updates on the server...
process-update.js:98 [HMR] Nothing hot updated.
process-update.js:107 [HMR] App is up to date.
After this, when I refresh the page, my style changes appear. I'm not entirely sure what's going on or where the issue stems from but would like some help and clarification.Below is my setup:
Webpack.config.js
var webpack = require('webpack');
var path = require('path');
var autoprefixer = require('autoprefixer');
var DashboardPlugin = require('webpack-dashboard/plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var argv = require('yargs').argv;
const config = {};
// This configured production
if (argv.p) {
config.entry = [
'./src/client/scripts/index',
'./src/client/scripts/utils/index',
'./src/client/styles/index.scss'
]
config.plugins = [
new DashboardPlugin(),
new ExtractTextPlugin({
filename: 'bundle.css',
allChunks: true
}),
]
}
else {
config.entry = [
'react-hot-loader/patch',
'webpack-hot-middleware/client',
'./src/client/scripts/index',
'./src/client/scripts/utils/index',
'./src/client/styles/index.scss'
]
config.plugins = [
new DashboardPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.NamedModulesPlugin(),
new ExtractTextPlugin({
filename: 'bundle.css',
allChunks: true
})
]
}
module.exports = {
entry: config.entry,
output: {
path: path.join(__dirname, 'src', 'client', 'static'),
filename: 'bundle.js',
publicPath: '/static/'
},
devtool: 'inline-source-map',
devServer: {
hot: true,
contentBase: path.resolve(__dirname, 'src', 'client', 'static'),
publicPath: (__dirname, 'src', 'client', 'static')
},
plugins: config.plugins,
module: {
rules: [
{
test: /\.js?$/,
exclude: /(node_modules|bower_components)/,
include: path.join(__dirname, 'src'),
use: [
{
loader: 'babel-loader',
query: {
presets: ['react', ['es2015', { 'modules': false }], 'stage-0'],
plugins: ['react-hot-loader/babel', 'react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'],
}
}
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
use: [
{
loader: 'url-loader?limit=100000'
}
],
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'sass-loader']
})
}
]
}
};
Server.js using webpack-dev-middleware
const router = Router();
const clientDir = resolve(`${__dirname}/../../client`);
if (isDev()) {
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const webpackConfig = require('../../../webpack.config')
const webpackHotMiddleware = require('webpack-hot-middleware')
const compiler = webpack(webpackConfig)
// This compiles our app using webpack
router.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
noInfo: true
}))
// This connects our app to HMR using the middleware
router.use(webpackHotMiddleware(compiler))
}
router.use(express.static(clientDir));
export default router
index.js on client side
import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import App from './App'
const root = document.querySelector('.root');
// Wraps our App in AppContainer
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component/>
</AppContainer>,
root
);
};
// Renders our application
render(App);
// This checks if a component has been updated
// It then accepts the changes and replaced the module.
// It's only checking if JS has been changed...
// #TODO - it only works for JS not CSS.
// I think this is an issue with webpack not
// recognizing bundle.css as a dependency?
if (module.hot) {
module.hot.accept();
}
You're using extract-text-webpack-plugin and after webpack rebuilds the bundle the webpack-dev-middleware thinks that nothing changed, because the corresponding module in your bundle representing the CSS is empty as its content has been extracted.
You need to disable extract-text-webpack-plugin in development to get HMR. You can use the disable option and it will fallback to the style-loader, which injects the <style> tags.
new ExtractTextPlugin({
filename: 'bundle.css',
allChunks: true,
disable: true
})
Instead of having to define two versions of the plugin you can use environment variables like NODE_ENV=production and use it in the plugin:
new ExtractTextPlugin({
filename: 'bundle.css',
allChunks: true,
disable: process.env.NODE_ENV !== 'production'
})

How to version CSS file in Webpack and update manifest?

I have a webpack config that has multiple JS entry points. In one of those entry points, I am requiring my styles: require('../sass/app.scss'); and then using a loader to extract the styles into another file app.css.
test: /\.scss$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader!postcss-loader!sass-loader',
})
And that is working great. Of course, we are having issues with old styles being served when we deploy because they are not being versioned like our JS. I have been searching around for a few hours on how to do this and I cannot find a source on how to not only version the CSS, but also get a manifest file for the CSS. I tried creating a new instance of the versioning plugin that I am using, but it only created a manifest for the JS files. I am assuming that since I only have an output for JS that is the reason for this. Anyhow, here is my webpack.config.js:
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const CommonsPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const VersioningPlugin = require('versioning-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash');
const routes = require('./resources/assets/js/routes');
module.exports = {
entry: routes,
devtool: 'eval-source-map',
output: {
path: path.join(__dirname, 'public/js'),
filename: '[name].[chunkhash:6].js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel-loader?presets[]=env',
exclude: path.resolve(__dirname, 'node_modules/')
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader!postcss-loader!sass-loader',
})
}
],
},
plugins: [
new CommonsPlugin({
minChunks: 3,
name: 'common'
}),
new BrowserSyncPlugin({
host: 'localhost',
port: 3000,
proxy: 'napaautocarepro.dev',
files: [
'public/css/app.css',
{
match: ['public/js/*.js', 'app/**/**/*.php', 'resources/views/**/**/*.php'],
fn: function(event, file) {
this.reload();
}
}
]
}, {
injectChanges: true,
reload: false
}),
new ExtractTextPlugin('../css/app.css'),
new VersioningPlugin({
cleanup: true,
basePath: 'js/',
manifestPath: path.join(__dirname, 'public/manifest.json')
}),
new WebpackMd5Hash()
]
};
And here is my weback.prod.config.js:
const config = require('./webpack.config');
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
config.plugins.unshift(
new CleanWebpackPlugin(['js', 'css'], {
root: path.join(__dirname, 'public'),
verbose: true,
dry: false,
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
comments: false,
sourceMap: true
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
canPrint: true,
cssProcessorOptions: { discardComments: { removeAll: true } }
})
);
module.exports = config;
How in the world can I version my CSS file and get it into a manifest so I can autoload the correct version?
Add [chunkhash] to the name of the file, something like:
plugins: [
//...
new ExtractTextPlugin('../css/app.[chunkhash].css'),
//...
]
(from https://github.com/webpack-contrib/extract-text-webpack-plugin#options )

All my code runs twice when compiled by Webpack

When I build my js bundle with webpack using webpack-dev-server my code runs twice every time. Not sure how to fix it.
Screenshot of Developer Tools console
My webpack config:
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
devtool: 'cheap-eval-sourcemap',
entry: [
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server',
path.join(__dirname, '../src/main')
],
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new HtmlWebpackPlugin({
template: path.join(__dirname, '../src/index.html')
}),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../assets'),
to: path.join(__dirname, '../dist/assets')
}
])
],
devServer: {
contentBase: path.join(__dirname, '../dist'),
outputPath: '/lol',
hot: true
},
module: {
loaders: [
{
test: /\.js$/,
loaders: ['babel-loader'],
include: path.join(__dirname, '../src')
}
]
}
};
in the template file you might have manually added a loading the bundle.
If you don't have the
inject: false
option in
new HtmlWebpackPlugin({
template: path.join(__dirname, '../src/index.html')
}),
the bundle will get added again.
Expanding a bit on #Emil Perhinschi's and #ggloren's earlier replies ...
Alternatively, if your ../src/index.html file does not rely on any script other than <script src="bundle.js"></script>, simply remove the latter from your index.html.
Per https://github.com/jantimon/html-webpack-plugin, the default for inject is true and ...
When passing true or 'body' all javascript resources will be
placed at the bottom of the body element.
Thus your two instantiations of bundle.js are:
The <script src="bundle.js"></script> that you (presumably) coded, and
HtmlWebpackPlugin's injection "at the bottom of the body element".

Categories

Resources