by Before Semicolon


engine details
name engine
type async function
arguments express app, pages directory, options
description A function that will setup the express engine and setup the routing of your application.

Asynchronous function

The engine function is ASYNCHRONOUS which means that you can await on it before you start the server.

const {engine} = require('@beforesemicolon/html-plus');

const app = express();

(async () => {
	// setup all middlewares and any route overrides before the engine
	await engine(app, path.resolve(__dirname, './pages'), {
		staticData: {
			site: {
				version: "2.4.1",
				license: "MIT"


If you await on the engine and have some dynamic routes you may need to set them up before otherwise any matching static route will override them. Prefer the engine call as the last thing before you call the listen method for the server.

If you are not awaiting the engine, you can just set the routes after. The effect is the same but not awaiting on the engine may throw an error if you get to the page before the engine is done processing your pages directory.


express app
type: object, required : An express app
pages directory
type: string, required : A valid absolute path to the directory where all HTML files will be placed.
type: object, optional : Object with different options to be consumed by the engine. If not provided, the engine will try to find for the hp.config.js file on the project root directory.


Options can be provided directly to the engine function as third argument or you can simply create a hp.config.js at the root directory and export(using module.exports = {...}) an object with the following properties.

If you provide options both, by providing the hp.config.js file and engine options, these are deeply merged and the engine options will override the hp.config.js file where they match in properties.

type: object : An object with whatever data you want to be available on all templates.
type: array : An array of function or class based custom tags.
type: array : An array of Attributes based custom attributes.
type: string : A string of value 'development' or 'production' that will signal how engine should handle the pages.
type: function : A callback function that will be called on every page request with a express request that must return an object representing the context data to be passed to the template to be rendered on that page request.
type: object : object of sass options but only these are supported: indentWidth, precision, indentType, linefeed, sourceComments, includePaths, functions.
type: object : object of less options but only these are supported: strictUnits, insecure, paths, urlArgs, sourceComments, modifyVars, lint.
type: object : object of stylus options but only these are supported: paths.
type: object : object of postCSS options but only these are supported: plugins. It already uses atImport, postcssPresetEnv, autoprefixer, purgeCSS, cssnano and Discard Comments plugins by default.


The engine returns a Promise.

Usage Examples

How hp.config.js file may look like. Right now you must always export with module.exports.

// hp.config.js
const homePage = require('./website/data/');
const aboutPage = require('./website/data/');
const {CodeSnippet} = require('./website/tags/code-snippet');
const tailwind = require('tailwindcss');

const env = process.env.NODE_ENV || 'development';

module.exports = {
	staticData: {
		pages: {
			home: homePage,
			about: aboutPage,
	customTags: [
	postCSS: [
		plugins: [tailwind]

Providing static data to the engine.

engine(app, path.resolve(__dirname, './pages'), {
	staticData: {
		site: {
			version: "2.4.1",
			license: "MIT"

Providing a page request handler which inject context data to the templates.

engine(app, path.resolve(__dirname, './pages'), {
	onPageRequest: (req) => {
		return {
			path: req.path,
			params: req.params