Zine static site generator

Zine is a collection of tools (eg markdown to html renderer, templating engine, etc) orchestrated via the Zig Build system.

Zine also features its own templating language called Super (which currently lives in the same repository but that will eventually become a standalone project).

It’s still super alpha but it’s complete enough to build my personal blog which was ported to Zine from Hugo a while ago.

I develop it on stream quite often so if you catch me live you can watch me fix the bugs and crashes that you will inevitably find :^)

Official website: https://zine-ssg.io

Today I’ve added initial hot reloading support on Linux and macOS.

The sample website has been updated to depend on this new version of Zine.

To try it yourself just run zig build serve -Dport=8080 from the sample website directory and start making changes.

Also filed a couple contributor-friendly issues:

Hot reloading in action:

27 Likes

Zine has an official website now!

https://zine-ssg.io

11 Likes

If anybody here is crazy enough to try and build a static site with it, please do post in this thread if you need help, I’ll be happy to assist you.

Please do be aware that Zine is still super alpha so it will only be able to build relatively simple sites.

1 Like

added a changelog to the website to make it easier for people to update zine as it gets improved Changelog - Zine

1 Like

I finally got some time to look into this, but now the zig master (2701) from last night gives me errors running zig build serve -Dport=8080 in lib/std/std.zig. I also tried an older version that I had (2076 from Jan 9th), but then datetime, frontmatter and super are missing urls in build.zig.zon

I tried to find a build from last week by browsing to the parent directory of the master download ( https://ziglang.org/builds/ ) but that gives 403/Forbidden. So I can’t bisect to a version of zig between the two that I could try and find one that works.

For good measure I also tried to check out under Linux (Ubuntu 20.04, the above was done on my Macbook M1) with the same error result:

~/src/zine-sample-site$ zig build
install
└─ install generated to index.html
   └─ run super_exe (index)
      └─ zig build-exe super_exe ReleaseFast native 1 errors
/opt/zig/zig-linux-x86_64-0.12.0-dev.2701+d18f52197/lib/std/std.zig:201:69: error: expected type 'std.Options', found 'type'
pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options else .{};
                                                                ~~~~^~~~~~~~~~~~
/opt/zig/zig-linux-x86_64-0.12.0-dev.2701+d18f52197/lib/std/std.zig:203:21: note: struct declared here
pub const Options = struct {
                    ^~~~~~
referenced by:
    enable_segfault_handler: /opt/zig/zig-linux-x86_64-0.12.0-dev.2701+d18f52197/lib/std/debug.zig:2517:36
    maybeEnableSegfaultHandler: /opt/zig/zig-linux-x86_64-0.12.0-dev.2701+d18f52197/lib/std/debug.zig:2521:9
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
error: the following command failed with 1 compilation errors:
/opt/zig/zig-linux-x86_64-0.12.0-dev.2701+d18f52197/zig build-exe /home/avanderneut/src/zine-sample-site/zig-cache/o/967d2a149266326b019ed56a8e2366d7/libtree-sitter.a -OReleaseFast -I /home/avanderneut/src/zine-sample-site/zig-cache/i/9fbdf9c3c8e444b5b87a15db0528ebf9/include --dep options --dep super --dep scripty --dep datetime -Mroot=/home/avanderneut/.cache/zig/p/1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299/zine/src/super.zig -Moptions=/home/avanderneut/src/zine-sample-site/zig-cache/c/a9623a3dc336cd23ffaa892bb69dbc52/options.zig /home/avanderneut/src/zine-sample-site/zig-cache/o/967d2a149266326b019ed56a8e2366d7/libtree-sitter.a -I /home/avanderneut/src/zine-sample-site/zig-cache/i/9fbdf9c3c8e444b5b87a15db0528ebf9/include --dep frontmatter --dep scripty -Msuper=/home/avanderneut/.cache/zig/p/1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299/super/src/main.zig -Mscripty=/home/avanderneut/.cache/zig/p/1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299/scripty/src/main.zig -Mdatetime=/home/avanderneut/.cache/zig/p/1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299/datetime/src/main.zig -Mfrontmatter=/home/avanderneut/.cache/zig/p/1220e6580fdbd0a56a97300bab938f61fe3b5b35fc7755a150db267422cf554ab299/frontmatter/frontmatter.zig -lc++ -lc --cache-dir /home/avanderneut/src/zine-sample-site/zig-cache --global-cache-dir /home/avanderneut/.cache/zig --name super_exe --listen=- 
Build Summary: 24/34 steps succeeded; 1 failed (disable with --summary none)
install transitive failure
├─ install generated to index.html transitive failure
│  └─ run super_exe (index) transitive failure
│     └─ zig build-exe super_exe ReleaseFast native 1 errors
├─ install generated to about/index.html transitive failure
│  └─ run super_exe (about) transitive failure
│     └─ zig build-exe super_exe ReleaseFast native (+5 more reused dependencies)
├─ install generated to foo/index.html transitive failure
│  └─ run super_exe (index) transitive failure
│     └─ zig build-exe super_exe ReleaseFast native (+5 more reused dependencies)
└─ install generated to bar/index.html transitive failure
   └─ run super_exe (index) transitive failure
      └─ zig build-exe super_exe ReleaseFast native (+5 more reused dependencies)
error: the following build command failed with exit code 1:
/home/avanderneut/src/zine-sample-site/zig-cache/o/29737ff94888fdb0b4c17022a43d76fd/build /opt/zig/zig-linux-x86_64-0.12.0-dev.2701+d18f52197/zig /home/avanderneut/src/zine-sample-site /home/avanderneut/src/zine-sample-site/zig-cache /home/avanderneut/.cache/zig --seed 0x5945dd4c -Z09c0948d34468bd6

Sorry you had this experience, unfortunately you tried to build Zig at the exact moment in which Zig made a breaking change. I’ve updated Zine to latest Zig so you should now be able to run it with a new-enough build of Zig.

See the changelog for more info: Changelog - Zine

I will probably stick to 0.12.0 once it gets released or at the very least I will use MachEngine’s Nominated Zig versions in order to minimize this kind of thing happening.

1 Like

Absolutely no problem, you had me duly warned. And I expected something like this, which is why I tried to find older daily builds to try (not wanting to check out an older verseion and build from source, that was going a bit to far for me just to try this out).

However I was still running into the same error on std.Options I checked my zig version (0.12.0-dev.2701+d18f52197) and removed zig-cache and zig-out from the directory where I cloned the zine-sample-site (umodified), as well as cleaned out ~/.cache/zig (where I found a zine directory from earlier today).

I was about to give up until, looking at the changelog, I asked myself why there is a copy button, and realised the build.zig.zon in the sample site needed updating with the information copied with that button. Now it all works!

Maybe it is a good thing to mention why there is a copy button at the top of the changelog page, or did I miss something?

Lovely project, I’m looking forward to following this as it grows!
I also had a similar issue as @Anthon (plus for some reason zig-cache needing sudo permissions to alter), thanks for pointing me in the right direction to fix it! Zig newcomer here :sweat_smile:

2 Likes

Thank you! Hopefully now the storm has passed, a recent build of Zig should not have any conflict with Zine.

Some updates:

  • I’ve rewritten the documentation about templates, hopefully making it easier for people to get started.
  • ninja_tron has PRd a guide to deploy on Coudflare Pages
  • I’ve deleted kristoff-it/zine-sample-site in favor of recommending to people to clone and play with the repo of the official website instead, since it’s a much more interesting website that we will naturally maintain and improve over time.

This is the zine-ssg.io repo for those who are interested:

$ git clone https://github.com/kristoff-it/zine-ssg.io.git
$ cd zine-ssg.io
$ zig build serve -Dport=8080
3 Likes

I think the link to the sample project is now the zine site itself?

Yes, see the 3rd bullet item in kristoff’s post 10 days ago.

I was meaning to suggest to @kristoff to edit the opening post to change the 2nd bullet point to the new site, because the old sample site is now a 404.

I’ve updated the post thank you.

1 Like

Updated to latest Zig (post std.http changes) and added syntax highlighting support. You can see that the official website has a bunch colorful code blocks in the documentation section.

3 Likes

Big update for Zine:

  • Zine now uses Ziggy as the frontmatter language! In the near future Zine will develop tooling for editing ziggy-markdown files. In the meantime consider downloading the Ziggy CLI tool for a smoother editing experience if you plan to use Ziggy directly.
  • Added an initial version of sections to Zine! See the updated documentation section for more information about that. Beware that $site.pages() was removed in favor of the new system.
  • Added a the ability to define alternatives in the frontmatter of a page. Alternatives allow you to specify multiple layouts to apply to the same piece of content. Useful for generating RSS feeds.
  • Added syntax highlighting to layouts: now strings have a syntaxHighlight builtin.
  • Updated Zig version because a bugfix was needed to add syntax highlighting to templates. Now Zine depends on Zig 0.12.0-dev.3381+7057bffc1 and above. Make sure to update your GitHub Actions workflows accordingly.

Hey Loris, I tried out Zine with my personal website at GitHub - LewisGaul/LewisGaul.github.io at zine, which is currently using jekyll.

Some thoughts/issues I had:

  • Missing support for syntax highlighting (fails the build) for:
    • yaml, bat
  • Syntax highlighting build errors give a line number lower than actual code block line (ignores frontmatter lines)
  • Hit “panic: TODO: explain that a missing image has been found in a markdown file” in zine/src/markdown-renderer.zig:110:17: 0x31843f2 in main (markdown-renderer)
  • Hit “panic: err: unsupported date format” in src/template.zig:505:60: 0x3177c6a in runInternal (layout)
    • var="$page.date.format('January 02, 2006')" works, but I don’t like this format
    • ‘2 January 2006’, ‘2006-01-24’ don’t work
  • Unsure how to integrate sass, would probably try converting to plain css…
  • Unsure how to extract the first paragraph of a blog post (currently uses jekyll’s ‘excerpt’)

Overall pretty nice though, I like some of the design decisions. I’m slightly concerned that there may be slightly more limited control (if you want templates/HTML you have to make a layout, and there’s no mechanism for including a block of HTML without using a template?), but I haven’t yet tried anything very complex.

If I can figure out the image and css issues I think I’d be most of the way to getting it working.

Thank you Lewis for the feedback!

@neurocyte can we please have yaml added to your distribution of TreeSitter?

Apparently nobody has written a TreeSitter grammar for .bat files (or at least I can’t find it) so that would require more work to pull off.

Yep, more on that at the end of this reply (there’s a few of those TODO comments fyi :^))

I have only implemented a couple of those (see code) in part also because I don’t know what format to use. I don’t like YyMmDdMmSs etc and strongly prefer Go’s approach but I know other people don’t like that either. I’m genuinely interested in a potential third option if you have any.

I need to add to zine the ability for it to depend on other build steps (and use them as assets), so this way you will be able to do sass which way you want. After that it might be worth looking into streamlining processing of common assets (such as css-related languages) but a general solution is easier and infinitely flexible, so I want do to that first.

Currently not possible, I can implement it but requires first for me to take a moment to implement a tricky dependency tracking system that I haven’t been able to implement using the Zig build system yet. It’s on my TODO list. I’ve been conservative in terms of information sharing between pages because I want to make sure that a single page change doesn’t trigger needless rebuilds so I will add more builtins to do so once I get this working.

you mean something like reusable snippets of code that can be included in multiple templates? I’m thinking of adding those as well but haven’t done so yet.

if you put an image in the same directory as your post, zine will copy it automatically

Ok so here’s the main thing: I’ve been very unhappy with how Tree Sitter behaved when trying to generate diagnostics so I wrote my own HTML parser from scratch and even built a HTML LSP in the meantime (GitHub - kristoff-it/super-html: HTML Language Server & Templating Language Library). I’m currently in the process of refactoring the Zine templating system to make use of the new parser. Once that’s in you should expect:

  1. error message quality to go up
  2. the beginnings of a kickass LSP not only for HTML but for the whole templating language (ie aware of the semantic meaning of special attributes etc)

Working on it, will update this thread when I have reached a meaningful milestone. Today I got the Template AST to pass its unit tests again.

2 Likes

I’ve added yaml support to tree-sitter and flow-syntax. I too couldn’t find a windows batch file tree-sitter grammar.

Zine v0.1.0 is out!

zig fetch --save "git+https://github.com/kristoff-it/zine#v0.1.0"

Zine has finally reached a first tagged release!

A lot has happened in these 4 months, so read this changelog to learn how to upgrade your Zine website.

The first thing that changed is the recommended way of updating your build.zig.zon.

The second is that Zine now tracks the latest stable version of Zig, which is 0.13.0 at the moment of writing.

Now onto new features and breaking changes:

  • The recommended GitHub Actions Workflow files for building Zine websites on Github have changed. The new version uses the mlugg/setup-zig@v1 action that will automatically manage caching for you. It is highly recommended to update your scripts.

    • While you’re at it, change --summary all to --summary new, which will only list the pages that were rebuilt.
  • SuperHTML (Zine’s templating language) dropped Tree Sitter as its HTML parser in favor of a handcrafted implementation that more closely follows the HTML5 spec. This brings us significantly improved error messages and other advantages.

    • It’s highly recommended you install and configure SuperHTML as your language server for both HTML and SuperHTML Templates in order to get in-editor diagnostics and kickass autoformatting. The repo also offers a Tree Sitter grammar for SuperHTML that incudes a few visual improvements for tags and attributes that have semantic meaning.

      SuperHTML also has a VSCode extension.

    • SuperHTML follows the HTML5 spec much more closely and, while writing an HTML parser from scratch for it, I learned that self-closing tags (tags with a final /) are not a thing in HTML5, so now <extend> and <super> have been defined as void elements in SuperHTML and want no final slash nor closing tag.

      Note that SuperHTML will consider an error using self-closing tags in HTML (outside of a <svg> scope).

    • The correct file extension for templates is .shtml. You must rename all your templates to the new file extension otherwise you will get an error from SuperHTML when it sees non-HTML compliant syntax, since <extend> and <super> are recognized as void elements only in SuperHTML template files.

  • Scripty has impoved as well: inside of nested loops, it is now possible to access outer $loop variables by doing $loop.up(). Each call to up() goes up one level.

    • This is thanks to the fact that interrupts were implemented in Scripty, opening the door to features that rely on the ability to pass from the outside values into scripty (up() relies on that since loops are a SuperHTML concept that Scripty is completely unaware of).
  • For syntax highlighting, Zine uses a distribution of Tree Sitter that bundles a lot of grammars and highlighting queries from Flow Control. The dependency has now been updated to a new version that adds support for more languages.

That’s mostly it. If you encounter bugs while updating, please don’t hesitate to open a new issue on GitHub with a link to a reproduction.

6 Likes

Congrats!

About this, though:

Self-closing tags are a thing in HTML: they’re legal.

The HTML Living Standard says this about void elements:

Then, if the element is one of the void elements, or if the element is a foreign element, then there may be a single U+002F SOLIDUS character (/), which on foreign elements marks the start tag as self-closing. On void elements, it does not mark the start tag as self-closing but instead is unnecessary and has no effect of any kind.

HTML5 has been superceded by the Living Standard for six years now. That said, it has this to say:

Then, if the element is one of the void elements, or if the element is a foreign element, then there may be a single U+002F SOLIDUS character (/). This character has no effect on void elements, but on foreign elements it marks the start tag as self-closing.

It doesn’t seem to me that following the standard closely would result in making the optional / in a void element into a syntax error. This isn’t one of the normative rules about what to do with malformed content, it is simply optional, the current spec goes so far as to call it ‘unnecessary’, but that doesn’t make it malformed.

That doesn’t oblige SuperHTML to accept it, but given the reasoning you’ve stated, wanting to follow the spec closely, I thought this was worth pointing out.

1 Like