jus
jus is a development server and build tool for making static websites with no configuration and no boilerplate code. It has built-in support for browserify, ES6 and ES2015 with Babel, React JSX, GitHub Flavored markdown, syntax highlighting, Sass, Less, Stylus, Myth, Handlebars, browsersync and more.
Why?
The year is 2016 and you're building a new website. At first you just create a single HTML file with some inline scripts and style tags. This works for a bit, but soon your code grows and you decide to extract the styles and scripts into standalone files. This is slightly better, but eventually you want to do something more sophisticated, like writing your stylesheets in Sass, or concatenating and minifying assets, or using npm dependencies with browserify. These conveniences are essential to building a website of any magnitude, but setting them up is tedious and time-consuming. It's at this point in the project that your attention turns from the creative to the mundane. Rather than building, you're now configuring.
In this day and age, most developers would turn to Gulp, npm scripts, Jekyll or one of dozens of static site tools. This is where jus comes in as an alternative.
There is no setup with jus. It has just two commands: serve
and build
. Run jus serve
in your project directory and you've got a live develpment server running, watching for file changes, autorefreshing your browser with browsersync, and serving your content with clean URLs. Write a foo.sass
file and it'll be served at /foo.css
. Use an npm-style require
statement in your script, and jus will serve it up as a browserified bundle. Write React's JSX syntax and it'll be transpiled to javascript on the fly. Write a GitHub-flavored /markdown/file.md
and it'll be served as syntax-highlighted HTML at /markdown/file
.
When it's time to deploy, run jus build
to compile your site down into plain old HTML, CSS, and Javascript files, ready for deployment to GitHub Pages, Surge, or any other static site host that supports clean URLs.
Getting Started
jus requires node 4 or greater, because it uses some newer Javascript features. Install the command-line interface globally, then run it to see usage instructions:
npm i -g jus && jus
jus has a lot of dependencies, so it takes a while to install. Maybe go grab a ☕ and read up on how to make npm faster.
If you like to learn by example, check out the repos of sites using jus. Otherwise, read on...
Pages
Pages are written in Markdown, HTML, Handlebars, or any combination thereof. At render time each page is passed a Handlebars context object containing metadata about all the files in the directory.
- Markdown parsing with marky-markdown, the battle-tested commonmark-compliant parser used by npmjs.com.
- GitHub-flavored Markdown support, including fenced code blocks
- Syntax Highlighting powered by Atom's highlights package.
- Markdown headings (
H1
,H2
, etc) are automatically converted to anchored hyperlinks. - Emoji support. Converts :emoji:-style shortcuts to unicode emojis.
- HTML frontmatter as page metadata
Extensions: html|hbs|handlebars|markdown|md
Scripts
All javascript files in your project are automatically browserified and babelified using the es2015
and react
presets.
You can use node-style require
statements to include node and npm modules in your code:
const url = require('url').parse('https://example.com')
console.log(`the domain is ${url.host}`)
You can also use ES6-style imports, if you prefer:
import React from 'react'
import ReactDOM from 'react-dom'
import domready from 'domready'
domready(() => {
// do some React magic
})
Scripts are browserified using babel-preset-react
, so you
can write JSX in your scripts.
Extensions: js|jsx|es|es6
Stylesheets
Stylesheets can be written in Sass, SCSS, Less, Stylus, Myth, or plain old CSS. Use whatever preprocessor suits your fancy.
Extensions: css|less|sass|scss|styl
Context
When the jus server is initialized, it recursively finds all the files in the directory tree,
ignoring node_modules
, .git
, and other unwanted patterns. These files are then stored in
memory in an array called files
. For convenience, this list of files is broken down
into smaller arrays by type: an array for pages
, another array for scripts
, etc.
{
files: [...],
pages: [...]
scripts: [...]
stylesheets: [...]
images: [...]
datafiles: [...]
}
When you request a page, the server renders the page on the fly, passing this object to the given page's template. This means every page has access to metadata about every file in the site at render time.
Using handlebars in your pages is entirely optional. If your pages don't need to do any dynamic rendering at build time, that's okay. The context will simply be ignored at render time.
Frontmatter
jus supports HTML frontmatter. This allows you to add key-value metadata to your pages:
<!--
title: Alice in Wonderland
year: 1951
-->
Any such values present in an HTML comment at the top of a page are made available in that page's Handlebars context object at render time.
Note: Jekyll uses YAML for frontmatter, but jus uses HTML, because it can be included in a file without adversely affecting the way it renders on github.com.
Templates
Handlebars templates can be used to wrap layouts around your pages.
- If a file named
/layout.(html|hbs|handlebars|markdown|md)
is present, it will be applied to all pages by default. - Templates must include a
{{{body}}}
string, to be used as a placeholder for where the main content should be rendered. - Templates must have the word
layout
in their filename. - Pages can specify a custom layout in their frontmatter. Specifying
layout: foo
will refer to the/layout-foo.(html|hbs|handlebars|markdown|md)
layout file. - Pages can disable layout by setting
layout: false
in their frontmatter.
Extensions: html|hbs|handlebars|markdown|md|mdown
Helpers
jus provides a number of helper functions you can use in your handlebars templates. All of the helpers are from lobars, a collection of utility functions plucked directly from lodash.
lobars includes comparison helpers like endsWith, eq, gt, gte, includes, isArray, isBoolean, isDate, isEmpty, isMatch, isNumber, isString, isSymbol, isUndefined, lt, lte, startsWith and more.
Here's an example use of the gte
(greater than or equal to) helper:
lobars also provides helpers for manipulating input like camelCase, capitalize, escape, kebabCase, lowerCase, lowerFirst, pad, padEnd, padStart, parseInt, repeat, replace, snakeCase, split, startCase, template, toLower, toUpper, trim, trimEnd, trimStart, truncate, unescape, upperCase, upperFirst, and more.
Here's how you use the string helpers:
Images
Delicious metadata is extracted from images and included in the Handlebars context object, which is accessible to every page.
- Extracts EXIF data from JPEGs, including geolocation data.
- Extracts dimensions
- Extracts color palettes
Extensions: png|jpg|gif|svg
Datafiles
JSON and YML files are slurped into the Handlebars context object, which is accessible to every page.
Extensions: json|yaml|yml
Clean URLs
jus uses a clean URL strategy that is compatible with
GitHub Pages
and
surge.sh.
In essence, pages get their extension lopped off,
and pages named index
inherit the name of their directory.
Filename | URL |
---|---|
index.html | / |
nested/index.html | /nested |
nested/page.html | /nested/page |
also/markdown.md | /also/markdown |
also/handlebars.hbs | /also/handlebars |
stylesheet.scss | /stylesheet.css |
stylesheet.sass | /stylesheet.css |
stylesheet.styl | /stylesheet.css |
stylesheet.styl | /stylesheet.css |
Deployment to GitHub Pages
Add the following to your package.json:
{
"scripts": {
"start": "jus serve",
"deploy": "npm run build && npm run commit && npm run push && npm run open",
"build": "jus build . dist",
"commit": "git add dist && git commit -m 'update dist'",
"push": "git subtree push --prefix dist origin gh-pages",
"open": "open http://zeke.sikelianos.com"
}
}
Now whenever you want to publish to GitHub Pages, run:
npm run deploy
Note: GitHub's User Pages (like yourname.github.io
) are built from the master
branch,
whereas Project Pages (like yourname.github.io/project
) are built from the gh-pages
branch.
Be aware of this when setting up your npm scripts.
Note: GitHub's CDN can take a minute to update, so you might have to refresh a few times when visiting.
Deployment to Surge
surge.sh is an awesome new platform for publishing static websites.
Install the Surge CLI:
npm i -g surge
Add the following to your package.json:
{
"scripts": {
"start": "jus serve",
"deploy": "npm run build && npm run build && npm run open",
"build": "jus build . dist",
"push": "surge dist YOUR-URL",
"open": "open YOUR-URL"
}
}
Now whenever you want to publish to Surge, run:
npm run deploy
Prior Art
jus was inspired by a number of existing tools:
- Harp: The main inspiration for jus. It was the first static site tool to introduce the concept of an in-place asset pipeline.
- Jekyll: A blog-aware static site generator in Ruby. jus borrows the concept of frontmatter from Jekyll, but uses HTML frontmatter, unlike Jekyll's YAML frontmatter.
- Brunch: A lightweight tool for building HTML5 applications with emphasis on elegance and simplicity. The jus development server uses the chokidar module from Brunch to watch the filesystem.
- Ruby on Rails: The web development framework that helped popularize Convention over Configuration
Sites using jus
Sometimes real examples are the easiest way to learn. Check out these open-source sites built with jus:
- jus.js.org, the site you're looking at now.
- zeke.sikelianos.com, a personal portfolio site.
- acrophony, an experimental React GUI for acrophonic alphabets.