3

I've read quite a few tutorials on webpack, but it seems more for creating web apps then for what I'm trying to do so I didn't want to waste any more time if the following isn't possible.

I'm creating websites on a third-party eCommerce system and they have a system of coding out templates that can be used to change the structure of their website. Below is an example of one of their templates I would be creating (although there are many types & variations I will need to make, not just a few).

My idea to streamline the creation of these templates is to create a bunch of pug components and place them in the components/ directory. Outside of the components directory I want to make higher level pug templates that utilize the components. Once these have been created, I would build it with NPM and the template files need to be converted to HTML and placed within the /dist folder.

Is this hard to do with webpack?

Structure of the project:

src/
..components/
....header/
......header1.pug
......header1.scss
..navcustom-template.pug
..customfooter-template.pug
..non-template-specific.scss

and once built:

dist/
..navcustom-template.html
..customfooter-template.html
..non-template-specific.css

src/
..components/
....header/
......header1.pug
......header1.scss
..navcustom-template.pug
..customfooter-template.pug
..non-template-specific.scss

Sample of a template:

<!--
    Section: NavCustom
-->

<style>

    //Template Speific CSS Imports Here

</style>
<script type="text/javascript">

    //Template Speific JS Imports Here

</script>
<header>

    <div class="col-md-4">

        // Social Media Code

    </div>

    <div class="col-md-4">

        // Logo Code

    </div>

    <div class="col-md-4">

        //  Call to Action Code

    </div>

</header>
<nav>

</nav>
Graham
  • 7,431
  • 18
  • 59
  • 84
Blake Bell
  • 376
  • 5
  • 16

2 Answers2

4

You can use these packages (--save-dev for all):

Then configure Webpack similar to the following, where index.js is a dummy file you should create if you don't already have an entry. Each Pug template you need to process is written in a separate HtmlWebpackPlugin object at the bottom.

var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackPugPlugin = require('html-webpack-pug-plugin');

module.exports = {
  entry: ['./src/index.js'],
  output: {
    path: __dirname + '/dist',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.pug$/,
        use: [
          "raw-loader",
          "pug-html-loader"
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/navcustom-template.pug',
      filename: 'navcustom-template.html'
    }),
    new HtmlWebpackPlugin({
      template: 'src/customfooter-template.pug',
      filename: 'customfooter-template.html'
    }),
    new HtmlWebpackPugPlugin()
  ]
}

All Pug mixins (your src/components files) will be included in the output. This example is tested and working.


Edit: To dynamically process all Pug files in the src directory use this config:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackPugPlugin = require('html-webpack-pug-plugin');
const fs = require('fs');

let templates = [];
let dir = 'src';
let files = fs.readdirSync(dir);

files.forEach(file => {
  if (file.match(/\.pug$/)) {
    let filename = file.substring(0, file.length - 4);
    templates.push(
      new HtmlWebpackPlugin({
        template: dir + '/' + filename + '.pug',
        filename: filename + '.html'
      })
    );
  }
});

module.exports = {
  entry: ['./src/index.js'],
  output: {
    path: __dirname + '/dist',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.pug$/,
        use: [
          "raw-loader",
          "pug-html-loader"
        ]
      }
    ]
  },
  plugins: [
    ...templates,
    new HtmlWebpackPugPlugin()
  ]
}

This uses fs.readdirSync to get all Pug files in a directory. The synchronous version is used (as opposed to fs.readdir) because the module.exports function will return before the files are processed.

Adam
  • 3,829
  • 1
  • 21
  • 31
  • Thanks Adam, this is a huge help for me and I really appreciate it. Using index.js, do you think it would be hard to append `new HTMLWebpackPlugin({});` programatically to the webpack.config.js for each template file so it's not hard coded? I'm just not certain yet if the config is already bootstrapped before code within index.js is hit; if that makes sense. – Blake Bell Sep 05 '18 at 15:47
  • @BlakeBell I have updated my answer to include a way to dynamically process all Pug files. – Adam Sep 05 '18 at 21:44
  • Thanks! I didn't think to add it to the config file, this is perfect. I appreciate you taking the time to code out a working example for me. It really helps me see how someone experienced would handle the same situation. I did have one more SCSS question; if you don't mind. When looking at tuts on SCSS, most of them discuss using external SCSS stylesheets and building them to external CSS files. Do you know if it's possible to include SCSS inside of a style tag in a PUG template and once processed, it becomes embedded CSS in the generated HTML file? – Blake Bell Sep 06 '18 at 17:25
  • As far as I can tell, it's impossible to do that with the loaders and plugins out there right now. – Adam Sep 07 '18 at 18:57
  • Thanks Adam, I did end up finding [jstransformer-scss](https://github.com/jstransformers/jstransformer-scss) which looks fairly promising. It lets you embed SCSS from an external file or a JS string. – Blake Bell Sep 14 '18 at 18:44
1

in 2022 is appeared the Pug plugin for Webpack that compiles Pug to static HTML, extracts CSS and JS from their source files defined directly in Pug.

It is enough to install the pug-plugin only:

npm install pug-plugin --save-dev

webpack.config.js

const path = require('path');
const PugPlugin = require('pug-plugin');

module.exports = {
  output: {
    path: path.join(__dirname, 'dist/'),
    filename: 'assets/js/[name].[contenthash:8].js'
  },

  entry: {
    // Note: a Pug file is the entry-point for all scripts and styles.
    // All scripts and styles must be specified in Pug.
    // Define Pug files in entry:
    index: './src/views/index.pug', // => dist/index.html
    'navcustom-template': './src/navcustom-template.pug', // => dist/navcustom-template.html
    'customfooter-template': './src/customfooter-template.pug', // => dist/customfooter-template
    // etc ...
  },

  plugins: [
    // enable using Pug files as entry-point
    new PugPlugin({
      extractCss: {
        filename: 'assets/css/[name].[contenthash:8].css' // output filename of CSS files
      },
    })
  ],

  module: {
    rules: [
      {
        test: /\.pug$/,
        loader: PugPlugin.loader, // the Pug loader
      },
      {
        test: /\.(css|sass|scss)$/,
        use: ['css-loader', 'sass-loader']
      },
    ],
  },
};

Of cause, the entry can be dynamically generated like in the answer above.

In your Pug file use the source files of styles and scripts via require():

html
  head
    //- add source SCSS styles using a path relative to Pug file
    link(href=require('./styles.scss') rel='stylesheet')
  body
    h1 Hello Pug!
    
    //- add source JS/TS scripts
    script(src=require('./main.js'))

Generated HTML:

<html>
  <head>
    <link href="/assets/css/styles.f57966f4.css" rel="stylesheet">
  </head>
  <body>
    <h1>Hello Pug!</h1>
    <script src="/assets/js/main.b855d8f4.js"></script>
  </body>
</html>

Just one Pug plugin replaces the functionality of many plugins and loaders used with Pug:

  • pug-html-loader
  • html-webpack-pug-plugin
  • html-webpack-plugin
  • mini-css-extract-plugin
  • resolve-url-loader
  • svg-url-loader
  • posthtml-inline-svg
biodiscus
  • 365
  • 3
  • 8