Chapter 6 HTML part one

Cover image by Antoni Shkraba Recap Last time, we installed Webpack so we could process our JavaScript. With more JavaScript. We learned: Webpack does for JavaScript what SASS does for CSS (and SASS) How to configure Webpack for development Two different types of module imports in JavaScript Why we might use NPX as well as NPM How to configure Webpack for production How to keep these two configuration files DRY What IIFEs are and why we use them Writing client-side JavaScript modules and the correct way to import them This time, we're finally going to work on generating HTML files. Working with HTML We're on the home straight now. We've done images, JavaScript and CSS. Just HTML to go. Soon, you'll be able to write your own code and discard me like an old sock. HTML is less of a focus for a lot of Single Page Applications (called "SPA" by its fam). This is because most of the work of rendering snippets of HTML is taken up by JavaScript - the HTML is really just a wrapper into which JavaScript plonks markup. But you might not want to do that. Wouldn't it be nice to split HTML into modules, so that repeated parts (such as the header and the footer) could be found in one place and then pulled into the page? To achieve this goal, we'll need to import four different modules. Spoilers follow: PostHTML gives JavaScript the power to mess around with HTML posthtml-cli allows us to control PostHTML using Node, so we can string all this together posthtml-modules lets us cut up our page into tiny wee bits (also known as modules) htmlnano takes a HTML page and removes all the repeated whitespace, just like we've already done with our JavaScript and CSS These packages aren't as popular as the ones we've been using up until now. That's for a couple of reasons: MVC Single-page applications don't need to generate multiple HTML pages. They build them on-the-fly. MVC stands for "Model View Controller". This is one way to split up an application's code: "Model" is basically the same thing as the data. It's what's feeding into the application and needs to be displayed "View" is closest to a template. It's the structure which sits around the data and gives it shape and allows the user to visually group together similar parts of the page "Controller" is all the business logic which decides what happens when the user interacts with the user interface in some way, such as clicking a "like" button This way of splitting up the code is another example of separation of concerns. The MVC approach is dominating the application market at the time of writing. The three main front-end frameworks which do this are React, Vue and Angular but there are many, many more. Front-end MVC frameworks run client-side. This means all the JavaScript is parsed by the web browser. Our approach with PostHTML will be rendered server side (also called SSR)1. Because MVC frameworks handle everything which PostHTML and its pals do, PostHTML is not as popular as React. JAMSTACK The second reason that PostHTML isn't a very popular package is JAMSTACK. JAMSTACK sits on top of an MVC application and allows it to build out individual pages to create a multi-paged site. These pages exist as actual HTML pages for users and search engines to visit but once a modern browser lands on any single page, it kind of fakes a reload when the user requests to move to a different page. The data for just that next page is pulled into the MVC application and the current page is rebuilt with the new content. Just in case the user looks at the address bar, that is rewritten with the new URL (even though that flat HTML page wasn't loaded). This approach has a few advantages: The smallest possible parcel of data is sent across the internet The web browser usually doesn't have to re-render the whole page, just a small part of it The user doesn't have to suffer the existential doubt with comes when they look at an empty screen for a fraction of a second Because the pages all exist on the web server, the site can easily be spidered by search engines and users can enter the site on "deep" pages Some code files split the JavaScript into what is running on the server and what is running in the browser. This means we can manipulate sensitive data (such as API keys) with JavaScript without the risk of exposing this to a public URL. JAMSTACK frameworks do both server side rendering and client-side rendering. Twice the work! But all of this is a course in it's own right and won't be covered here. Sorry! Let's get back to the more traditional, static approach for now. Installing PostHTML, posthtml-cli, posthtml-modules and htmlnano Let's install all four of the packages we want at once. In a terminal, type: npm i -D posthtml posthtml-cli posthtml-modules htmlnano Congratulations! You have unlocked a new achievement! "Downloaded your first security vulnerability". Note that your terminal says: 5

Mar 11, 2025 - 10:38
 0
Chapter 6 HTML part one

Cover image by Antoni Shkraba

Recap

Last time, we installed Webpack so we could process our JavaScript. With more JavaScript. We learned:

  • Webpack does for JavaScript what SASS does for CSS (and SASS)
  • How to configure Webpack for development
  • Two different types of module imports in JavaScript
  • Why we might use NPX as well as NPM
  • How to configure Webpack for production
  • How to keep these two configuration files DRY
  • What IIFEs are and why we use them
  • Writing client-side JavaScript modules and the correct way to import them

This time, we're finally going to work on generating HTML files.

Working with HTML

We're on the home straight now. We've done images, JavaScript and CSS. Just HTML to go. Soon, you'll be able to write your own code and discard me like an old sock.

HTML is less of a focus for a lot of Single Page Applications (called "SPA" by its fam). This is because most of the work of rendering snippets of HTML is taken up by JavaScript - the HTML is really just a wrapper into which JavaScript plonks markup.

But you might not want to do that. Wouldn't it be nice to split HTML into modules, so that repeated parts (such as the header and the footer) could be found in one place and then pulled into the page?

To achieve this goal, we'll need to import four different modules. Spoilers follow:

  1. PostHTML gives JavaScript the power to mess around with HTML
  2. posthtml-cli allows us to control PostHTML using Node, so we can string all this together
  3. posthtml-modules lets us cut up our page into tiny wee bits (also known as modules)
  4. htmlnano takes a HTML page and removes all the repeated whitespace, just like we've already done with our JavaScript and CSS

These packages aren't as popular as the ones we've been using up until now. That's for a couple of reasons:

MVC

Single-page applications don't need to generate multiple HTML pages. They build them on-the-fly.

MVC stands for "Model View Controller". This is one way to split up an application's code:

  • "Model" is basically the same thing as the data. It's what's feeding into the application and needs to be displayed
  • "View" is closest to a template. It's the structure which sits around the data and gives it shape and allows the user to visually group together similar parts of the page
  • "Controller" is all the business logic which decides what happens when the user interacts with the user interface in some way, such as clicking a "like" button

This way of splitting up the code is another example of separation of concerns.

The MVC approach is dominating the application market at the time of writing. The three main front-end frameworks which do this are React, Vue and Angular but there are many, many more.

Front-end MVC frameworks run client-side. This means all the JavaScript is parsed by the web browser. Our approach with PostHTML will be rendered server side (also called SSR)1. Because MVC frameworks handle everything which PostHTML and its pals do, PostHTML is not as popular as React.

JAMSTACK

The second reason that PostHTML isn't a very popular package is JAMSTACK. JAMSTACK sits on top of an MVC application and allows it to build out individual pages to create a multi-paged site. These pages exist as actual HTML pages for users and search engines to visit but once a modern browser lands on any single page, it kind of fakes a reload when the user requests to move to a different page.

The data for just that next page is pulled into the MVC application and the current page is rebuilt with the new content. Just in case the user looks at the address bar, that is rewritten with the new URL (even though that flat HTML page wasn't loaded).

This approach has a few advantages:

  • The smallest possible parcel of data is sent across the internet
  • The web browser usually doesn't have to re-render the whole page, just a small part of it
  • The user doesn't have to suffer the existential doubt with comes when they look at an empty screen for a fraction of a second
  • Because the pages all exist on the web server, the site can easily be spidered by search engines and users can enter the site on "deep" pages
  • Some code files split the JavaScript into what is running on the server and what is running in the browser. This means we can manipulate sensitive data (such as API keys) with JavaScript without the risk of exposing this to a public URL.

JAMSTACK frameworks do both server side rendering and client-side rendering. Twice the work!

But all of this is a course in it's own right and won't be covered here. Sorry! Let's get back to the more traditional, static approach for now.

Installing PostHTML, posthtml-cli, posthtml-modules and htmlnano

Let's install all four of the packages we want at once. In a terminal, type:

npm i -D posthtml posthtml-cli posthtml-modules htmlnano

Congratulations! You have unlocked a new achievement! "Downloaded your first security vulnerability". Note that your terminal says:

5 moderate severity vulnerabilities

What can you do about this? Glad you asked - usually, bugger-all.

Side quest: Node security woes
So we can run npm audit from a terminal to see a list of these issues. It tells us:

got  <11.8.5
Severity: moderate
Got allows a redirect to a UNIX socket - https://github.com/advisories/GHSA-pfrx-2q88-qq97
fix available via `npm audit fix --force`
Will install posthtml-cli@0.7.7, which is a breaking change
node_modules/got
  package-json  <=6.5.0
  Depends on vulnerable versions of got
  node_modules/package-json
    latest-version  0.2.0 - 5.1.0
    Depends on vulnerable versions of package-json
    node_modules/latest-version
      update-notifier  0.2.0 - 5.1.0
      Depends on vulnerable versions of latest-version
      node_modules/update-notifier
        posthtml-cli  >=0.8.0
        Depends on vulnerable versions of update-notifier
        node_modules/posthtml-cli

Wait, so all versions of posthtml-cli greater than 0.7.7 depend upon a vulnerable version of got? And the fix is to install an older version of posthtml-cli? How does this make sense? Also to downgrade to the "safe" version of posthtml-cli is a breaking change. How broken exactly? No idea what to do with that information.

Should I even care?

The vulnerability is to posthtml_cli which helps us interact with PostHTML using the terminal. posthtml_cli sits within the devDependencies node of package.json. Which means it never makes it to the live site. It wouldn't make any sense on the live site. So this situation is only risky if a hacker had direct access to your computer, ran the site locally, then used the compromised version of got to ... do something?

Pro-tip: if a hacker has control over your computer, the gig is already up. They will be too busy stealing your data to bother running a dev environment.

Instead of forcing a breaking change, let's try the gentle method and see how far it gets us. In a terminal, type:

npm audit fix

Did that fix the issue? No. Did it break anything else? Also no. Did it make us feel better? Little bit. Let's move on.

Configuring PostHTML

Remember when we installed Webpack and it came with it's own configuration file which needed to sit in the root of our project? PostHTML's got one of those too. It's called posthtml.json and looks like this:

{
  "input": "src/views/**/*.html",
  "output": "dist",
  "plugins": {
      "posthtml-modules": {
          "root": "./src/views",
          "initial": true
      },
      "htmlnano": {}
  }
}

So create that, if you could. It sits along site package.json and all the rest in the root of the Node application.

You might have noticed that this is pointing at a directory which doesn't exist yet - a sub-folder of src called views. Why is this?

We need somewhere to put all our HTML where it won't get mixed up with all the other file types. For example, if we pointed PostHTML at the src directory and then set up a watch task to look for changes, this task would trigger every time we did something inside the scss or js folders too.

The reason it's called views echoes what I said earlier about MVC - the views part of MVC is the presentational HTML.

Wildcards
You might spot a bunch of asterisks hanging out on this line of posthtml.json2:

"input": "src/views/**/*.html",

In this context, the asterisks are behaving as a "wildcard" (in normal JavaScript, a single asterisk would act as a multiplication symbol). Wildcard characters can stand for anything, like in that card game I've never played because it doesn't interest me. So the path here translates into English as "any files which end with a html file extension inside the src/views directory or any of the sub-directories (or no sub-directory)". So:

  Any sub-directory ↓↓ ↓ any file name
"input": "src/views/**/*.html",
or no sub-directory ↑↑

You might reasonably assume that this pattern works in the same way as the sass package does - that it watches a directory and replicates anything it finds there into a different directory. Sadly, this isn't the case. But we'll fix that issue in a bit.

Finally, the plugins node calls two other packages we just installed, posthtml-modules and htmlnano. posthtml-modules has a number of different options we can specify. We've simply pointed it at our views directory and asked that when it first starts up, it should automatically run.

The second plugin is htmlnano the configuration of which is ... not well documented. So we've passing it an empty configuration object, which is the same as telling it to run with the default configuration. If we don't pass it something, it won't run at all.

Setting up the new commands

Back in package.json, let's set up our develop and build tasks for HTML files. They'll sit inside the scripts node and look like this:

"html-build": "posthtml -c posthtml.json",
"watch-html": "onchange \"src/views\" \"src/fragments\" -- npm run html-build",

I've added them inside the scripts node at the bottom.

Hang on - there's another new directory referenced here called fragments. What's that all about?

More new directories

PostHTML will process any and all html files inside our views directory and we also want to use little fragments of HTML to include into these pages. By splitting these out into a new, sibling directory, we can ensure that these wee bits never end up as html files in their own right on the live site. So the directory structure of the src folder should look like this:

...