A lightweight static-site generator written in Node.js. Converts Markdown files with YAML frontmatter into HTML pages and generates RSS/Atom/JSON feeds.
- π Markdown to HTML conversion using marked
- π° YAML frontmatter parsing
- π RSS, Atom, and JSON feed generation
- π Custom templates using JavaScript template literals
- π¨ Asset copying (CSS, images, etc.)
- β‘ Fast build times (typically <100ms for small blogs)
- π Draft post support
npm installTest easto with the included example content:
node index.js --config=test_config.json --verbose=trueThis will:
- Read markdown files from
./content/ - Convert them to HTML using templates from
./templates/ - Generate RSS/Atom/JSON feeds
- Copy assets and data files
- Output everything to
./output/
View the result:
npm run serve
# Visit http://localhost:8000/node index.js --config=path/to/config.jsonnode index.js --config=config.json --verbose=trueEasto is designed to be used as a dependency in your blog repository:
{
"dependencies": {
"easto": "../easto"
},
"scripts": {
"build": "rm -rf ./output/* && node ./node_modules/easto/index.js --config=easto_config.json"
}
}Then run: npm run build
Create a JSON config file (see test_config.json for an example):
{
"content_dir": "./content",
"output_dir": "./output",
"templates_dir": "./templates",
"data_dir": "./static",
"author": "Your Name",
"baseurl": "https://yourblog.com/",
"feed": {
"title": "Your Blog Title",
"description": "Blog description",
"id": "https://yourblog.com",
"link": "https://yourblog.com",
"copyright": "All rights reserved 2024, Your Name",
"feedLinks": {
"json": "https://yourblog.com/feed/json",
"atom": "https://yourblog.com/feed/atom",
"rss": "https://yourblog.com/feed/rss"
},
"author": {
"name": "Your Name",
"email": "you@example.com",
"link": "https://yourblog.com"
}
}
}Create markdown files in your content_dir with YAML frontmatter:
---
title: My Blog Post
date: 2024-12-15
datelabel: December 15th, 2024
language: en
tags: [web development, javascript]
permalink: my-blog-post
draft: false
description: A short description of the post
---
Your markdown content goes here...
## Headings work
So do **bold**, _italic_, and `code`.title: Post titledate: Publication date (YAML date format)datelabel: Human-readable date for displaylanguage: Content language (en, de, etc.)tags: Array of tags/categoriespermalink: URL slug (without .html extension)draft: Boolean - set totrueto exclude from feeds/indexdescription: Short summary for meta tags and teasers
Create three template files in your templates_dir:
Contains the layout for the homepage with ${ content } placeholder for teasers.
Template for each post preview on the homepage.
Full page template for individual blog posts.
All templates use JavaScript template literal syntax with access to:
blogmeta: Your config.json valuesmeta: Post frontmattercontent: Rendered HTML content
Example:
`<h1>${ meta.title }</h1>
<time datetime="${ meta.date }">${ meta.datelabel }</time>
${ content }`your-blog/
βββ content/ # Markdown files
β βββ 2024-12_post-one.md
β βββ 2023-06_post-two.md
βββ templates/ # HTML templates
β βββ index.html
β βββ index_teaser.html
β βββ post.html
β βββ assets/ # CSS, fonts, etc.
βββ static/ # Static files (images, downloads)
βββ output/ # Generated site (gitignore this)
βββ easto_config.json # Configuration
βββ package.json
Easto generates files without extensions (e.g., /blog-post instead of /blog-post.html).
npm run serve
# or: node serve.jsThis serves extensionless files with Content-Type: text/html and runs on http://localhost:8000/
If you have Caddy installed, use this configuration:
:8000 {
root * output
file_server
try_files {path} {path}.html {path}/index.html
}- Parse Arguments: Reads
--configand--verboseflags - Load Config: Loads JSON configuration file
- Read Content: Reads all
.mdfiles fromcontent_dir - Sort by Date: Processes files in reverse alphabetical order (newest first)
- Parse Frontmatter: Extracts YAML metadata from each file
- Convert Markdown: Converts markdown to HTML using marked
- Apply Templates: Evaluates templates with content and metadata
- Generate Feeds: Creates RSS, Atom, and JSON feeds
- Copy Assets: Copies template assets and data files to output
Posts with draft: true:
- β HTML file is generated
- β Not included in feeds (RSS/Atom/JSON)
- β Not shown in index page teaser list
- β Counted separately in build output
Use date-prefixed filenames for proper ordering:
2024-12_latest-post.md(processed first)2023-06_older-post.md(processed later)
Files are sorted reverse-alphabetically, so newer dates appear first in the index and feeds.
- Keep the generator simple - it's intentionally a single-file architecture
- Templates and content typically live in your blog repo, not in easto
- Use easto as a dependency (
npm install ../eastoor from git) - Clean the output directory before each build to avoid stale files
See the real-world usage in blog.thomaspuppe.de repository.
Run the test suite:
npm test
# or: ./test.shThe test script:
- Builds the site with test content
- Verifies all expected files are generated
- Checks that drafts are excluded from feeds/index
- Tests the local server
- Validates HTTP responses and content types
No testing framework required - just a simple bash script.
To work on easto itself:
- Make changes to
index.js - Build:
npm run build - Test:
npm test - Serve:
npm run serve - Visit http://localhost:8000/
The included serve.js dev server correctly handles extensionless files that easto generates.
MIT