Project

kozeki

0.0
No release in over a year
Convert markdown files to rendered JSON files with index for static website blogging
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0

Runtime

>= 1.0.0.pre11
>= 0
~> 1.2
 Project Readme

Kozeki: Markdown to JSON renderer for blogging with static site generator

Backend renderer used at https://diary.sorah.jp/ and https://blog.sorah.jp/

  1. Collect markdown files with front matter (for metadata)
  2. Decorate metadata using Ruby scripts
  3. Render as HTML in JSON files, with final metadata
  4. Emit index JSON files for listing articles in various ways
  5. Use JSON files to run your website, e.g. with Nextjs

Concept

  • All files in source_directory are imported as source
  • All sources will be compiled into item. Each item represents itself in destination_directory as a single JSON file.
  • An item may belong to collections.
    • Using a collection is only way to generate a list of items. You may use a collection just only for a single item., such like to implement a index of permalink-to-item.
  • Each collections generate JSON files (multiple if paginated) in destination_directory to provide list of items.

Installation

Requires Ruby 3.2+

# Gemfile
gem 'kozeki'
bundle install

Example Workflow

Configuration

# config.rb
source_directory './articles'
destination_directory './build'
cache_directory './cache' # to store incremental build state and cache

# Ensure timestamp for all items
metadata_decorator do |meta, source|
  meta[:timestamp] ||= meta[:published_at]
  meta[:timestamp] ||= meta[:created_at]
  meta[:timestamp] ||= source.mtime
end

# Include published items to collections
metadata_decorator do |meta|
  next unless meta[:published_at]

  meta[:collections] ||= []
  meta[:collections].push('index')
  meta[:collections].push(meta.fetch(:timestamp).strftime('archive:%Y:%m'))
  meta[:collections].concat (meta[:categories] || []).map { "category:#{_1}" }
end

# Generate permalink and make a index as a collection
metadata_decorator do |meta|
  meta[:permalink] = "#{meta.fetch(:timestamp).strftime('%Y/%m/%d')}/#{meta.fetch(:slug)}"
  meta[:collections] ||= []
  meta[:collections].push "permalink:#{meta[:permalink].tr('/','!')}"
end

Advanced configuration example available at ./ADVANCED.md

Prepare input

Example input at ./articles/2023/foo.md:

---
title: foo bar
published_at: 2023-08-06T17:50:00+09:00
categories: [diary]
slug: foo-bar
---

## foo bar

blah blah

Run Build

bundle exec kozeki build ./config.rb

Confirm output

Example output:

// out/items/ao_ca1c4d8da0e7823fa8b31fb4f15a727d9a11c57ca0736b7c6a40bd3906ca85f1.json
{
  "kind": "item",
  "id": "ao_ca1c4d8da0e7823fa8b31fb4f15a727d9a11c57ca0736b7c6a40bd3906ca85f1"
  "meta": {
    "id": "ao_ca1c4d8da0e7823fa8b31fb4f15a727d9a11c57ca0736b7c6a40bd3906ca85f1",
    "title": "foo bar",
    "collections": ["archive:2023:08", "category:diary"],
    "timestamp": "2023-08-06T17:50:00+09:00",
    "published_at": "2023-08-06T17:50:00+09:00",
    "updated_at": "2023-08-06T18:00:00+09:00"
  },
  "data": {
    "html": "<h2>foo bar</h2><p>blah blah</p>"
  },
  "kozeki_build": {
  }
}
// out/collections/archive:2023:08.json
{
  "kind": "collection",
  "name": "archive:2023:08",
  "items": [
    {
      "path": "items/ao_ca1c4d8da0e7823fa8b31fb4f15a727d9a11c57ca0736b7c6a40bd3906ca85f1.json",
      "meta": {
        "id": "ca1c4d8da0e7823fa8b31fb4f15a727d9a11c57ca0736b7c6a40bd3906ca85f1",
        "title": "foo bar",
        "collections": ["archive:2023:08", "category:diary"],
        "timestamp": "2023-08-06T17:50:00+09:00",
        "published_at": "2023-08-06T17:50:00+09:00",
        "updated_at": "2023-08-06T18:00:00+09:00"
      }
    }
  ]
}
// out/collections.json
{
  "kind": "collection_list",
  "collections": [
    {"id": "archive:2023:08", "path": "collections/archive:2023:08.json"},
    {"id": "category:diary", "path": "collections/category:diary.json"}
  ]
}

Known metadata keys

  • id (string): Static identifier for a single markdown file. Used for final JSON file output. Default to file path.
  • collections (string[]): Specify index files to include a file
  • timestamp (Time or RFC3339 String): Timestamp for file. Used for descending sort of list of items in collection.

License

The gem is available as open source under the terms of the MIT License.