Tuesday, 12 June, 2018 UTC


Summary

In a previous post we met Hyperapp, a tiny library that can be used to build dynamic, single-page web apps in a similar way to React or Vue.
In this post we’re going to turn things up a notch. We’re going to create the app locally (we were working on CodePen previously), learn how to bundle it using Parcel (a module bundler similar to webpack or Rollup) and deploy it to the web using GitHub Pages.
Don’t worry if you didn’t complete the project from the first post. All the code is provided here (although I won’t go into detail explaining what it does) and the principles outlined can be applied to most other JavaScript projects.
If you’d like to see what we’ll be ending up with, you can view the finished project here, or download the code from our GitHub repo.
Basic Setup
In order to follow along, you’ll need to have both Node.js and npm installed (they come packaged together). I’d recommend using a version manager such as nvm to manage your Node installation (here’s how), and if you’d like some help getting to grips with npm, then check out our beginner-friendly npm tutorial.
We’ll be using the terminal commands to create files and folders, but feel free to do it by just pointing and clicking instead if that’s your thing.
To get started, create a new folder called hyperlist:
mkdir hyperlist
Now change to that directory and initialize a new project using npm:
cd hyperlist/
npm init
This will prompt you to answer some questions about the app. It’s fine to just press enter to accept the default for any of these, but feel free to add in your name as the author and to add a description of the app.
This should create a file called package.json inside the hyperlist directory that looks similar to the following:
{
  "name": "hyperlist",
  "version": "1.0.0",
  "description": "A To-do List made with Hyperapp",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "DAZ",
  "license": "MIT"
}
Now we need to install the Hyperapp library. This is done using npm along with the --save flag, which means that the package.json file will be updated to include it as a dependency:
npm install --save hyperapp
This might give some warnings about not having a repository field. Don’t worry about this, as we’ll be fixing it later. It should update the package.json file to include the following entry (there might be a slight difference in version number):
"dependencies": {
  "hyperapp": "^1.2.5"
}
It will also create a directory called node_modules where all the Hyperapp files are stored, as well as a file called package-lock.json. This is used to keep track of the dependency tree for all the packages that have been installed using npm.
Now we’re ready to start creating the app!
Folder Structure
It’s a common convention to put all of your source code into a folder called src. Within this folder, we’re going to put all of our JavaScript files into a directory called js. Let’s create both of those now:
mkdir -p src/js
In the previous post we learned that apps are built in Hyperapp using three main parts: state, actions and view. In the interests of code organization, we’re going to place the code for each part in a separate file, so we need to create these files inside the js directory:
cd src/js
touch state.js actions.js view.js
Don’t worry that they’re all empty. We’ll add the code soon!
Last of all, we’ll go back into the src directory and create our “entry point” files. These are the files that will link to all the others. The first is index.html, which will contain some basic HTML, and the other is index.js, which will link to all our other JavaScript files and also our SCSS files:
cd ..
touch index.html index.js
Now that our folder structure is all in place, we can go ahead and start adding some code and wiring all the files together. Onward!
Some Basic HTML
We’ll start by adding some basic HTML code to the index.html file. Hyperapp takes care of creating the HTML and can render it directly into the <body> tag. This means that we only have to set up the meta information contained in the <head> tag. Except for the <title> tag’s value, you can get away with using the same index.html file for every project. Open up index.html in your favorite text editor and add the following code:
<!doctype html>
<html lang='en'>
  <head>
    <meta charset='utf-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <title>HyperList</title>
  </head>
  <body>
    <script src='index.js'></script>
  </body>
</html>
Now it’s time to add some JavaScript code!
ES6 Modules
Native JavaScript modules were introduced in ES6 (aka ES2015). Unfortunately, browsers have been slow to adopt the use of ES6 modules natively, although things are now starting to improve. Luckily, we can still use them to organize our code, and Parcel will sort out piecing them all together.
Let’s start by adding the code for the initial state inside the state.js file:
const state = {
  items: [],
  input: '',
  placeholder: 'Make a list..'
};

export default state;
This is the same as the object we used in the previous article, but with the export declaration at the end. This will make the object available to any other file that imports it. By making it the default export, we don’t have to explicitly name it when we import it later.
Next we’ll add the actions to actions.js:
const actions = {
  add: () => state => ({
    input: '',
    items: state.items.concat({
      value: state.input,
      completed: false,
      id: Date.now()
    })
  }),
  input: ({ value }) => ({ input: value }),
  toggle: id => state => ({
    items: state.items.map(item => (
      id === item.id ? Object.assign({}, item, { completed: !item.completed }) : item
    ))
  }),
  destroy: id => state => ({
    items: state.items.filter(item => item.id !== id)
  }),
  clearAllCompleted: ({ items }) => ({
    items: items.filter(item => !item.completed)
  })
};

export default actions;
Again, this is the same as the object we used in the previous article, with the addition of the export declaration at the end.
Last of all we’ll add the view code to view.js:
import { h } from 'hyperapp'

const AddItem = ({ add, input, value, placeholder }) => (
  <div class='flex'>
    <input
      type="text"
      onkeyup={e => (e.keyCode === 13 ? add() : null)}
      oninput={e => input({ value: e.target.value })}
      value={value}
      placeholder={placeholder}
    />
    <button onclick={add}>+</button>
  </div>
);

const ListItem = ({ value, id, completed, toggle, destroy }) => (
  <li class={completed && "completed"} id={id} key={id} onclick={e => toggle(id)}>
    {value} <button onclick={ () => destroy(id) }>x</button>
  </li>
);

const view = (state, actions) => (
  <div>
    <h1><strong>Hyper</strong>List</h1>
    <AddItem
      add={actions.add}
      input={actions.input}
      value={state.input}
      placeholder={state.placeholder}
    />
    <ul id='list'>
      {state.items.map(item => (
        <ListItem
          id={item.id}
          value={item.value}
          completed={item.completed}
          toggle={actions.toggle}
          destroy={actions.destroy}
        />
      ))}
    </ul>
    <button onclick={() => actions.clearAllCompleted({ items: state.items }) }>
      Clear completed items
    </button>
  </div>s
);

export default view;
First of all, this file uses the import declaration to import the h module from the Hyperapp library that we installed using npm earlier. This is the function that Hyperapp uses to create the Virtual DOM nodes that make up the view.
This file contains two components: AddItem and ListItem. These are just functions that return JSX code and are used to abstract different parts of the view into separate building blocks. If you find that you’re using a large number of components, it might be worth moving them into a separate components.js file and then importing them into the view.js file.
Notice that only the view function is exported at the end of the file. This means that only this function can be imported by other files, rather than the separate components.
Now we’ve added all our JavaScript code, we just need to piece it all together in the index.js file. This is done using the import directive. Add the following code to index.js:
import { app } from 'hyperapp'

import state from './js/state.js'
import actions from './js/actions.js'
import view from './js/view.js'

const main = app(state, actions, view, document.body);
This imports the app function from the Hyperapp library, then imports the three JavaScript files that we just created. The object or function that was exported from each of these files is assigned to the variables state,actions and view respectively, so they can be referenced in this file.
The last line of code calls the app function, which starts the app running. It uses each of the variables created from our imported files as the first three arguments. The last argument is the HTML element where the app will be rendered — which, by convention, is document.body.
The post Use Parcel to Bundle a Hyperapp App & Deploy to GitHub Pages appeared first on SitePoint.