npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

wolfdog-generator

v1.0.2

Published

Simple and straightforward static site generator

Readme

wolfdog-generator

npm

Simple and straightforward static site generator, with as few dependencies as I could realistically manage without going insane. Uses Handlebars for templating, and good ol' raw HTML for page content.

How to Use

Set up a directory with this layout:

my-cool-site/
├── posts/
│   ├── some-topic-or-other.html
│   ├── some-topic-or-other.json
│   ├── another-topic-i-care-about.html
│   ├── another-topic-i-care-about.json
│   └── ...
├── static/
│   └── ...(stuff placed here will be copied as is)
├── templates/
│   ├── additionalPages/
│   │   └── index.html.hbs
│   └── post.html.hbs
└── wolfdog.json

Then run:

npm install --save-dev wolfdog-generator
npx wolfdog build

Be sure to have a look at the examples!

The Manifest: wolfdog.json

This is the main config file, usually referred to as the Manifest. The smallest allowed config file that simply uses the default config values for everything is:

{ "version": "1.0.0" }

There is a JSON schema for Wolfdog Manifest files, for type checking and autocompletion in text editors. Use it by adding the $schema property to your manifest:

{
  "$schema": "./node_modules/wolfdog-generator/wolfdog.schema.json",
  "version": "1.0.0"
}

Probably the most immediately-useful property in the manifest is additionalValuesInTemplateScope, which lets you inject values into all of the templates. Put your website's title here, for example:

{
  "$schema": "./node_modules/wolfdog-generator/wolfdog.schema.json",
  "version": "1.0.0",
  "additionalValuesInTemplateScope": {
    "siteName": "My Cool Site",
    "myFavoriteStaticSiteGenerator": "Wolfdog Generator!"
  }
}

For a full list of every allowed property and its default value, see either the JSON schema or types.ts.

Posts

Posts are given as two files:

  • An HTML file that contains the actual post content,
  • And a JSON file for metadata and additional values to be passed on to the Handlebars template

There is a JSON schema included for post metadata files. To use it, add the $schema property to your post metadata files:

{
  "$schema": "./node_modules/wolfdog-generator/post.schema.json",
  "title": "On Regression and Overthinking",
  "pubDate": "2020-01-20T00:00:00-07:00",
  "additionalValuesInTemplateScope": {
    "description": "Exploring some thoughts on going backwards and thinking too much.",
    "customPubDate": "Originally written while still living in Sawtooth"
  }
}

The post template (by default templates/post.html.hbs, determined by postSettings.postTemplate in the manifest) is then executed for each post with a scope that looks something like this:

const SCOPE = {
  post: {
    slug: "on-regression-and-overthinking",
    title: "On Regression and Overthinking",
    fullPath: "posts/on-regression-and-overthinking/index.html",
    fullPathDirname: "posts/on-regression-and-overthinking",
    pubDate: "Januray 20, 2020",
    pubDateIso: "2020-01-20T00:00:00-07:00",
    pubDateRfc: "Mon, 20 Jan 2020 00:00:00 -0700",
    content: "(the entire HTML content of the post)",
    additionalValues: {
      description:
        "Exploring some thoughts on going backwards and thinking too much.",
      customPubDate: "Originally written while still living in Sawtooth",
    },
  },
  additionalValues: {
    // (from the manifest)
  },
  generatorTag: "Wolfdog Generator 1.0.2",
};

Posts are output, by default, to dist/posts/{{post.slug}}/index.html. To change this, refer to the postSettings.postOutputFileTemplate subproperty in the manifest file.

The format and locale of pubDate in the templating scope are determined by the postSettings.postPubDate.formatString and postSettings.postPubDate.locale sub-sub-properties.

Additional Pages

In addition to individual posts, Wolfdog also lets you define templateable pages. These are given as Handlebars templates, by default under the directory templates/additionalPages/ (configurable using additionalPageTemplatesDir in the manifest)

This directory and all subdirectories are recursively scanned, and any files with names that end in .hbs are interpreted as Handlebars templates. (Files without the .hbs extension are ignored). The directory structure is kept and copied over, and the resulting files get the same name as the input files, but without the .hbs extension.

For example, the following tree:

additionalPages/
├── about/
│   ├── index.html.hbs
│   └── socials.html.hbs
├── index.html.hbs
└── rss.xml.hbs

Will result in the following:

dist/
├── about/
│   ├── index.html
│   └── socials.html
├── index.html
└── rss.xml

Each "additional page" template is executed with the following scope:

const SCOPE = {
  allPosts: [
    // sorted by descending order of publication; newest first, oldest last
    {
      // (identical to the `post` property in the Posts section)
    },
    {
      // (identical to the `post` property in the Posts section)
    },
    // ...
  ],
  additionalValues: {
    // (from the manifest)
  },
  fullPath: "full/path/to/page.html",
  fullPathDirname: "full/path/to",
  generatorTag: "Wolfdog Generator 1.0.2",
};

Partial Templates

For convenience, you can use Handlebars' Partials feature in your templates. Before templating any pages, Wolfdog will recursively iterate over everything in the directory templates/partials/ (configurable using partialTemplatesDir in the manifest), and load any file it finds as a Handlebars partial. The name of the partial will be the path to the file, relative to the partial templates directory.

For example, the following tree:

partials/
├── util/
│   └── identity.html.hbs
├── main-template.html.hbs
└── rss-template.xml.hbs

Will result in three partials being loaded:

  • One called main-template.html.hbs,
  • One called rss-template.xml.hbs,
  • And one called util/identity.html.hbs

Preprocessing and postprocessing (SASS, Typescript, etc.)

This will probably never be supported. There's plenty of other, more capable and chonkier static site generators that support that kind of elaborate stuff. My use case is simple, so Wolfdog stays simple.

Wait, why avoid dependencies?

I feel like we have become too reliant on npm installing stuff like Leftpad or whatever, that then pulls in 2,147,483,647 more dependencies. Whenever vulnerabilities are discovered, or NPM randomly decides to take down some package, it creates massive unnecessary headaches for everyone.

Not counting dev-dependencies, this project currently has 4 direct dependencies:

  • docopt -- for argument parsing (0 dependencies)
  • luxon -- for date stuff (0 dependencies)
  • zod -- for type validation (0 dependencies)
  • handlebars -- for templating (5 dependencies, that all have 0 dependencies)

These packages implement pretty important functionality that would be a headache to implement myself, and more importantly, all except one of them have 0 dependencies of their own, minimizing the potential for future NPM headaches.

Wolfdog?

Awoooooooooooo!