npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

buildkit-server

v0.1.14

Published

Easily create pages from templates and blocks

Downloads

11

Readme

Buildkit Server

The term 'buildkit' is used to refer to the suite of pages, templates and other assets that you end up handing over to the back-end guys for the integration into your main application (often driven via .net or Java for example).

The aim of this project is to speed up the creation of a modular based HTML framework. The project does not include any CSS pre-compilers or client-side JavaScript libraries. It is purely focused on the rendering of HTML page.

All functionality is made available via a nodejs package.

Getting started

This guide assume that you've initialised a blank nodejs project with npm init, though of course you could easily add this to an existing project.

  1. Install the buildkit-server module with npm install buildkit-server --save

  2. Create a new JavaScript file, say buildkit.js, and add paste in the following code (take specific note of the '.default' part after the require call):

    var BuildkitServer = require('buildkit-server').default;
    var buildkit = new BuildkitServer();
       
    buildkit.start();
  3. On the command line, run node buildkit. After doing this you should see some text printed to the console telling you that the buildkit server has been started and is available at http://localhost:9000/

  4. Open http://localhost:9000/ in your browser and you should see a message telling you that no documentation system is installed

  5. That's it. Your server is up and running. You can now start creating blocks, templates and pages which will then be visible through the server.

Creating our first page

First, a quick bit on terminology. There are three concepts that its key to understand, and which will likely be very familiar to you as a UI developer:

Templates - you may know these are 'layouts'. They are the skeleton HTML that defines the different types of layout that you have. You may for example have a template which is a one column template, and another which maybe has a sidebar and a content area. I've often found that I end up with no more than 5 or 6 templates for even the biggest of sites. Your needs may be different though. Templates are defined as a Handlebars file. Blocks - these are just a chunks of HTML. You might call them components, modules, whatever you like. The term 'blocks' is just something that I used on a previous project and stuck with me. The same block might be used many times, or just once. It's up to you. Pages - pages are defined simply as a JSON file. This JSON file allows you to specify the template that the page uses, and the blocks that you want to drop on to your page. The approach of using a JSON file ensures that all markup is kept as separate blocks, so that you don't have random bits of HTML scattered all over the place.

Create a block

By default, all blocks must be stored in a folder named blocks that should sit in the root of your project (where your package.json is defined). That folder won't be created automatically, so go ahead and create it, then create another folder inside it and call it menu

Inside this menu folder, create two more files, one called block.json and the other called menu.hbs

So now, among perhaps other things, you'll have the following folder structure:

\blocks
    \menu
        menu.hbs
        block.json

You can leave the block.json file empty for now, but lets add a little bit of markup to the menu.hbs file:

<ul>
    <li><a href="#">Electricals</a></li>
    <li><a href="#">Homeware</a></li>
    <li><a href="#">Kids</a></li>
    <li><a href="#">Luxury</a></li>
    <li><a href="#">Books</a></li>
</ul>

It's important that both block.json and menu.hbs exist, as the block won't work without them. Every block must have it's own folder, and in that folder must be a block.json file and also an hbs file whose name matches the name of the directory.

Create a template

By default, all templates must be stored in a folder named templates that should sit in the root of your project. The templates folder won't be created automatically, so please create it. Then, inside the templates folder, go ahead and create a file called one-column.hbs and add the following content to it:

<!doctype html>
<html class="no-js" lang="en-gb">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        {{{content}}}
    </body>
</html>

They key thing to notice in that template is the {{{content}}} expression. If you've used Handlebars before then you'll probably recognise this a simple identifier expression. The triple curly braces mean that any content we pass it are not escaped. This identifier will end up containing the output of our blocks, and so it's important that the markup isn't escaped. I call these identifiers template areas. So in this case we have just created a template area named 'content'.

Create a page

This is where we combine the template and block that we've just created.

By default, all pages must be stored in a folder named pages that should sit in the root of your project. The pages folder won't be created automatically, so please create it. Then, inside the pages folder, create a file called my-first-page.json and add the following content to it:

{
    "templateName": "one-column",
    "templateContent": {
        "content": [
            {
                "blockName": "menu"
            }
        ]
    }
}

Hopefully that makes sense. First of all it says that we want to use the 'one-column' template via the templateName property. The templateContent property is an object, where each property should map to a template area defined in the 'one-column' template (remember our template contained the {{{content}}} identifier)

Lets view the page

Now you should be able to go to http://localhost:9000/documentation/pages and see the page listed. Click on the link and you should see your new page.

Driving block content via JSON files

The idea of a block is that it's a re-usable chunk of HTML. It doesn't absolutely have to be re-usable, but in general you'll probably want it to be. For example, you may have an ecommerce site where you have the multiple marketing banners, all using the same markup, just with different content. Perhaps you have some menu markup that you re-use in different places. Other times there might just be one block, like a site footer for example, where you don't ever expect it to be used more than once. We can cater for changing content in all of these scenarios, without having to change the markup.

Continue through the sections below to find out more...

Adding another copy of the same block

Lets say that for some reason, we want two of those menu blocks on our page. We can edit the my-first-page.json file and add it in again:

{
    "templateName": "one-column",
    "templateContent": {
        "content": [
            {
                "blockName": "menu"
            },
            {
                "blockName": "menu"
            }
        ]
    }
}

Refresh the page and unsurprisingly you'll see another menu.

Adding block data via the page JSON file

Now what if you want to have the same sort of markup, in other words you want to keep the HTML pattern, but you want to have different content for the menu? You can do that by using the blockData property. Take a look at the example below:

{
    "templateName": "one-column",
    "templateContent": {
        "content": [
            {
                "blockName": "menu",
                "blockData": {
                    "items": [
                        {
                            "label": "Electricals",
                            "url": "#"
                        },
                        {
                            "label": "Homeware",
                            "url": "#"
                        },
                        {
                            "label": "Kids",
                            "url": "#"
                        },
                        {
                            "label": "Luxury",
                            "url": "#"
                        },
                        {
                            "label": "Booke",
                            "url": "#"
                        }
                    ]
                }
            },
            {
                "blockName": "menu",
                "blockData": {
                    "items": [
                        {
                            "label": "Login",
                            "url": "#"
                        },
                        {
                            "label": "Register",
                            "url": "#"
                        },
                        {
                            "label": "My Account",
                            "url": "#"
                        }
                    ]
                }
            }
        ]
    }
}

Add this to your current page JSON file and refresh the page. Nothing will happen, because of course we still have our menu hardcoded.

Bear in mind that the blockData property must be an object, and is passed in as-is to the block's Handlebars file (menu.hbs). It will be available via an identifier called content. So we can update our hbs file to loop over this array of menu items like so:

<ul>
    {{#each content.items}}
        <li><a href="{{url}}">{{label}}</a></li>
    {{/each}}
</ul>

Now refresh your page and you should have two of the same menu structures, but with different data.

Adding block data via the block JSON file

If your menu almost always contains the same data, then you may not want to have to define it on every page that uses it. Instead you probably just want to be able to define the default data and then be able to override it when you need to. You can do this by adding content in to the block.json file within the menu block folder. Open that JSON file and add the following content:

{
    "content": {
        "items": [
            {
                "label": "Electricals",
                "url": "#"
            },
            {
                "label": "Homeware",
                "url": "#"
            },
            {
                "label": "Kids",
                "url": "#"
            },
            {
                "label": "Luxury",
                "url": "#"
            },
            {
                "label": "Booke",
                "url": "#"
            }
        ]
    }
}

After updating block.json with the content above and then remove the blockData property from the first block defined in the my-first-page.json

Refresh the page and you should find that nothing's changed visually. However, we're now getting the content for the first menu block from the menu block's block.json file, while the second menu block has it's content overriden at the page level.

Overriding properties

Lets add a title to our menu. In our menu's block.json file we can just add it in as another property on the content object:

{
    "content": {
        "title": "Categories",
        "items": [
            {
                "label": "Electricals",
                "url": "#"
            },
            {
                "label": "Homeware",
                "url": "#"
            },
            {
                "label": "Kids",
                "url": "#"
            },
            {
                "label": "Luxury",
                "url": "#"
            },
            {
                "label": "Booke",
                "url": "#"
            }
        ]
    }
}

Then we just update out menu block's hbs file to refer to this:

<h3>{{content.title}}</h3>

<ul>
    {{#each content.items}}
        <li><a href="{{url}}">{{label}}</a></li>
    {{/each}}
</ul>

Refresh the page and you should now see that both menu's have the new title. For the second menu, it's using the items from the page level content, while still using the title from the block level content. The system merges the block's content property and the page's blockData property together before passing the merged object to the block's hbs via the content object.

Adding another template area

Create another template under the templates folder and name it two-column.hbs, and add in the following content:

<!doctype html>
<html class="no-js" lang="en-gb">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <main>
            {{{content}}}
        </main>
        
        <aside>
            {{{subcontent}}}
        </aside>
    </body>
</html>

Notice that this is very similar to our previous template, but we have an additional template area named subcontent. Now create another page called my-second-page.hbs and tell it to use this new template. Then add a menu block in to both template areas. You should have a page JSON file that looks something like this (feel free to add a 'blockData' property and play around with the content):

{
    "templateName": "two-column",
    "templateContent": {
        "content": [
            {
                "blockName": "menu"
            }
        ],
        "subcontent": [
            {
                "blockName": "menu"
            }
        ]
    }
}

View this page via http://localhost:9000/pages/my-second-page and you should see that you have a menu instance in each of the template areas.

Passing page data to a template

The properties templateName and templateContent are both special. You can however add any other variables that you like to this object. Those properties will then be made available to the template that the page uses, via the page object.

So lets say that I have a page JSON file that looks like this:

{
  "title": "My Page Title",
  "templateName": "two-column",
  "templateContent": {
      "content": [
          {
              "blockName": "menu"
          }
      ],
      "subcontent": [
          {
              "blockName": "menu"
          }
      ]
  }
}

You could then use this in a template like this:

<!doctype html>
<html class="no-js" lang="en-gb">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>{{page.title}}</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <main>
            {{{content}}}
        </main>
        
        <aside>
            {{{subcontent}}}
        </aside>
    </body>
</html>

Using Handlebars partials in your blocks

If your block contains a lot of markup then you might want to break it down in to separate Handlebars partials. To use a partial, you need to first of all create a folder named partials within your block folder. Any .hbs files that you create in here will then be registered as partials for use by the block who's folder they're located in.

For example, lets say that we want to use a partial to represent a sub-menu for our menu block. Our block folder would look like this:

\blocks
  \menu
    menu.hbs
    block.json
    \partials
      submenu.hbs

Note that we now have a partials folder, and within that we have a submenu.hbs file, which is our submenu partial.

To use that submenu partial inside menu.hbs, you would simply use the standard Handlebars partial syntax, so our menu block might look like this:

<h3>{{content.title}}</h3>

<ul>
    {{#each content.items}}
        <li>
            <a href="{{url}}">{{label}}</a>
            {{> submenu}}            
        </li>
    {{/each}}
</ul>

You can see the submenu partial being used within the each loop.

Adding partials sub-folders

Inside the partials folder you can add sub-folders, nested to any level you like. Lets say we add an additional folder so our new block structure looks like this:

\blocks
  \menu
    menu.hbs
    block.json
    \partials
      submenu.hbs
      \tertiary
        submenu.hbs

Here you can see that we've added another submenu.hbs file under the tertiary folder. We can access this partial using the following syntax:

{{> tertiary/submenu}}            

So we really just remove the .hbs file the partial filename, and separate folders with a forward slash.

Where partials can be used

Partials defined within a block folder can only be used within that block's Handlebars file, or within any of the other partials for that particular block. Partials will only work within the block under which they were defined. For example, if we have a menu block, and maybe a header block, then partials defined under the menu block will not be available within the header block.

Creating your own documentation

When you go to http://localhost:9000/ you are seeing a basic UI which is powered by a few Handlebars files within the documentation folder which you'll find under the node_modules/buildkit-server folder. At the moment there's little more than a basic listing of pages, templates and blocks.

You may want to create your own documentation. To do this it's easiest to take a copy of the documentation folder described above and place it in your project directory.

The Handlebars files that you have in the documentation folder are accessible via the same name, but minus the '.hbs' extension. So the file pages.hbs is available via http://localhost:9000/documentation/pages. You can create as many additional files and folders as you like. So if you wanted to document your menu block, you could create a folder called blocks and then a file called menu.hbs under this. You can then access that file via http://localhost:9000/documentation/blocks/menu

You'll see that your new pages will always have the same template and style. This is because they're always wrapped in the _template.hbs file before being rendered. At the moment this is hardcoded and can't be changed. The _template.hbs file must sit in under the documentation folder otherwise you'll receive an error.

If you add an index.hbs folder to any folder or sub-folder within documentation, this will be used without having to specify the filename in the URL.

Using blocks in your documentation

You can use blocks in your documentation by adding a Handlebars partial call, the same way as you would include a nested block. For example, to render our menu block you can use the following Handlebars partial in any of your documentation:

{{> menu}}

As your documentation probably doesn't contain styles to make your menu look how it should, you're much more likely to want to print out the raw HTML of the block, so that you can talk about this in your documentation. To do this you can use the following code:

{{raw "block:menu"}}

Note the double quotes around the name. It won't work without them. We're prefixing the block name here with 'block:' to indicate that we want to output a block. You could also use a template using the code below:

{{raw "template:one-column"}}

or you can output page:

{{raw "page:my-first-page"}}

I tend to then use something like https://highlightjs.org/ for highlighting code fragments.

Global variables in Handlebars

It can be handy to be able to set global variables that can be referenced in any Handlebars template. This is where the buildkit.json file comes in. If this file exists in your base directory, the buildkit server reads it (it must be a valid JSON object) and exposes it as an object named site.

Here's a simple example:

{
	"title": "My Example Site",
	"enableAdobeAnalytics": false
}

Here you can see I've defined two variables that I might then use in my template files, like this for example:

<!doctype html>
<html class="no-js" lang="en-gb">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>{{site.title}}</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <main>
            {{{content}}}
        </main>
        
        <aside>
            {{{subcontent}}}
        </aside>
        
        {{#if site.enableAdobeAnalytics}}
            <script src="/path/to/adobeAnalytics.js"></script>
        {{/if}}
    </body>
</html>

The title property is used to set the value of the title element, while the enableAdobeAnalytics is used to determine whether or not to include some analytics file. There are obviously many ways that you could use variables like this.

Accessing page properties from a template

You may want to access variables in a template, that are defined for a page. The entire JSON object that you create for a page is available within a template through the page object. For example, here we use the page's title property in the title element:

<title>{{page.title}} - {{site.title}}</title>

Mocking responses

Lets say that you have a block that represents your product information. In that block you have an 'Add to basket' button. When the user clicks on this link you want the request to go off to a URL where you can return a JSON object representing the success or failure response. This is where mocks come in.

By default, mocks are stored in a directory call mocks. You create a mock file by simply creating a nodejs module that exports a function called serve. This function takes two parameters: req and res. These are the http.IncomingMessage and http.ServerResponse respectively.

Say I create a file called add-to-basket.js under the mocks folder:

exports.serve = function(req, res) {
	
	res.statusCode = 200;
	res.setHeader('Content-type', 'application/json');
	res.end(JSON.stringify({
		success: true,
		items: 3,
		total: 124.43
	}));
};

This mock is then available by going to http://localhost:9000/mocks/add-to-basket

You can create as many mocks as you like, and nest them to whatever depth you like.

If you create a mock, you are entirely responsible for the output of that request. The buildkit server takes no other action after handing the request off to your mock code. You must for example, set the status code, content type, write the content that you want to return, and most importantly, remember to call res.end().

Options

When you create an instance of the BuildkitServer class, you can pass in a number of different parameters:

port

default: 9000

The port on which the server should run.

logLevel

default: info

The level of logging that you want output to the console. Must be one of: log, trace, debug, info, warn, error

baseDir

default: current directory

The directory in which the following folders/files are expected to be located:

  • blocks (folder)
  • templates (folder)
  • pages (folder)
  • buildkit.json (file - can be changed with siteConfigPath option)

Can be absolute to relative to the executing script.

siteConfigPath

default: buildkit.json

Path to the site configuration file. The path is relative to the baseDir setting.

blockConfigFilename

default: block.json

The name of the file that exists within each block folder, which contains the JSON configuration for that particular block.

debug

default: true

Whether to output debug information in the HTML when rendering blocks, pages and templates.