A flexible, configurable static website generator using pug (used to be called jade) templates.
This software ist still very much experimental and might break at any time. Use with caution.
- Why yet another static site generator
- Installation
- Usage
- CLI
- Overview
- Adding new content
- Using front-matter
- Configuration
- Built-in Processors
- Contributing
- License
First off let my try to explain why I wrote this tool and why it might be exactly what you are looking for.
I wanted something that I configure to my needs especially when it comes to optimizing the assets downloaded by the browser. The main reason for writing this tool is to allow the user to exactly define the scripts and styles downloaded by the browser for each and every page.
A further reason to write this tool was to allow its extention it without having to write npm modules for it. A plugin is a function and you can decide if want to use npm (or any other way) to publish it.
A further aspect which I think make the tool unique is that you as user can add new contents to it. Pages and posts are included by default but you can override those by adding your own plugin or you can define new contents by writing a plugin. Of course you can override any built-in pluging by defining your own.
The tool can be installed via npm. Be aware that the tool needs Node.js versions greater than 6.0.0. It might work with earlier versions especially if combined with babel but it was never tested with a version older than 6.0.0.
npm install -g flexi-site-gen
flexi-site-gen init
The command creates a dummy site in the directory in which it is called in. Make sure the directory is empty. If it is not empty no dummy site will be generated.
flexi-site-gen generate
Calling the above command in the directory with the dummy site will build it. The result will be in the public
folder. Now you can use any webserver to serve the website.
flexi-site-gen init
Creates a dummy site to help you get started with the tool.
flexi-site-gen generate
Generates the website writing all files into the directory specified with the publicDir
property. Per default the generated site can be found in the public
directory.
Flags
--prod
Calls all plugins with productionMode
set to true. The exact behavior of the generator depends on what each plugin does when the productionMode
property is set.
--cachebust
Calls all plugins with cacheBust
set to true. The exact behavior of the generator depends on what each plugin does when the cacheBust
property is set.
The tool differentiates between 5 types of processors, asset, content, decorator, file and template.
Assets: Assets are things you can use in page of the website but are not necessarily part of the content. Images, fonts, scripts and styles are considered assets.
Content: The contents of the content folder will be used to create individual pages for your site.
Decorator: Decorators are used to add more features to your content. For example the sitemap decorator can create a sitemap.xml file based on the pages and posts of your site.
File: Depending on the extension used for the content files, the needed file processor is called to parse the file.
Template: When you generate your site, the templates are used and define the final output for the individual html files. The content of each page, posts etc. are combined with a template and this is what is later wrote as an html page. Pug (used to be called jade) is used per default to process the templates. The selected template processor depends on the template file extension.
generator_config.yml: Configuration file for the tool. Here you can configure, for example, the active processors. The configuration file is written in YAML.
This section shows you the ways you can write new content for your webpage. What is written here holds true for the built-in contents (pages/posts). 3rd-Party content plugins might allow other ways of writing content.
The easiest way to add new content, is to add an HTML or Markdown file into content->pages or content->posts. Each page has (optional) meta data and content. During generation you can use those in your templates. When using an individual file for your content you can use front-matter to define the meta data (see about.html and index.html). Any meta data you define, can be used in the templates. Anything below the front-matter is considered to be the contents of the page.
Page (uses the html file processor):
---
title: 'Dummy page'
---
<p>Page contents</p>
Template (uses the pug template processor):
doctype html
html(lang="en")
head
title=current.meta.title
body
h1=current.meta.title
!{current.content.content}
current
is the current page, post etc.current.meta
is an object containing all front-matter attributescurrent.content
is an object with all content. Per default you can usecurrent.content.content
to access the actual content. If sections are used thencurrent.content
contains the section names as properties
Result:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Dummy page</title>
</head>
<body>
<h1>Dummy page</h1>
<p>Page contents</p>
</body>
</html>
Front-matter is a block of YAML at the beginning of a file. To parse it, the generator uses front-matter npm package.
When you add new content by using only one file (front-matter is not supported if you use multiple files for a page, see blog1), you can use front-matter to define meta data for each file. Meta data are optional but there might be content processors requiring some specific meta data attributes. For example the page content processor can use the url
attribute in the meta data to define the URL and the output path for the page.
---
title: 'Foo bar'
---
It is important to place your front-matter attributes between ---
and ---
and it needs to be the first thing in the file.
The configuration for the tool can be found in the generator_config.yml
file.
There are different levels of configuration each with a different priority. The different levels are:
- configuration for a processor type and a concrete processor in generator_config.yml, for example for the markdown file processor
- common configuration for a processor type in generator_config.yml, for example for all file processors
- common configuration for all processors in generator_config.yml
The order above also defines the precedence order for the built-in processors. If for example you define the same key in the common configuration and the configuration for the processor, then the value of the processor configuration will be used.
Please note that in the end each processor defines which options it will use, if it will consider options set in individual content files (meta data) and if it wants to honor the precedence order defined here. Also the processor defines how exactly the keys of the various configuration levels will be merged if the key points to an object or array.
In some cases you can also configure a processor using the meta data of a file. As above the processor defines what it does with the data. See each processor for more information on how it handles the configuration.
Default configuration file
common:
outputDir: 'public' # Directory in which the site will be generated
template:
common:
path: 'templates' # Path where the templates are saved
pug:
pretty: true
asset:
common:
path: 'assets' # path to assets
styles:
postcss:
plugins:
prod:
- name: 'cssnano' # Used during production mode to minify the CSS
content:
common:
path: 'content' # path to content
global:
urlWithExtension: false # Don't use .html as extension for the URLs
htmlmin: # Used during production mode to minify the HTML
removeAttributeQuotes: true
collapseWhitespace: true
posts:
urlFormat: ':year/:month/:day/:name/'
The file processors are called for each file in the content folders. Which processor is called depends on the extension of the file. You can configure the file processor in the generator configuration file. Use file
as key (see below for examples for the individual file processor). File processors are activated by default.
Built-in file processors:
- html
- json
- markdown
- yaml
Below you can find more information about the individual file processors.
- Extensions: .html, .htm
- Front-Matter support: true
- Sections support: true
- Section splitter: <!---SectionName---->
- Configuration: N/A
- Extensions: .json
- Front-Matter support: false
- Sections support: false
- Configuration: N/A
- Extensions: .markdown, .mdown, .mkdn, .md, .mkd, .mdwn, .mdtxt, .mdtext
- Front-Matter support: true
- Sections support: true
- Section splitter: ---SectionName---
- Uses markdown-it
- Configuration: you can use the options allowed by markdown-it
- The
highlight
option is not supported. UsecodeHighlight: true
instead. When this is used highlighting will be enabled using highlight.js
- The
- Default configuration: The default configuration for markdown-it
file:
markdown:
html: true
codeHighlight: true
- Extensions: .yaml, .yml
- Front-Matter support: false
- Sections support: false
- Uses: js-yaml with
safeLoad
- Configuration: currently not supported
The template processors are used for the files in the templates folder. Depending on the extension the correct processor is called. You can use the template
key to configure them. Template processors are activated by default.
template:
common:
path: 'templates' # Path where the templates are saved
Built-in template processors:
- pug (used to be jade)
- Extensions: .pug
- Uses: pug
- Configuration: you can use the options allowed by pug
- The
filename
option is automatically set
- The
- Default configuration:
pretty: true
template:
pug:
pretty: true
The content processors are used for files/folders in the content folder. Content processors are deactivated by default and need to be explicitly activated using the active
key in the configuration file.
content:
active: # Activate the pages and posts content processors
- 'pages'
- 'posts'
common:
path: 'content' # Path where the contents are saved
Built-in content processors:
- pages
- posts
Can be used to create individual pages. Uses htmlmin in production mode for minification.
generator_config.yml
content:
pages:
meta: # Meta data object to be used for all pages
- title: '' # Meta data of individual pages can override this
template: 'page' # The template for all pages
scripts: # scripts for all pages. Requires the scripts asset processor
- name: 'script.js'
opts: # Options for this script
async: true
styles: # styles for all pages. Requires the styles asset processor
- name: 'styles.css'
opts: # Options for this style
media: 'all'
htmlmin: # HTML minification for production mode. You can specify more htmlmin options if you want
removeAttributeQuotes: true,
collapseWhitespace: true,
File meta data
- url: Defines the URL to be used for the page. If not specified the file/folder name is used
- template: Defines the template to be used for this page. If not specified the
template
in generator_config.yml is used - scripts: Same format as above. If scripts are defined in the configuration file and the meta data then those are concatenated. If the same name is defined multiple times, the last definition is used
- styles: Same format as above. If styles are defined in the configuration file and the meta data then those are concatenated. If the same name is defined multiple times, the last definition is used
- current: Object for the current page
- content: Object containing the page content. If no sections are used
content.content
is all content of the file minus meta data - meta: Object containing all page meta data
- styles: Array of all styles for this page
- scripts: Array of all the scripts for this page
- content: Object containing the page content. If no sections are used
- pages: Array containing all pages. Each page is an object with the same attributes as
current
Can be used to create a blog. The main blog page is defined using pages
and the individual blog posts are defined using the posts
content processor. Uses htmlmin in production mode for minification.
generator_config.yml
content:
posts:
meta: # Meta data object to be used for all posts
- title: '' # Meta data of individual posts can override this
template: 'post' # The template for all posts
scripts: # scripts for all posts. Requires the scripts asset processor
- name: 'script.js'
opts: # Options for this script
async: true
styles: # styles for all posts. Requires the styles asset processor
- name: 'styles.css'
opts: # Options for this style
media: 'all'
htmlmin: # HTML minification for production mode. You can specify more htmlmin options if you want
removeAttributeQuotes: true,
collapseWhitespace: true,
urlFormat: '_blog/${year}/${month}/${date}/' # Uses the date in the meta data to create a url in this format. Values within ${...} will be replaced by numbers given in the date key in the post's meta data
dateFormat: 'DD-MM-YYYY' # used to parse the date key in the posts meta data
File meta data
- url: Defines the URL to be used for the post. If not specified the file/folder name is used. The
urlFormat
value is used a prefix for the URL - date: It is used to define the URL and is a required attribute. Its format is specified by the
dateFormat
key. Posts are also sorted according to this date (newest first) - template: Defines the template to be used for this post. If not specified the
template
in generator_config.yml is used - scripts: Same format as above. If scripts are defined in the configuration file and the meta data then those are concatenated. If the same name is defined multiple times, the last definition is used
- styles: Same format as above. If styles are defined in the configuration file and the meta data then those are concatenated. If the same name is defined multiple times, the last definition is used
- current: Object for the current post
- content: Object containing the page content. If no sections are used
content.content
is all content of the file minus meta data - meta: Object containing all post meta data
- styles: Array of all styles for this post (only if styles is defined in the configuration or meta)
- scripts: Array of all the scripts for this post (only if scripts is defined in the configuration or meta)
- content: Object containing the page content. If no sections are used
- posts: Array containing all posts. Each post is an object with the same attributes as
current
The asset processors are used for files/folders in the assets folder. Asset processors are deactivated by default and need to be explicitly activated using the active
key in the configuration file.
asset:
active: # Activate the styles and scripts asset processors
- 'styles'
- 'scripts'
common:
path: 'assets' # Path where the assets are saved
Built-in asset processors:
- images
- scripts
- styles
- fonts
Is used to copy images from the assets directory to the output directory. Uses imagemin in production mode to minify/optimize the images. You have to configure which minification plugin is used for each extension. Read the imagemin documentation for information on which plugins are available. The plugin must be installed using npm and it will automatically be loaded by the tool. All imagemin plugins use imagemin-pluginName
as package name. You need to just use pluginName
the imagemin-
prefix is automatically added.
generator_config.yml
asset:
images:
minify: # imagemin configuration
'.png': # config for all .png images
plugin: 'optipng' # plugin to be used imagemin-optipng
opts: # options for imagemin-optipng
optimizationLevel: 3
Is used to copy fonts from the assets directory to the output directory. Currently not configurable.
It uses PostCSS to process the CSS files. You can define which PostCSS plugins to use. The plugins need to be installed via npm. An exception is cssnano
which is installed by default and used in production mode to minify the CSS. The plugins will automatically be loaded by the tool. There is also support for the following PostCSS options: syntax
, parser
and stringifier
. For those you can give the name of the PostCSS plugin and it will be automatically loaded by the tool. You would have to install the plugin via npm. Supports the --cachebust
flag.
generator_config.yml
asset:
styles:
postcss:
plugins: # PostCSS plugins
prod: # Plugins for production mode
- name: 'autoprefixer' # Use the autoprefixer plugin
opts: # Options for autoprefixer
browsers:
- 'last 2 versions'
- 'cssnano'
dev:
- name: 'autoprefixer'
opts: # Options for PostCSS. Supported are syntax, parser, stringifier
parser: 'postcss-scss' # Use postcss-scss as parser
bundles: # Define bundle with name common.css. The bundle can be used in the styles of pages/posts
- name: 'common.css'
files: # Files to be concatenated to create the bundle
- '00-normalize.css'
- '01-grid.css'
- styles: Array containing all style objects
It uses Terser to minify the scripts in production mode. Supports the --cachebust
flag.
generator_config.yml
asset:
scripts:
minify: # Terser options
mangle: true
bundles: # Define bundle with name common.js. The bundle can be used in the scripts of pages/posts
- name: 'common.js'
files: # Files to be concatenated to create the bundle
- 'index.js'
- 'menu.js'
- scripts: Array containing all script objects
The decorators are used to extend and manipulate your contents. Decorators are deactivated by default and need to be explicitly activated using the active
key in the configuration file.
generator_config.yml
decorator:
active: # Activate the tags decorator
- 'tags'
Built-in decorators:
- tags
- sitemap
- pagination
Reads the tags key out of the meta data for the defined content type (createTagsFor
array) and create a template variable named tags
which is an object with keys matching the names in createTagsFor
. Each key is an array containing the relevant content. It also extends the tags of the meta data and adds a URL to them. An example of tags usage can be found in ./e2e_test/test_site.
Currently all the tags must be in one page with name tags.html
. Each tag is referenced using tags#TagID
or tags.html#TagID
as a URL depending on the urlWithExtensions
config value.
generator_config.yml
decorator:
tags:
createTagsFor:
- 'posts'
File meta data
- tags: Tags for an individual page/post etc.
- tags: Object containing all keys defined in
createTagsFor
. Each key has an array as value containing tag objects withlabel
,url
,id
andcontents
.contents
is an array with all elements which have the tag. If for examplecreateTagsFor
was used with "posts", then thecontents
array will contain all the posts with the given tag. - The tags-Array in the meta data of content used with
createTagsFor
are replaced with an object containinglabel
andurl
for each tag.
Creates a sitemap.xml file for your site.
generator_config.yml
decorator:
sitemap:
domain: 'http://localhost:8080' # domain to be used in the sitemap
createMapFor: # the content which should be added to the sitemap
- 'pages'
- 'posts'
File meta data
- sitemap: Object with the following keys:
priority
,lastModified
(lastmod),changeFrequency
(changefreq) andexclude
. In caseexclude
is true, then the page will not be added to the sitemap. Check sitemaps.org for the values allowed for the keys.
With this decorator you can for example paginate posts. For this to work you need to define the content to paginate an a page (paginationPage
key) in which to write the content. The decorator will automatically create copies of the page and each copy will receive a number of content depending on contentPerPage
. An example of pagination usage can be found in ./e2e_test/test_site.
generator_config.yml
decorator:
pagination:
paginate: # Array with the contents to paginate
- contentToPaginate: 'posts' # content type to paginate (pages cannot be paginated)
paginationPage: 'blog' # template to be used for the pagination contents
contentPerPage: 5 # the number of content files per page
This decorator creates an object called pagination
and is within the current
template variable. It contains the following attributes:
- controls: An array of objects with keys:
label
,isActive
,url
. Thelabel
is the page number,isActive
informs us on which pagination page we are and theurl
can be used to navigate to a specific page - currentPageNumber: The number of the current page
- numOfPages: Total number of pagination pages
- [contents to paginate]: This key changes depending on
contentToPaginate
, ifcontentToPaginate
isposts
, then this key will also beposts
. The key always contains an array with as many content objects ascontentPerPage
. The last page might have less.
Content processors, asset processors and decorators are all considered plugins.
Besides the internal plugins which are part of the generator, you can create your own plugins.
In order to use one of your plugins, you have to give its path to the plugins
property in the generator's config file.
If you define a plugin having the same name as an internal plugin then yours will take precedence.
- Add more unit tests
- Add more documentation
If you feel you can help in any way, be it with documentation, examples, extra testing, or new features please open an issue or pull request. If you have any questions feel free to open an issue with your question.