Neithan Max
Neithan Max

Reputation: 11766

Can't make Webpack "live reload" features with an Express+Pug (Jade) web app

I've searched here and there very few questions using Pug (Jade) and even fewer answers. I thought looking code in GitHub would give me an answer but has only brought more confusion, outdated code, and repos that don't work.

What I want is very simple: develop a simple Node+Express+Postgress+Pug site with webpack's live reloading features. Postgress hasn't entered the picture yet, since using Webpack as a dev aid hasn't worked.

Using HMR and html-webpack-plugin, I expected a swift development experience. My *.pug files should show data sent down by the controller, as they do when I run a node server instead of Webpack's webpack-dev-server. Also, it fails to refresh the browser on changes. Everything else I have no problems with bundling works well; it quickly reloads server changes, etc.

Because I'm not doing a SPA, and I've seen you have to spawn a new plugin object per *.pug page, I made a simple js utility to collect all the *.pug pages so I can do this:

    new HtmlWebPackPlugin({
      filename: 'index.html',
      template: './src/views/pages/index.pug',
      inject: true,
    }), ...pugPages.pages(env),

I've tested and it works so that's a ton of silly code I don't need to write and update.

With that hack, I get to see the PUG pages rendered in the browser. But they don't show the data sent down by the Express controller. For example:

index.js:

router.get('/', (req, res, next) => {
  res.render('index', { msg: 'index page' });
  next();
});

index.pug::

extends ../layout

block head
  include ../partials/head

block content
  h1 You are in #{msg}
  //h1= htmlWebpackPlugin.options.title
  //p Welcome to #{htmlWebpackPlugin.options.title}
  //script.
  //  console.log(!{JSON.stringify(htmlWebpackPlugin.options)})

This just shows "You are in". Again, if run by Node, it shows the correct "You are in index page". As you can tell I was trying with htmlWebpackPlugin.options.title. But if that's the only way this works (through that object), how can I get pug the data from Express? Does html-webpack-plugin make templates static, therefore, rendering pug useless?

I'm using:

Node.js v10.16.0
darwin 16.7.0
npm 6.9.0
webpack 4.29.0
html-webpack-plugin 3.2.0

I made a leaner branch with everything in place for easy helping. Here it is: https://github.com/nmaxcom/Express-pug-webpack

Upvotes: 2

Views: 1346

Answers (1)

Sobo
Sobo

Reputation: 11

I had the same question but hopefully I have figured it out. Webpack will bundle your code before your server is up and running - this is different from Express which will take a request from the user, process it, and send a response. Therefore, when you are using the html-webpack-plugin and bundling your pug files, everything has to happen at compile time rather than request processing time.

However, that does not really mean that using pug is useless. You can use composition (using includes and extends) to greatly speed up your web development and by using a combination of pug files and options, you can create several html files using just one pug template. For dynamically loading content based on user request, you can either serve those dynamic pages through NodeJS or you can have a ajax call on client side to populate the data

Regarding hot reloading, please note that for HMR to work you need to ensure the following:-

  1. Add 'webpack-hot-middleware/client?reload=true' as the first item for all entries in your webpack config
  2. Ensure that your pug file (that you are trying to hot reload) is in your entry
  3. Ensure you have use new Webpack.HotModuleReplacementPlugin() as a plugin in your webpack config (where Webpack is just require('webpack')

So, possible setup would be

    const webpack = require('webpack');
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = {
      entry: {
        index: [
          'webpack-hot-middleware/client?reload=true',
          path.resolve(__dirname, '../src/client/assets/js/main.js'),
          path.resolve(__dirname, '../src/client/templates/views/index.pug'),
        ],
      },
      .
      .
      .
      plugins: [
        // OccurrenceOrderPlugin is needed for webpack 1.x only
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        // Use NoErrorsPlugin for webpack 1.x
        new webpack.NoEmitOnErrorsPlugin(),
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, '../src/client/templates/views/index.pug'),
          filename: 'index.html'
          chunks: ['index']
        }),
      ]
      .
      .
      .

Note: If in the above example main.js requires index.pug, then you can remove index.pug from the entry

Upvotes: 1

Related Questions