Installation

Installion of STExtended is very simple:

npm init @jlpenny/stextended dirname

cd dirname to enter the directory and get building!

If you are wanting to build a blog, we also offer a blogging template:

npm init @jlpenny/stextended dirname blog

To build your site, simply use:

npm run build

Now that you have a basic website, the first thing you should learn about STExtended is the object.yml file.

Configuration

Configuration of a website is stored in an objects.yml file. Here you can find various global configuration variables that are used by the processor for building your site. For basic sites, you just need to specify a base, which is your build folder. You should also specify source which specifies your source directory, otherwise it will assume the working directory! Then you have urlBase to specify the URL of the website when routing, and titleBase which is used to generate titles.

Advanced configuration

Macros

Macros are used to generate pages programatically. They will iterate through a specified configuration attribute and will pass a list of pages for each attribute valie to a specified layout. This is useful, for instance, if you wanted to generate a list of HTMLs that contained a list of articles in each category.

Perhaps the best way to illustrate this is using an example:

Let's say you had a blog with 3 categories, "stuff," "things," and "tidbits." You also want a page generated for each category which lists the blog posts.

First of all, simply specify the "category" attribute in your pages. Then, you'll want to create a layout page for the macro:

parent: /includes/layout.html
build: false
---
{{#each page.local}}
<div>
    <h3>{{this.config.title}}</h3>
    <p class='text-right'>
      <a href='{{route this.name}}'>
        <button>View Post</button>
      </a>
    </p>
</div>
{{/each}}

The pages are stored in page.local, and you can access all page configuration and contents there.

Finally, you specify the macro in the configuration file:

macros:
  - attribute: 'category'
    href: '/{{value}}/index.html'
    layout: '/includes/category.html'

Attribute specifies which configuration attribute to group by, href is the route to the page, and layout is the layout you wish to use.

Pagination

Macros can be paginated simply by adding a "paginate" directive to the macro configuration. This specifies the number of pages per page, you will also need to update your href directive:

macros:
  - attribute: 'category'
    paginate: 10
    href: '/{{value}}/index.html'
    layout: '/includes/category.html'

Sorting

Macros can also be sorted

macros:
  - attribute: 'category'
    paginate: 10
    href: '/{{value}}/index{{pagenum}}.html'
    layout: '/includes/category.html'
    sortby: date
    sortorder: desc

Generation

The static site generator will go through all HTML and MD (markdown) files to look for data to render. These files have two sections: a 'configuration' section at the top which uses YAML, and a content section below. A basic html file might look like:

---
layout: /includes/layout.html
title: Test title
---
<p>Hello world!</p>

Within the 3 dashes, you add configuration values. These can include title, the layout it should be using, and anything you like! These can be used to influence things like macros to generate content automatically, or you can even put variables in here that you can use in the page.

Templating language

The template language is very simple, using Handlebars.js, and mostly consists of putting inline javascript or in-built "helper statements" between {{}}.

Helpers

Apart from being able to embed plain ol' javascript, we've made some shortcuts to make it simpler for you:

  • route
    • Returns a full URL to the specified path.
    • Usage: {{route '/index.html'}}

Accessing config values

Configuration values can be accessed inline as such:

{{page.variablename}}

For example, for the title:

{{page.title}}

Iterating through other pages

If you are trying to do something like create a blog, you are able to query through your pages using the 'where' command. This goes through all rendered pages in the engine, and filters them. There is also a sort command to sort them how you like too! For example, if you wanted to grab all pages that have the config variable "blog: 1" by their date, simply use the example below:

Generation script

Generation (building) of a website is handled with a script called generate.js (or at least, it is when using the stextended init tool.) The one for this website looks like this:

const stextended = require('@jlpenny/stextended');

const { Builder, HTML, CSS } = stextended();

CSS.ProcessFile('./style.css');

Builder.all();

The Builder, HTML, and CSS objects contain classes related to the generation process. CSS is the CSS processor, HTML is the HTML parser, and Builder is the builder itself. You can find more information about these objects under the parser section.

Going Classless

STExtended allows you to write classless code, which basically means adding attributes which automatically get converted into CSS to your HTML tags. These are styled after bootstrap's tags. For example, if you want a margin, you can use m-x where x is the size in em which you want the margin.

<div m="2">This has a margin</div>

Apart from some built in attributes, you can also define any CSS property by putting a dollar symbol in front of the tag:

<div $background-color="#ccc">I have a grey background!</div>

The CSS file will automatically be processed and the attributes will be replaced with a class name. This way CSS is generated only for what you used.

Adding your own CSS and using SASS

You can add your own CSS to the processing system using the following: CSS.ProcessFile(filename);

This is to be put in your generation script, which is generate.js if you used the stextended init command. You can see this in action on the source code of this very website:

const { Builder, HTML, CSS } = STextended();

CSS.ProcessFile('./src/style.css');

Generate.all();

Registering your own HTML tag listeners

You can create your own HTML tag components into the parser using HTML.define as follows:

HTML.define('CustomClass', (name, attribs, content) => {
    return `<span class="${attribs.addClass('customClass').extract('class')}" ${attribs.toString(true)} $position="relative">${content}</span>`;
});

This will replace any instance of <CustomClass>text here</CustomClass> with the returned value, which would be <span class="customClass generated-position-relative-class">text here</span> in this case.