trash

command module
v0.0.0-...-e97b2ae Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2025 License: Unlicense Imports: 55 Imported by: 0

README

Trash

Trash - a stupid, simple website compiler.

[!CAUTION] Nothing is stabilized yet, so existing pages may break from time to time, do not use this for anything serious

Features

  • $LaTeX$ expressions (no client-side JS!)
  • D2 diagram rendering (no JS still!)
  • Mermaid diagram rendering (yeah, still no client-side JS)
  • Pikchr diagram rendering (you guessed it)
  • Painless embedding of YouTube videos, HTML5 audio, and more in native Markdown
  • Syntax highlighting
  • Various Markdown extensions such as image <figure>s, image sizing, callouts, Pandoc-style fences, :emojis:, and more
  • YAML and TOML frontmatter parsing support
  • Automatic anchor placement
  • Automatially minifies output HTML, CSS, JS, JSON, SVG and XML for smallest builds
  • Lots of built-in template functions including an integration with the Expr expression language
  • Built-in webserver with live-reloading (trash serve)
  • Under 1400 lines of Go code in a single file

Installation

Install Go if you haven't yet.

$ go install github.com/zeozeozeo/trash@latest

Usage

$ trash help
Usage: trash <command> [directory]

A stupid, simple website compiler.

Commands:
  init     Initialize a new site in the directory (default: current).
  build    Build the site.
  watch    Watch for changes and rebuild.
  serve    Serve the site with live reload.
  help     Show this help message.

Template cheatsheet

Trash uses the Go template syntax for templates, extending it with some handy built-in functions. Below is a reference of all extra functions defined by Trash.

You should still refer to the source code instead of this if possible, this mostly serves as a general overview.

File system
  • readDir "path": Get all pages within a directory

    {{ $posts := readDir "posts" }}
    
  • listDir "path": List directory entries, use .Name and .IsDir on returned values

    used in personal example

  • readFile "path": Read a file from the project root

    <style>{{ readFile "static/style.css" }}</style>
    
Dict operations
  • dict "key1" val1 "key2" val2: Create a dict, usually paired with other functions
  • sortBy "key" "order" $dict: Sort by a key path. order can be "asc" or "desc"
    {{ $posts := readDir "posts" | sortBy "date" "desc" }}
    {{ $users := $data.users | sortBy "age" "asc" }}
    
  • where "key" value $dict: Filter where a key path matches a value
    {{ $featured := where "featured" true .Site.Pages }}
    {{ $activeUsers := where "active" true $data.users }}
    
  • groupBy "key" $dict: Group by a key path
    {{ $postsByYear := groupBy "date.year" .Site.Pages }}
    {{ $usersByDept := groupBy "department" $data.users }}
    
  • select "key" $dict: Extract values from a key path across many dicts
    {{ $allTags := select "tags" .Site.Pages }}
    {{ $allNames := select "name" $data.users }}
    
  • has "key" $dict: Check if a dict has a certain key path
    {{ if has "image" .Page }} ... {{ end }}
    {{ if has "email" $user }} ... {{ end }}
    

All collection functions support dot notation for nested keys:

{{ where "author.name" "Alice" .Site.Pages }}
{{ groupBy "metadata.tags.primary" .Site.Pages }}
{{ select "contact.email.work" $users }}

Passing a .Page will decay into its frontmatter (.Page.Metadata):

{{/* These are equivalent - both access the page's frontmatter */}}
{{ if has "title" .Page.Metadata }} ... {{ end }}
{{ if has "title" .Page }} ... {{ end }}
Datetime
  • now: Return the current UTC time

  • formatTime "format" date: Format time

    {{ .Metadata.date | formatTime "DateOnly" }}
    

    Supported formats:

    Format Output (example)
    Layout 01/02 03:04:05PM '06 -0700
    ANSIC Mon Jan _2 15:04:05 2006
    UnixDate Mon Jan _2 15:04:05 MST 2006
    RubyDate Mon Jan 02 15:04:05 -0700 2006
    RFC822 02 Jan 06 15:04 MST
    RFC822Z 02 Jan 06 15:04 -0700
    RFC850 Monday, 02-Jan-06 15:04:05 MST
    RFC1123 Mon, 02 Jan 2006 15:04:05 MST
    RFC1123Z Mon, 02 Jan 2006 15:04:05 -0700
    RFC3339 2006-01-02T15:04:05Z07:00
    RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00
    Kitchen 3:04PM
    Stamp Jan _2 15:04:05
    StampMilli Jan _2 15:04:05.000
    StampMicro Jan _2 15:04:05.000000
    StampNano Jan _2 15:04:05.000000000
    DateTime 2006-01-02 15:04:05
    DateOnly 2006-01-02
    TimeOnly 15:04:05

    Or vice-versa (passing "15:04:05" will have the same effect as passing "TimeOnly")

Strings and URLs
  • concatURL "base" "path1" "path2" ...: Join URL parts together
    <img src="{{ concatURL .Config.site.url .Page.Metadata.image }}">
    
  • truncate "string" length: Shorten a string to a max length by adding
    <p>{{ .Content | truncate 150 }}</p>
    
  • pluralize count "singular" "plural": Return the singular or plural form based on the count
    {{ len $posts | pluralize "post" "posts" }}
    
  • markdownify "string": Render a string of Markdown as HTML
    {{ .Page.Metadata.bio | markdownify }}
    
Conditionals
  • default "fallback" value: Return the fallback if the value is empty
    <img alt="{{ default "A cool image" .Page.Metadata.altText }}">
    
  • ternary condition trueVal falseVal: if/else
    <body class="{{ ternary (.Page.Metadata.isHome) "home" "page" }}">
    
Math and random
  • add, subtract, multiply, divide, max, min: Operations on integers
  • randint min max: A random integer in a range
  • randfloat min max: A random float in a range
  • choice item1 item2 ...: Randomly select one item from the list of arguments provided
    <p>Today's greeting: {{ choice "Hello" "Welcome" "Greetings" "Howdy" }}!</p>
    
  • shuffle $list: Randomly shuffle a list (returns a copy)
Slice utilities
  • first $list: Get the first item of a slice
  • last $list: Get the last item of a slice
  • reverse $list: Return a new slice with the order of elements reversed
  • contains $list item: Check if a slice contains an item
    {{ if contains .Page.Metadata.tags "featured" }}
      <span class="featured-badge">Featured</span>
    {{ end }}
    
Casts
  • toString value: Convert any value to a string
  • toInt value: Convert any value (e.g. float, string) to an integer
  • toFloat value: Convert any value (e.g. int, string) to a float
Debugging
  • print value: Print a value during the build
Utility
  • toJSON $data: Convert a value to a JSON string
  • fromJSON $data: Parse a JSON string
  • sprint "format" values...: Return a formatted string, similar to printf
    {{ $message := sprint "Processed page %s" .Page.Permalink }}
    
  • expr "code" environ: Execute an Expr block, see the blog example for more

Config format

Running trash init will create a Trash.toml as one of the files in your current directory. The structure of this file is not forced upon you whatsoever, however there are a few optional settings you can toggle:

[mermaid]
theme = "dark" # see available themes at https://mermaid.js.org/config/theming.html

[d2]
sketch = true # see https://d2lang.com/tour/sketch/
theme = 200   # see available themes at https://d2lang.com/tour/themes/

[pikchr]
dark = true # change pikchr colors to assume a dark-mode theme

[anchor]
text = "#"          # change the ¶ character in auto-anchors to something else
position = "before" # default is "after", where to place the anchor

Aside from this, you can add your own fields, and access them in templates:

[site]
url = "https://example.com/"

Let's say you're making an RSS feed for your blog:

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
    <title>My Blog</title>
    <link>{{ .Config.site.url }}</link> <!-- this is the `url` from Trash.toml! -->
    ...
    <item>
        ...
    </item>
</channel>

Template context

All templates under the layouts directory are created in the same context, so you can include other templates within a template:

<!-- layouts/base.html (this is the default layout) --->
<!DOCTYPE html>
<html lang="en">
  {{ template "boilerplate.html" }}
  <body>
    {{ .Page.Content }}
  </body>
</html>
<!-- layouts/boilerplate.html --->
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My Blog</title>
</head>

This is not the case for .md filles inside the pages directory.

Documentation

Overview

Trash - a stupid, simple website compiler. Licensing information at the bottom of this file.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL