• Stars
    star
    224
  • Rank 177,108 (Top 4 %)
  • Language
    JavaScript
  • Created almost 10 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Render static blog sites from Markdown using Ghost themes

ghost-render

Renders static blog sites from Markdown using Ghost themes.

Features

  • Renders a blog from Markdown using themes designed for Ghost.
  • Beautiful themes from Ghost; rather than reinventing the wheel when it comes to writing layouts and helpers, you can get productive immediately with all the Ghost theme features.
  • Completely static output is easy to host anywhere.
  • Generates:
    • individual blog post pages
    • blog home page: a paginated post list ordered by post date
    • author pages: a paginated post list by author and post date
    • tag pages: a paginated post list by tag and post date
  • Static pages
  • RSS feeds for the blog, for each author and for each tag
  • Supports multiple authors
  • Supports syntax highlighting via highlight.js

Changelog

v.1.1.0: added a missing dependency to package.json

Getting started

Install ghost-render:

npm install -g ghost-render

Initialize a basic settings JSON file (mostly for Ghost-specific settings):

ghost-render --init > settings.json

Download a Ghost theme. There are a number of free and paid Ghost themes - I'll use the default Casper theme from Ghost here:

git clone https://github.com/TryGhost/Casper.git

Create some content:

mkdir -p ./blog/2014/01/30/
echo "# Hello world\n YOLO" > ./blog/2014/01/30/hello-world.md

Run ghost-render:

ghost-render --input ./blog/ --settings ./settings.json --theme ./Casper --output ./tmp 

Open up the result in a browser (mostly because of file:// URL security issues). Any HTTP server will work, I'll use the one built into Python:

cd ./tmp
python -m SimpleHTTPServer 5000

All done; here's a screenshot of the result:

screenshot

The resulting output

The output HTML is fully static. This means that you could, for example, point a HTTP server at the output folder and be done with it or push the output folder to Amazon S3.

For example, here is how I deploy my own blog: aws s3 sync ./output/ s3://some-s3-bucket/some-folder/ --delete --exclude "node_modules/*" --exclude ".git" (assuming credentials are in the necessary environment variables and that the AWS CLI is installed).

Adding metadata to each post

Each blog post can have metadata associated with it. To set the metadata, start your markdown file with a metadata block that looks like this:

---
title: Page title
published_at: 2014-01-30 11:26:04
author: foo
tags: foo bar
---

All of the post metadata is optional - see below for the fallback values.

You can also write HTML inside the posts and it will be included in the rendered page as-is. This can be handy for those moments where you need to produce very specific markup.

title

title is used as the page title in the templates and the <title> tag.

The first heading in the markdown file is used if title is not set. If there is no first heading, the file name is used (without the file extension).

published_at

published_at determines the sorting order for posts, and it is used in the index templates to show when a post was made.

If published_at is not set, it is detected from the file path. If your file paths contain numbers that look like yyyy-mm-dd, then you don't need to explicitly set the published_at metadata value.

Some examples of acceptable paths which are automatically parsed if the published_at value is missing:

  • 2014/01/30/hello-world.md (yyyy-mm-dd)
  • 2014-01-30-hello.md (yyyy-mm-dd)
  • 30/01/2014/hello.md (dd-mm-yyyy)
  • 30-01-2014-hello.md (dd-mm-yyyy)

If this detection fails to produce a result, the creation time of the file is used.

author

author refers to an author by their short name (slug). It is used to fetch the author's metadata from the settings.json file, which will then be rendered by most templates as an author blurb somewhere in the page.

A per-author index of posts is generated for each author at /author/:slug/ (e.g. /author/mixu/). An associated RSS feed is also generated at /author/:slug/rss/. You can set the slug in settings.json.

If you leave the field blank, the author with the name default will be used (e.g. authors.default in settings.json).

tags

tags contains a space-separated (foo bar) or comma-separated (foo, bar) list of tags. Each tag becomes a paginated collection of posts grouped by tag and sorted by published_at. The tag URL slug is generated by lowercasing the tag name and then replacing all non-alphanumeric characters with -.

A per-tag index of posts is generated for each tag at /tag/:slug/ (e.g. /tag/linux/).

If you leave the field blank, there are no tags and no tag index pages.

How post and page URLs are determined

One of the things I dislike about most static site generators is how fiddly it is to get the exact URL structure you want. ghost-render is WYSIWYG in that what you have in your input directory structure is what you get in the output directory structure.

The only change is that .md files will have their extension changed to .html. Other than that, you can implement any directory structure you want, and the generated index pages will adjust to contain the correct URLs to your posts.

You can include non-markdown files. They are automatically copied into the output directory, maintaining the same relative paths.

BTW, if you want /2014/01/30/post-name to work as a link, name your markdown file /2014/01/30/post-name/index.md. This will become /2014/01/30/post-name/index.html, which makes pretty links work as expected.

Indexes are ordered by published_at. See the section on metadata about how this value is automatically detected.

Publishing pages

Everything is a post by default, but you can set page: true in the metadata to render a file as a page. Pages are rendered using the Ghost page template, and they are not included in the post indices.

Publishing drafts

Drafts are posts you’re still working on and don’t want to publish yet.

To mark a page or post as a draft, set draft: true in the metadata section. Drafts are excluded from the rendering pipeline.

Configuring the blog

The blog is configured using the shared settings.json file. The settings.json file should contain the following shared fields:

{
  "blog": {
    "url": "http://blog.example.com",
    "title": "Blog title",
    "description": "Blog description",
    "logo": ""
  },
  "authors": {
    "default": {
      "name": "Author name",
      "bio": "Author bio",
      "website": "http://blog.example.com",
      "image": "http://lorempixel.com/90/90/people/",
      "cover": "http://lorempixel.com/800/600/people/",
      "slug": "nick"    
    }
  }
}

The blog key contains information about the blog's URL, title and location:

  • blog.url is used everywhere where absolute links are preferred (e.g. RSS links). Make sure you change this to match your production URL.
  • blog.title is used in all the blog page headings and in most themes (same as theme.title)
  • blog.description is used in most themes (same as theme.description) and embedded as a meta tag in the page <head>.
  • blog.logo is made available for themes that want a large image to use somewhere in the theme.

The authors key is a hash, indexed by author short name. The authors.default key must be set. The default author is used on all posts that do not have a author: authorname metadata information. To support multiple authors, add more entries to the authors hash and then add the author: somename metadata entry for the posts written by that author.

  • author.name: the name of the author
  • author.bio: the author's bio
  • author.website: the author's website
  • author.image: the author's profile image (e.g. small picture shown next to the author bio). It seems the image should be 90 x 90px.
  • author.cover: the author's cover image (e.g. large image shown in the background for author pages)
  • author.slug: the author URLs are based this value. All posts written by a particular author are listed at /author/{slug}/.

Customizing the theme

Take a look at the Ghost themes documentation on how to write a theme and the helpers that are available in themes.

ghost-render uses code extracted from Ghost (0.5.x), so it should produce the same result in most cases. I've tested it casually, but not super extensively - if it works on Casper and a couple of other themes I tried so it's good enough for me. File pull requests or issues if you notice themes that don't work as expected.

Ideally, the Ghost core team would package the core rendering functions separately so that simple static sites can take advantage of the same standard for writing themes. Not sure if they are interested, but I'd love to see something cleaner than what I've done here.

Converting a Wordpress blog

I used wp2md to do the initial conversion. The markup requires a bit of touchup:

  • the meta block needs a starting ---
  • the title is repeated in the first heading of the markdown output

but other than those manual changes everything was smooth.

What would make this module easier to maintain?

Here are the main things that, if changed in Ghost core, would make this module easier to maintain:

  • extracting the rendering into it's own module that is useable by other npm modules, like this renderer.
  • dependency inversion for helpers. Instead of the helpers reaching into the rest of the core by requiring and calling various parts of the Ghost core, the rest of the core should call the rendering functions, passing in just the necessary data. This way, the helpers don't need to care about applying filters and loading additional data.
  • The dependency inversion thing would also make what seems to be the initial version of apps (which operate via async filters) much nicer: they could be just written as through-streams.
  • getting rid of the global configuration and settings API dependencies. In the Ghost codebase, helpers access the configuration and settings objects via top level requires by path. This means that one can't instantiate them with different configuration options (because the global settings would still be shared between the two, making it impossible to do two simultaneous builds with different configs). The helpers should be created with an instance of a configuration object instead, allowing them to work without having an extremely specific set of paths and dependencies present.
  • getting rid of express-hbs. It's just doing a bit too much and adds an extra layer of wrapping that one needs to punch through to get to handlebars. It's just not granular enough. Having the rendering be built on just handlebars would be nice.

More Repositories

1

distsysbook

The book Distributed systems: for fun and profit
HTML
2,538
star
2

markdown-styles

Markdown to static HTML generator and multiple CSS themes for Markdown
HTML
1,843
star
3

singlepageappbook

Content and site generator for Single page apps in depth (my book on single page applications)
HTML
1,712
star
4

nwm

Tiling window manager for X11 written in Node.js
JavaScript
786
star
5

npm_lazy

A lazy local cache for NPM to make your local deploys faster
JavaScript
753
star
6

gr

Multiple git repository management tool
JavaScript
675
star
7

electroshot

Capture website screenshots with optional device and network emulation as jpg, png or pdf (with web fonts!) using Electron / Chrome.
JavaScript
549
star
8

minilog

Lightweight client & server-side logging with Stream-API backends
JavaScript
378
star
9

cssbook

The book "Learn CSS layout the pedantic way"
CSS
225
star
10

gluejs

Build CommonJS modules for the browser via a chainable API
JavaScript
165
star
11

useradmin

User administration and auth for Kohana 3
PHP
106
star
12

fastlint

Lint faster by only running linters and other tools on files that have recently changed or files that are different from `master` in git.
JavaScript
89
star
13

vectorclock

A simple implementation of vector clocks in Javascript.
JavaScript
78
star
14

token

Time-limited, HMAC-based authentication token generation
JavaScript
65
star
15

perfect

A perfect minimal hash function generator
JavaScript
63
star
16

siobench

Basic socket.io benchmarking
JavaScript
44
star
17

nplay

Console-based mp3 player with Winamp key bindings and jump-to-file
JavaScript
42
star
18

markdown-styles-lambda

Automatic static site generation on `git push` using AWS Lambda and markdown-styles using a Gulp-style API.
JavaScript
33
star
19

tmux-cpu

Display CPU usage in your tmux status bar or in the terminal.
JavaScript
32
star
20

archey.js

Archey.js is a system information tool written in JS (based on Archey)
JavaScript
29
star
21

nodebook

A book about using Node.js
HTML
29
star
22

vnodehash

Consistent hashing using virtual nodes.
JavaScript
29
star
23

datalog.js

A trivial Datalog with top-down and bottom up evaluation written in Javascript to learn how Datalog evaluation works.
JavaScript
28
star
24

pipe-iterators

Like underscore for Node streams. Functions for iterating over object mode streams: forEach, map, mapKey, reduce, filter, fromArray, toArray, fromAsync, devnull, pipe, head, tail, through, thru, writable, readable, duplex, pipeline.
JavaScript
27
star
25

file-dedupe

Fast duplicate file detection library
JavaScript
25
star
26

microee

A tiny EventEmitter-like client and server side library for routing events
JavaScript
21
star
27

amdetective

Like node-detective, but for AMD/r.js files
JavaScript
19
star
28

tmux-mem

Display memory usage in your tmux status bar or in the terminal
JavaScript
16
star
29

snapshot

Serialize circular references, custom objects and other types not supported by JSON
JavaScript
16
star
30

pixiedust

RESTful lazy chainable API generator
JavaScript
14
star
31

htmlparser-to-html

Converts the JSON that the htmlparser/htmlparser2 package produces back to HTML
JavaScript
13
star
32

sioconfig

Socket.io HAProxy and Stunnel configs, and a test tool
JavaScript
12
star
33

nwm-user

Custom nwm configuration
JavaScript
11
star
34

hmvc-cfs

Cascading file system
JavaScript
11
star
35

wildglob

JavaScript
9
star
36

minimal

A router and http client - without unnecessary additions, modifications, or complications
JavaScript
8
star
37

node-winamp

Node.js remote control app for Winamp over LAN + client bindings
JavaScript
7
star
38

nodeko

Node.js window manager for X11 (written at NodeKO 2011)
C++
6
star
39

mg

JavaScript
6
star
40

miniee

An EventEmitter-like Client and server side library for routing events w/regexps
JavaScript
6
star
41

heartbeat

Combine multiple recurring timers to a single interval
JavaScript
6
star
42

7z-encrypted-backup

JavaScript
5
star
43

npm_push

Deprecated, see npm_lazy instead
JavaScript
5
star
44

tmux-colors

Write tmux-compatible color strings and have them work both in the terminal and in tmux.
JavaScript
5
star
45

glob-parse

Returns a parsed representation of a glob string; does not require Minimatch.
JavaScript
5
star
46

minitask

A standard/convention for running tasks over a list of files based around Node core streams2
JavaScript
4
star
47

changes

Check that your own npm packages are up to date
JavaScript
4
star
48

espresso

Syntax highlighting theme for Sublime Text 2 and Guake
Shell
4
star
49

markdown-stream-utils

Utility functions for processing markdown files using object mode streams. Used by markdown-styles and by ghost-render.
JavaScript
4
star
50

requireincontext

Wrapper to require() js files in a custom context
JavaScript
3
star
51

nbar

Status bar for nwm
C
3
star
52

glob-github

Run glob expressions against the Github Repo Contents API and return the matching files and metadata; with caching and handling of concurrent requests
JavaScript
3
star
53

fake.io

Fake Engine IO server and client for running tests without starting a server
JavaScript
3
star
54

apache_ai

Summarizes Apache error logs by removing unnecessary uniquely identifying information
JavaScript
3
star
55

unbundle-model

A model layer, implements a model and a collection.
JavaScript
3
star
56

controlflow

Node control flow patterns from my book
JavaScript
2
star
57

package-json-resolver

Library for reading package.json files for the gluejs build system
JavaScript
2
star
58

github.js

Github API v3 JS client
JavaScript
2
star
59

mds-csv

csv highlighting support for markdown-styles / generate-md
JavaScript
2
star
60

fail

Generic failure detector for connections
JavaScript
2
star
61

lost

A system for locating things by their name (and for binding things to names)
JavaScript
2
star
62

dom-to-htmlparser

Converts the DOM into htmlparser-compatible JSON
JavaScript
1
star
63

nodeunit-runner

A simple nodeunit test runner for invoking tests via node
JavaScript
1
star
64

tcp-loadbalancer

TCP load balancer for running load balanced scenario tests
JavaScript
1
star
65

sloppy

Sloppy quorum implementation
JavaScript
1
star
66

chromium-emulated-networks

The list of emulated network conditions from Chromium's devtools as JSON. Useful for configuring a specific latency and bandwidth profile.
1
star
67

fffuuu

A TCP socket client with transparent reconnection support and message buffering while disconnected.
JavaScript
1
star
68

chromium-emulated-devices

The list of emulated devices from Chromium's devtools as JSON. Useful for plucking out device resolutions, pixel ratios and user agent strings.
1
star
69

video-tools

Shell
1
star
70

identify-github-event

Map Github webhook events to their names
JavaScript
1
star
71

npmjs-github-crawler

Gets github metadata for the repos in the npm database
JavaScript
1
star
72

zendesk.js

Zendesk Javascript client (work in progress)
JavaScript
1
star