Skip to content

yakkomajuri/teeny

Repository files navigation

Teeny: A tiny static site generator

Teeny is a static site generator written in 350 lines of plain JavaScript. You can easily understand exactly how it works and you can launch a blog in 5 minutes.

If you need more, you can use plugins to extend functionality.

Teeny was originally built over a couple of hours (pre-AI) because I wanted something simple for my own personal website. You can read that story on my blog.

🏃 Quick start

npm i -g teeny-cli # yarn global add teeny-cli
teeny init && teeny develop

For an example of a project using Teeny, check out my personal blog's repo.

💻 Supported commands

Initialize a Teeny project in the current directory

teeny init

Build the static HTML files and add them to public/

teeny build

Start a local Teeny server that listens for file changes

teeny develop

📄 How it works

Teeny is a super simple static site generator originally built to suit my needs and my needs only, but it's very powerful in its simplicity, and may be of use to others.

All it does is generate pages based on HTML templates and Markdown content.

It does very little and is strongly opinionated, but has allowed me to build a blog I'm happy with extremely quickly.

Essentially, there are really only 2 concepts you need to think about: templates and pages.

Templates

Templates are plain HTML and should be added to a templates/ subdirectory.

They can contain an element with the id page-content, which is where Teeny adds the HTML generated by parsing the Markdown content.

Pages

Markdown is a first-class citizen in Teeny, so all of your website's pages are defined by a Markdown file.

The file need not have any actual content though, so if you want a page to be defined purely in HTML you just need to create a template that is referenced from a page file.

To specify what template a page should use, you can specify it in the frontmatter of the page, like so:

---
template: blog
---

In the above example, Teeny will look for a template called blog.html. If no template is specified, Teeny looks for a default.html file in templates/ and uses that.

💡 Example usage

Here's an example of Teeny at work.

To start a Teeny project, run teeny init. This will create the following in your current directory:

.
├── pages
│   └── index.md
├── static
│   └── main.js
├── templates
│   ├── default.html
│   └── homepage.html
└── teeny.config.js

If you then run teeny build, you'll end up with this:

.
├── pages
│   └── index.md
├── public
│   ├── index.html
│   └── main.js
├── static
│   └── main.js
├── templates
│   ├── default.html
│   └── homepage.html
└── teeny.config.js

index.md uses the homepage template, and together they generate index.html. As is standard with other SSGs, static files are served from public/.

You'll also notice main.js got moved to public/ too. Teeny will actually take all non-template and non-page files from pages/, templates/, and static/ and copy them to public/, following the same structure from the origin directory.

The reason for this is that I actually didn't want to have "magic" imports, where you have to import static assets from paths that do not correspond to the actual directory structure. As a result, I decided that static files would just live inside templates/ and pages/ as necessary.

Later I did surrender to the static/ directory approach though, as there may be assets both pages and templates want to use. Imports from static/ are "magic", meaning you need to think about the output of teeny build for them to work.

The last command that Teeny supports is teeny develop. This creates an HTTP server to server files from the public/ subdirectory.

It listens for changes to the files and updates the static files on the fly (naively, by just rebuilding everything each time it detects a change).

Plugins

You can extend Teeny's functionality by using plugins.

A plugin looks like this:

module.exports = function rssPlugin(config = {}) {
    const {
        siteUrl = '',
        title = 'RSS Feed',
        description = '',
        outputPath = 'feed.xml',
    } = config

    const items = []

    return {
        name: 'test-plugin',
        version: '1.0.0',
        onPage: ({ pagePath, frontmatter, document, markdown }) => {
            // runs for every page built
            // allows you to change content in a page or use the content for e.g. making an RSS feed
        },
        onBuildComplete: (fs, outputDir) => {
            // runs after all pages have been built and the public/ directory populated
            // let's you write new files like an RSS feed or a JSON index to the final build
        },
    }
}

Plugins can have two functions:

  • onPage receives the path, the parsed frontmatter, the raw markdown content, and the generated DOM object from built pages, allowing you to inject content and make changes to the pages.
  • onBuildComplete receives fs from the Node stdlib and the output directory to write files to, letting you add new files like an RSS feed to the final output.

Plugins should be defined in teeny.config.js as follows:

module.exports = {
    plugins: [
        require('./plugins/example-plugin')({
            siteUrl: 'https://example.com',
            title: 'My example plugin',
            description: 'An example plugin',
        })
    ],
}

Note that the order plugins are defined in determines the execution order.

Contributing

Teeny has been completely hand-written, with the exception of one commit that made me realize I wanted to keep LLMs away from this project.

Note that I'm not against LLMs and use them extensively, including in all my other projects, but I decided that this one would remain "artisinal".

What that means is that while I will accept contributions here, I'd like for you to attest that it was written by hand so we can keep the spirit alive.

Bug fixes are the most welcome contributions, and features will be considered as well, as long as we're keeping the source code tiny, understandable, and in one file.

Now that plugins exist, the best way to extend Teeny's functionality is by writing your own plugins. You're also more than encouraged to fork Teeny and build on top of it.

About

A super simple static site generator

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors