GitHub - danielfalbo/prev: Vanilla static site generator using Python and SQLite

4 min read Original article ↗
            __ ____  ___  __   ____  ____    ___
            `M6MMMMb `MM 6MM  6MMMMb `MM(    )M'
             MM'  `Mb MM69 " 6M'  `Mb `Mb    d'
             MM    MM MM'    MM    MM  YM.  ,P
             MM    MM MM     MMMMMMMM   MM  M
             MM    MM MM     MM         `Mbd'
             MM.  ,M9 MM     YM    d9    YMP
             MMYMMM9 _MM_     YMMMM9      M
             MM
             MM
            _MM_


    Build your website like it's 1996, deploy on Vercel like it's 2026.

BLUF

  - Vanilla anti-framework that scales down
  - Compiles in less than 10 seconds on Vercel
  - Pages are so light they load almost instantly
  - Implements static site generation in Python reading data from a SQLite db

  hn: https://news.ycombinator.com/item?id=46229756

WELCOME

  This repo contains code and data for my personal website and knowledge
  management system (https://wikipedia.org/wiki/Zettelkasten).
  Visit https://danielfalbo.com to see it in action.

  You can fork and use this as a template for your website.
  You can deploy this for free on https://vercel.com in a couple of clicks,
  or it's obviously also ready to be deployed anywhere else.

  In case you are reading this document somewhere else, you can find the
  official git repository at https://github.com/danielfalbo/prev.

STATIC SITE GENERATION

  Your computer likely already comes with python3 installed.

  Run

    python3 main.py

  and you will be able to

    open dist/index.html

  or any generated html file under 'dist/' with your browser.

DATABASE SCHEMA

  Your computer likely already comes with sqlite3 installed.

  You can see the database schema by running

    sh> sqlite3 knowledge.db

    sqlite> .schema

  or by just reading the 'create.db' script.

TEMPLATES

  Each "templates/<table>.html" file is the template page for entries of the
  given table. Site generation will create 'dist/<table>/[slug].html' files
  for each entry of each table that has a template. In the templates html you
  can use columns names inside brackets as '{placeholders}' for data that will
  be replaced with the actual value of the given column at generation time.

  All other "templates/<title>.html" where 'title' is not a table name will be
  rendered as standalone pages at 'dist/<title>.html'.

  The "templates/global.css" content can be imported into any template via the
  special '{css}' placeholder.

  The '{dateage}' is a special placeholder you can use within the entries 'html'
  that resolves to a dynamic component formatted like

    'December 15, 2022 (2y ago, 19.01 y.o.)'

  based on the 'created_time' value of the given entry in the database and the
  'AUTHOR_BIRTHDAY' from 'main.py' as ISO 8601 date string.

  There is a special '{context}' placeholder which will render hrefs to
  pages from other tables that have a relationship with this entry as specified
  in the 'RELATIONSHIPS' config object of 'main.py'.

COMPONENTS

  Components are like templates except their path and filename has no semantic
  meaning, they are just used by name wherever they are needed.

  As of today, the only components available are:

  _ "components/list.html" which is used as layout for generating indices at
    "dist/<table>.html" for each table in the db, and

  _ "components/dateage.html" which is used as blueprint for generating
    '{dateage}' blocks as described above.

ESCAPING BRACKETS FOR JS/CSS IN TEMPLATES AND COMPONENTS HTML

  Since {placeholders} replacement is done with Python's str.format, brackets in
  templates and components required for css/js must be escaped by being doubled.

  So

    function() {
      console.log({str_injected_by_python});
    }

  should be written as

    function() {{
      console.log({str_injected_by_python});
    }}

  in order for Python not to try to replace the function's body with some value
  from the format map.


ASSETS

  The 'assets/' dir gets copied over 'dist/assets/' as it is.

CREATING/UPDATING/DELETING DATA

  The database is a git-tracked file. The generated website is static and
  read-only. There is no server listening for requests to operate on the db.
  All data editing is done offline by running queries onto the 'knowledge.db'
  SQLite database file.

LIVE PREVIEW

  For ergonomic editing and previewing of the html content, the generation
  script also comes with a live compilation feature.

  You can start compiling 'dist/<table>/[slug].html' with local '{html}' content
  by starting the watch server with

    python3 main.py --watch <table> <slug>

  and then launching

    ./edit.sh <table> <slug>

  to load the current html content onto a buffer file and edit it in vi.
  Saving the file will trigger compilation of 'dist/<table>/[slug].html'
  so you will be able to preview your changes in the browser. Upon closing
  vi, the content of the buffer html will be dumped onto the database as
  html content of the given entry in the given table.

  NOTE: this feature only works on BSD because it relies on the kqueue syscall
    to listen for file changes.

PRINCIPLES

  > "Simple things should be simple, complex things should be possible."
    - Alan Kay

TODO, MAYBE

  - Support [[table/slug]] refs in html content

  - Consider https://html3000.dev/

  - Improve design on mobile

  - Introduce a server and dynamic content, implement visitors stats and
    text message user reach-out form, make sure SQL queries are injection-safe.

  - RSS feed

  - Breadcrumbs for table entry pages