HomeBlogTexas Ranger

Static Web Site Generator

Intro

Texas Ranger is a static web site generator written in TypeScript, built on node.js, dust.js, gray-matter and showdown.js. A few of its benefits are:

  • Flexible tagging: All files are indexed by freely defineable tags. They can be used to build index pages, tag clouds, menus and the like.
  • Components ("Partials") for social media: Facebook and Twitter
  • Components for including Open Graph meta-information on each page. Open Graph enables link preview-rendering in social media.
  • Provide perma-links for each page.

To see the tags in action, let's look at two samples:

There are three main components a Texas Ranger web-site is built from:

  • Templates provide the scaffold for a page, such as a landing page, a blog page, a news page. We use Dust.js as templating engine.
  • Markdown files provide the content that fills the template scaffold. We use Shodown.js for parsing and converting markdown.
  • Front matter are properties or meta-data within the markdown files. We use gray-matter for converting front matter to properties.

The markdown for the page you are just reading looks like this:

---
title: TexasRanger
subtitle: Static Web Site Generator
date: 2017-03-20
abstract: A static web site generator written in TypeScript, built on node.js, dust.js, gray-matter and showdown.js
tags: website, TexasRanger
authors: Thomas, Frank
template: texasRanger
---

#Intro
Texas Ranger is a static web site generator written in TypeScript, built on node.js, dust.js, gray-matter and showdown.js. Its main benefit is the flexible tagging: All files are indexed by freely defineable tags. They can be used to build index pages, tag clouds, menues and the like.

Let's look at two sample pages: 
* [Demo tag cloud or web-site index](/website/overview.html)
* [mindruptive blog](http://localhost:3000/blog/index/tags-all.html)

There are three main components a Texas Ranger web-site is built from:

* **_Templates_** provide the scaffold for a page, such as a landing page, a blog page, a news page.
We use [Dust.js](http://www.dustjs.com) as templating engine.
* **_Markdown files_** provide the content that fills the template scaffold.
We use [Shodown.js](https://github.com/showdownjs/showdown) for parsing and converting markdown.
* **_Front matter_** contains meta-data within the markdown files.
We use [gray-matter](https://github.com/jonschlinkert/gray-matter) for converting front matter to properties.
...

The first part between the three hyphens ("---") contains the front matter. The properties are freely defineable an can be used by template.

The template for this page looks like this:

<html>
<head> 
    {>html-head-partial/} 
</head> 

<body> 
    {>navbar-partial/} 
    <div class="intro-header">
        <div>
            <p>
                <img class="titleImage" style="-ms-interpolation-mode:bi-cubic; image-rendering:crisp-edges; vertical-align:middle" src="img/texasRanger.png" alt="">
            </p>
            <p class="subtitle">
                {file.properties.subtitle}
            </p>
        </div>
    </div> 
    <div class="container" style="margin-top:2.5rem">
        <div class="row">
            <div>
                {file.html|s} 
            </div>
        </div>
    </div>
    <br/> 
    {>social-footer-partial/}
</body>
</html>

The partials ({>html-head-partial/}, {>navbar-partial/}, {>social-footer-partial/}) are templates themselves. They enable re-use in templates. Please have a look at the Dust.js site for more details on the template syntax.

Objects and properties available for rendering - the Context In the sample above, objects and properties are referenced by {file.properties.subtitle} or {file.html|s} (|s is a Dust.js filter that prevents html-escaping the contents of the html property). The objects available for rendering are called context by the Dust.js templating engine. The context can contain any number of objects. To switch context, Dust.js uses so called sections, indicated by {#sectionName} ... {/sectionName}. Sections/contexts can be nested, e.g.:

{#texasRanger}
    {#indexes}
        This is the current index: {name}
    {/indexes}
    {#walkedFiles}
        This file was walked: {title}
    {/walkedFiles}
{/texasRanger}

The first section (texasRanger) sets the context to the texasRanger object. The second (indexes) accesses the indexes-collection of texasRanger and walkedFiles exposes the collection of walked files. From within the section, you can access properties like name and title.

For each rendered file, Texas Ranger exposes the following context objects:

  • file - it contains
    • all properties from the front matter, e.g. "file.properties.date", "file.properties.title", "file.properties.subtitle". All public members of texasRanger.File class can be used.
    • the original markdown: file.markdown
    • the transformed html: file.html
    • the path relative to the start-directory: file.htmlPath
  • texasRanger - Texas Ranger istself is exposed to the render process, too. The render process can for example access {texasRanger.indexes} or {texasRanger.walkedFiles}. All public members of texasRanger can be used.

Rendering collections Rendering collections is easy: In this sample, we create a dustjs section by {#files} ... {/files}. From within a section, we can access the properties of each iterated object.

<html>
<head>
    {>html-head-partial/}
</head>
<body>
    {>navbar-partial/} 
    {>blog-header/}
        <div class="articleListContainer">
            {>blog-tagValues-partial/}
            {#files}
                <div class="articleTeaser">
                    <a class="title" href="/{htmlPath}">{properties.title}</a>
                    <span class="date">{properties.date}</span>
                    <p class="subTitle">{properties.subtitle}</p>
                    <p class="abstract">{properties.abstract}</p>
                </div>
            {/files}
        </div>
    <br/> 
    {>social-footer-partial/}
</body>
</html>

How Texas Ranger builds the index Based on the index fields in the front matter of each file, Texas Ranger builds and index:

---
title: TexasRanger
subtitle: Static Web Site Generator
date: 2017-03-20
abstract: A static web site generator written in TypeScript, built on node.js, dust.js, gray-matter and showdown.js
tags: website, TexasRanger
authors: Thomas, Frank
template: texasRanger
---

Texas Ranger Index

The configuration file How does Texas Ranger know about the index fields? They are taken from the configuration file:

{
    "sourceDir": "texas-source",
    "destDir": "public",
    "templateDir": "./templates",
    "destFileExtension": "html",
    "defaultTemplate": "arcticle",
    "exclude": ["texas-source/demo1","texas-source/demo2"],
    "encoding": "UTF8",
    "clear": true,
    "fileTypesToProcess": [
        "md",
        "md2"
    ],
    "index": [
        {
            "index": "tags",
            "default": "all"
        },
        {
            "index": "authors"
        }
    ],
    "indexPagesConfig": [
        {
            "indexDir": "./texas-source/blog/index",
            "overview": "overview.md",
            "index": "index.md",
            "indexedFiles": "files.md"
        },
        {
            "indexDir": "./texas-source/webSite-index",
            "overview": "overview.md",
            "index": "index.md",
            "indexedFiles": "files.md"
        }
    ],
    "logLevel": "INFO"
}

The paths in the config file can be absolute or relative. If they are relative, they will be made absoulte by:

<directoryOfConfigFile> + '/' <relativePath>

The index section defines the indexes:

  • tags: Create an index on the field "tags": For each file, check if front matter contains "tags" and if so, add the file to the index. Default value, if no "tags" field found: "all"
  • authors: Create an index "authors"

The indexPagesConfig specifies the files or pages used for rendering the index.

  • indexDir: The location of the index files
  • overview: The overview page for indexes, see next chapter
  • index: The page being rendered for each index, e.g. "tags" or "authors", see next chapter
  • indexFiles: The page being rendered for each index value, e.g. "politics", "IT", "life style", see next chapter

Rendering index files For each input file, Texas Ranger generates an output file. The only exception are index files:

  • Index overview page: The overview page is rendered once per index configuration (see config-file above). Following objects are exposed to the page:
    • texasRanger: Texas Ranger itself
    • file: The file info from the overview file (e.g. overview.md)
    • htmlPath: The destination path of the generated page, relative to the starting point of generation
  • The index page: This page is rendered for each index, e.g. "tags" or "author". Following objects are exposed:
    • texasRanger
    • file: The file info from the index file (e.g. index.md)
    • index: The current index, e.g. "tags" or "author"; each index contains the index values, tags contains "politics", "life-style" and "IT", for example
    • htmlPath
  • The indexedFile page is rendered for each index value, e.g. "tags->politics", "tags->IT", "authors->thomas", "authors->frank". Following objects are exposed:
    • texasRanger
    • file: The file info from the indexedFiles file (e.g. files.md)
    • index
    • htmlPath
    • index value: The current index value

Each file knows which indexes it belongs to. It can be accessed by file.index which provides the method

file.index.has(indexName: string, value: string) 

For example:

file.index.has("tags", "politics")

checks if the file is indexed by index "tags" and index value "politics".

Advanced collection rendering

To make rendering easier, Texas Ranger provides so called Dust.js helpers. Helpers allow for extending Dust.js functionality. Let's take the tag-menu-bar from the mindruptive blog homepage, for example. It uses the @coll helper:

<div class="tagMenu">   
    {@coll collection=texasRanger.indexMap.tags.indexValues 
      sortBy="@value" 
      filter="return (current.toLowerCase() !== 'website') 
        && (current.toLowerCase() !== 'blog') "}
        <a href="./tags-{.}.html">
            <div class="tagMenuLink {@eq key="{.}" value="{indexValue}"}enabled{/eq}">
                {.}
            </div>
            </a>{/coll}
    <div style="clear:both"></div>
</div>

The @coll helper takes following arguments:

  • collection: The collection to iterate

  • sortBy (optional): The property to sort the collection by

    @value means: "Take the value of the current object"; useful for strings, e.g.

  • order (optional): "asc" or "desc", default == "asc"

  • filter: A JavaScript expression. The current object is passed as current. Texas Ranger will create a function from the expression and pass it to collection.filter().

Using the command-line interface

node <pathToTexasRanger>/texasRanger.js <pathToConfigFile>.json

The paths in the config file can be absolute or relative (as in sample above). If they are relative, they will be made absolute by:

<directoryOfConfigFile> + '/' <relativePath>