shou2017.com
JP

Creating Static Site Layouts with webpack

Sat Feb 1, 2020
Sat Aug 10, 2024

As you get more deeply involved in front-end development, webpack becomes unavoidable. Here, I’ll note the method for creating a static site with about 20 pages using webpack.

Environment

  • macOS Catalina 10.15.2
  • node.js v10.16.0
  • npm 6.13.4

Getting Started

In the project root:

$ npm init -y (Generate configuration file)

Installing webpack

$ npm install --save-dev webpack webpack-cli

The --save-dev option means that the information about the installed package will be recorded in package.json.

Options

  • --save-dev Used when installing tools for application development
  • --save Used when installing components needed for the application itself to run

After installing webpack, your package.json will have a record like this:

{
  "name": "e-learning-general",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10"
  }
}

Creating HTML Layouts with Pug

Apparently, webpack can also be used to create common elements like header and footer, but I couldn’t figure out how to do it effectively, so I decided to build with pug and output to the public folder for publishing.

So, for now, let’s forget about webpack and install the necessary pug and pug-cli:

$ npm install --save-dev pug pug-cli

Next, create a public folder for publishing and a src folder to store the HTML layouts at the project root.

$ mkdir public src

Create a layout folder directly under src to store the pug files.

$ mkdir src/layout

Now that we’ve done this, we can write with pug. Using pug’s Includes, you can componentize headers, footers, and other parts. In my case, I put common components in the _common folder. The folder structure looks like this:

layout/_common
         |- _partials folder (Common files such as header and footer)
         |- _baseof.pug (Base pug file)
      /user
      /money

Next, create the base _baseof.pug:

block pagedata
doctype html
html(lang="ja")
    head
        meta(charset="utf-8")
        meta(http-equiv="X-UA-Compatible" content="IE=edge")
        meta(name="viewport" content="width=device-width, initial-scale=1.0")
        link(href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet")
        title #{pageTitle} | Sample
    body
        include ./_partials/_header ← Common part
        block main ← Content for each page goes here
        include ./_partials/_footer ← Common part
    block pagejs ← JavaScript for each page

Now that we’ve come this far, all that’s left is to create the common parts in pug and then create each page. Here’s what creating a user screen looks like. First load the baseof.pug file, then build from there. Setting a path variable for the static site directory is convenient.

//- Load template
extend ../_common/_baseof
//- Page-specific settings
append pagedata
    - var pageTitle= "About this site";
    - var path = '../';
//- Page-specific JavaScript
append pagejs
    script(src=`${path}main.js`)
//- Page content
block main User will be displayed here

After creating the layout with pug, we need to create a script to output it to the public folder.

The location doesn’t matter, but for now, let’s create sample.sh at the project root.

$ touch sample.sh

Write a script to build pug in sample.sh:

#sample.sh

npx pug src/layout/user/*.pug --pretty --out public/terms

Now let’s try running the script:

% sh sample.sh

If it builds correctly, you should see HTML in the public folder. That completes the HTML part.

webpack-dev-server

If you use webpack-dev-server, changes to files will be reflected immediately, so let’s install it:

$ npm install --save-dev webpack-dev-server

Create a webpack.config.js to enable webpack-dev-server:

$ touch webpack.config.js

Here’s the basic form including webpack-dev-server and entry points:

module.exports = {
    entry: './src/index.js',
    output: {
        path: `${__dirname}/public`,
        filename: 'main.js'
    },
     devServer: {
        contentBase: `${__dirname}/public`,
    },
};

Edit package.json to make webpack-dev-server easy to start.

Also, add a build command:

  "scripts": {
    "start": "webpack-dev-server --open",
    "build": "webpack --config webpack.config.js"
  },

Creating the webpack Entry Point

Create index.js in the src folder to serve as the entry point:

$ touch src/index.js

An entry point is the starting file when bundling.

// index.js
console.log('This is the entry point')

Now that we’ve done this, let’s start webpack-dev-server.

First, build to output main.js:

$ npm run build

Start webpack-dev-server:

$ npm start

If the screen displays correctly, check the console in the developer tools.

You should see This is the entry point displayed.

Bundling SCSS

Install css-loader, style-loader, sass-loader, and node-sass which is needed to compile scss:

$ npm install --save-dev webpack webpack-cli css-loader style-loader sass-loader node-sass

After installation, specify the test files and use loaders to apply in the module-rules parameter of webpack.config.js.

Loaders are applied from right to left. In this case, since it’s a bit line-broken, they’re applied from the bottom up. First, sass-loader compiles sass to css using node-sass, then css-loader bundles it, and finally style-loader applies it to insert the css into the js file.

    module: {
        rules: [
            {
                test: /\.css|scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ],
            },
        ],
    }

Let’s try with style.scss like this:

body {
    background-color: red;
}

Import this style.scss as a module in the entry point index.js:

import './assets/scss/style.scss';
$ npm start

If the body is red, it’s a success. You should see the color change immediately when you update the scss.

Installing Normalize.css

$ npm install --save normalize.css

Import normalize.css:

import 'normalize.css'

Since normalize.css is officially published through npm, just install it as is.

If you build, you can confirm that normalize.css has been added to main.js:

$ npm run build

Separating Development and Production

In production, you might want to compress source code or remove unnecessary console.log statements before building. For this, we use webpack-merge.

Create the following files at the project root:

$ touch webpack.base.js webpack.development.js webpack.production.js
  • Common settings: webpack.base.js
  • Development settings: webpack.development.js
  • Production settings: webpack.production.js

Install webpack-merge:

$ npm install --save-dev webpack webpack-cli webpack-merge

First, let’s set up the common configuration:

// Common settings: webpack.base.js

module.exports = {
    entry: './src/index.js',
    output: {
        path: `${__dirname}/public`,
        filename: 'main.js'
    },
    devServer: {
        contentBase: `${__dirname}/public`,
    },
    module: {
        rules: [
            {
                test: /\.css|scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ],
            },
        ],
    }
};

Next is the webpack.development.js configuration. Since we’re in development, it’s better to be able to check sourcemaps:

const merge = require('webpack-merge');
const base = require('./webpack.base.js');
module.exports = merge(base, {
    mode: 'development',
    devtool: 'eval-source-map',
})

For production, just setting webpack’s mode to production will automatically handle compression. If you need detailed settings, configure them with plugins as needed. Below, we’re using terser-webpack-plugin to prevent outputting console.log:

const merge = require('webpack-merge');
const base = require('./webpack.base.js');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = merge(base, {
    mode: 'production',
    optimization: {
        minimize: true,
        minimizer: [new TerserPlugin({
            terserOptions: {
                compress: {drop_console: true}
            }
        })],
    }
})

Development and Production Settings in package.json

Now that we’ve split the development and production settings using webpack-merge, let’s specify these settings in package.json:

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open-page -config webpack.development.js",
    "build": "webpack --config webpack.production.js && sh sample.sh"
  },

With this, npm start will launch with development settings, and npm run build will build with production settings.

The end.

See Also