Hyperapp with Pug templates
Tags: javascript, virtualdom, pug, functional
Published: 2019-06-28 09:19:24

I recently completed an [excellent course](https://www.udemy.com/functional-programming-for-beginners-with-javascript/) by James Moore on the basics of functional JavaScript for web apps. The app framework he created is nice but uses [hyperscript](https://github.com/hyperhype/hyperscript) to define HTML as functions. I find hyperscript to be an interesting approach and it does allow for composable UI components. But I don't really get on with it...

As I was browsing DEV, I noticed [a post](https://dev.to/aspittel/functional-programming-in-javascript-done-right-with-hyperapp-570f) from @aspittel which mentioned [Hyperapp](https://github.com/jorgebucaran/hyperapp). Turns out this is built along very similar lines with an option to use JSX as well as hyperscript. I imagine the new v2 has moved on quite a bit from when Ali checked it out in early 2018.

Now, the Hyperapp framework is wonderful and has a number of features I like:
1. Clear functional approach to business logic
2. State-driven UI
3. Centralised state and no stateful components (easy 'undo' option and perfect for quick and reliable development of small to medium scale apps)
4. Events dispatched to update the state which updates the UI using virtualDOM diff
5. Fast, small and simple but sufficient

However, previously I've used [Pug](https://pugjs.org/api/getting-started.html) to define my UI templates. I like the retained ability to see the page structure and the clearer separation of logic and UI. Combining HTML with the business logic using hyperscript h functions doesn't sit right with me (yet?) and I find it hard to reason about a page when the layout is so abstract.

Maybe I'll come round eventually, but maybe I don't need to...

Fortunately for me, the project [pug-vdom](https://github.com/batiste/pug-vdom) (obviously) brings a virtualDOM to Pug. So what follows is a brief intro to the *very* simple app I built to demo how Hyperapp can use Pug templates. See the [Hyperapp pages](https://github.com/jorgebucaran/hyperapp) to get a better understanding of the full range of what it does, or try their new [Udemy](https://www.udemy.com/hands-on-web-development-with-hyperapp-v2/learn/lecture/14983948)/[Packt](https://www.packtpub.com/gb/application-development/hands-web-development-hyperapp-v2-video) course.

As project setup, here's the package.json. Key items to note being the start script and pug/pug-vdom dependencies (and you need [Node.js](https://nodejs.org/en/) version 6.4 [or above](https://node.green/)).

```json
{
  "name": "hyperapp-pug",
  "version": "1.0.1",
  "description": "An instance of hyperapp which uses pug and pug-vdom rather than the default h functions",
  "main": "main.js",
  "scripts": {
    "start": "node ./compilePug.js && parcel ./src/index.html"
  },
  "author": "John Kazer",
  "license": "ISC",
  "devDependencies": {
    "parcel": "1.12.4"
  },
  "dependencies": {
    "hyperapp": "2.0.12",
    "pug": "2.0.4",
    "pug-vdom": "1.1.2"
  }
}
```

And here is the basic project file structure

```
\dist (parcel output dir)
\src
  app.pug.js (compiled pug template)
  index.html
  main.js
  pug-to-view.js
  pug-vdom.js (added as a local copy to handle Hyperapp approach to textNodes)
\views
  app.pug
compilePug.js (see the start script)
package.json
```
And that's it.

_Note:_ There's [a PR for pug-vdom](https://github.com/batiste/pug-vdom/pull/52) to include this tweak, so perhaps in future the local copy can go away.

As you can imagine, after running "npm install", using the "npm run start" script runs a compilation process which converts the Pug view into a .js file. The compilation is defined by compilePug.js. The compiled file is included `require('./app.pug.js')` by main.js and provides the virtual DOM nodes that Hyperapp needs to display the content. Then [parcel](https://parceljs.org/) runs, updates the other files in \src and starts a browser instance of \dist\index.html.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hyperapp demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <div id='app'></div>
    <script src="./main.js"></script>
  </body>
</html>
```

The compile process is pretty simple - a list of the templates and destinations (in this case just the one):

```javascript
// compilePug.js
const vDom = require('pug-vdom')
vDom.generateFile('./views/app.pug', './src/app.pug.js', './views')
```

It compiles my simple Pug template:

```pug
// Need a root div to grab as the start of the view
div
    // receives the variables and functions from the supplied state object
    - var greeting = "Hello " + greet
    p(style={color: "red"}) #{greeting}
    input(size="60" placeholder=placeholder onchange=handler.updateMe)
    button(id='clickMe' onclick=handler.clickMe) Click Me
    p #{userText}

```

Now let's have a quick look at the main.js which defines the app:

```javascript
// main.js
import { app, h, text } from 'hyperapp'
import { pugToView } from "./pug-to-view"

const view = pugToView(h, text)

// event handlers
const clickMe = (state, event) => ({
    ...state,
    userText: state.value
})
const updateMe = (state, event) => ({
    ...state,
    value: event.target.value
})

const initialState = {
    greet: 'friends',
    placeholder: 'Write something here first, hit \<enter\> then click the button',
    value: '',
    userText: '',
    handler: {
        clickMe,
        updateMe
    }
}

const node = document.getElementById('app')

app({
    init: initialState,
    view: view,
    node: node
})

```

Where the helper function pugToView comes from here

```javascript

import 'pug-vdom/runtime' // runtime library is required and puts 'pugVDOMRuntime' into the global scope
const render = require('./app.pug.js')
export const pugToView = (h, text) => state =>
  render(
    state,
    (name, props, children) => h(name, props.attributes, children),
    text
  )[0] // grabs the root 'div' element whilst adjusting the way pug-vdom deals with props compared to hyperapp

```

The main.js file uses the three API functions provided by Hyperapp (yes, just three!), `h`, `text` and `app`. These functions provide state management, virtual DOM, diffing and DOM patching when state changes.

This is a static page, so there is only a simple initial state; some initial values and the event handlers to associate with the button and input elements.

We are going to inject the app at the selected `node` by providing the `view` as defined by the compiled Pug template using the `content` function.

The `app` function pulls all this together and Hyperapp takes care of the rest. Simple declarative, functional, logic coupled to a familiar templating system!

Find the repo [here](https://github.com/johnkazer/hyperapp-pug).