API

What mothwing and mothwing/html export, for scripts that build a site themselves and for typing mothwing.config.js.

Functions

fasset(
  file: string,
  content: string | Uint8Array<ArrayBufferLike>
): Asset

A file for the build, made from data: a search index, a feed, a generated image. The build writes it, and a page links to it by this value, its URL with a hash of the content, like the JS and CSS links: <input data-index="{{ search.index }}"> -> data-index="/search.json?v=1a2b3c4d". The hash only changes when the content does, so browsers and CDNs can keep it until then.

fbuild(site: Site): Promise<void>

The production build, from an empty build folder.

fclean(site: Site): Promise<void>

Removes the build folder, what the production build starts with.

fdev(
  site: Site,
  { host, port }?: { host?: string; port?: number; }
): Promise<void>

Builds, serves and rebuilds what changes.

fescapeHtml(text: string): string

Escapes the characters that mean something in HTML, &<>"', so the text shows as it is.

fhtml(strings: TemplateStringsArray, ...values: any[]): Html

A template tag for HTML in .data.js files. What goes into it is escaped, unless it's Html already: another html..., raw(), or a list of those. null, undefined and false are left out.

floadConfig(root?: string): Promise<Site>

Imports a folder's mothwing.config.js and resolves it, what the CLI does before it builds.

flog(type: string, message: string): void

Prints a line the way mothwing's own are, [ HTML ] 2026-01-01 12:00:00 message, for hooks to report in.

fraw(value: string): Html

Marks a string as HTML already, for html... to put in without escaping. Only for HTML you trust.

frenderSite(
  site: Site,
  { only }?: { only?: Set<string>; }
): Promise<{ deps: Record<string, string[]>; written: number; }>

Renders the pages into an existing build, as dev does after a change. With only, just those pages, and the sitemaps are left as they are.

fresolveConfig(config: Config, root: string): Site

Checks a config and fills in its defaults. An unknown option, or one without a domain, throws.

fslug(name: string): string

Makes a name safe for a URL, for the slugs of generated pages.

funescapeHtml(html: string): string

Undoes escapeHtml, for text that comes escaped, like the value of an attribute.

Classes

cnew Html(value: string): Html

HTML that's safe to put in as-is.

value: string
The HTML itself
toString(): string
The HTML, so an Html can be put in a string as it is

Types

Ttype Asset

No documentation

path: string
Its path in the build, "/search.json"
content: string | Uint8Array<ArrayBufferLike>
What's written
url: string
The path with a hash of the content, which changes when the content does: "/search.json?v=1a2b3c4d"
toString(): string
The URL, so {{ }} and html... put it in as it is
Ttype Config

What a site's mothwing.config.js default exports. Only domain is needed. Paths are relative to src unless they say otherwise. The extras (minify to activeLinks) are off until asked for: true turns one on as it comes, an object with its own settings.

domain: string
"example.com", for the canonical URLs, sitemaps and CNAME
src?: string
The source folder, relative to the config. "src"
out?: string
The build folder, relative to the config. "dist"
pages?: string
The pages folder. "pages"
partials?: string
The partials folder, left out of the build. <x-html> takes a partial from anywhere in src, this is only the folder that isn't copied. "partials"
data?: string
The data folder, its files are in every page by their name: projects.json -> {{ projects }}. Left out of the build. "data"
notFound?: string
The 404 page, in the pages folder. Kept in the build's root, out of the sitemap. "404.html"
js?: JsConfig
The JS to bundle
sass?: Record<string, string>
Sass to compile, source to output: { "scss/index.scss": "css/index.css" }. Needs sass installed
exclude?: string[]
Folders left out of the build, on top of pages, partials and data: ["js", "scss"]
rerender?: string[]
Folders and files the pages read with fs, whose changes render every page again in dev. They can be outside src, "../README.md", and are watched too, Sass in them builds the CSS first. What the pages import, include or use from the data folder is followed on its own
seo?: SeoConfig
For <x-seo>
minify?: boolean
Minify the HTML of production builds: comments and the whitespace that doesn't show are taken out, and esbuild minifies the inline scripts and styles. The tags and their attributes stay as written
csp?: boolean | Record<string, string>
A Content-Security-Policy <meta> that allows only the site's own files, and its inline scripts by their hash. An object sets directives over the defaults: { "style-src": "'self' 'unsafe-inline'" }
sitemap?: boolean | SitemapConfig
sitemap.xml with every page, each dated by the last git commit to its files
robots?: boolean | RobotsConfig
robots.txt, allowing every crawler and pointing at the sitemap
cname?: boolean
A CNAME file with the domain, for GitHub Pages
activeLinks?: boolean | ActiveLink
aria-current="page" on links to the page they're on, "true" on links to its section
tags?: Record<string, TagFunction>
Your own tags: { "x-name": ({ attrs }) => "<b>html</b>" }
transforms?: Transform[]
Run on every page's HTML once its placeholders are filled
after?: Hook[]
Run once the pages are written
esbuild?: BuildOptions
esbuild options for every bundle, over mothwing's
Ttype JsConfig

Each file is bundled to the same path in the build, js/index.js -> /js/index.js.

scripts?: string[]
For <x-js src="..."/>, bundled on their own
modules?: string[]
For <x-js src="..." module/>, ES modules that share their common code in chunks
chunks?: string
Where the shared chunks go. A chunks folder next to the first module
Ttype RobotsConfig

What robots.txt turns away.

block?: string[]
Crawlers turned away from the whole site, by name
disallow?: string[]
Paths kept out of every crawler
Ttype Route

A page to write. Most come one to a file in the pages folder, a [name].html file gives one per item. Its props can have files, more files to write, by their path in the build.

source: string
"gathering/[item].html", relative to the pages folder
output: string
Where it goes in the build: "gathering/cedar-log/index.html"
url: string
"/gathering/cedar-log/"
props: Record<string, any>
Its own values, the data folder's are added when it's rendered
sources: string[]
Files its content comes from, relative to src, for the sitemap's lastmod
Ttype SeoConfig

What <x-seo> fills its tags with when a page doesn't say.

siteName?: string
Added to titles that fit, and the link previews' site name. The domain
locale?: string
"en_US"
image?: null | { src: string; width: number; height: number; alt: string; }
The link preview for pages without their own
maxTitle?: number
Titles longer than this leave out the site name, search results cut them off. 60
maxDescription?: number
Longer descriptions are cut at a word. 160
Ttype Site

What Config becomes after resolveConfig, and what tags, transforms and hooks get: every option filled in with its default, its folders absolute, and each extra either false or its settings.

domain: string
"example.com", for the canonical URLs, sitemaps and CNAME
partials: string
The partials folder, left out of the build. <x-html> takes a partial from anywhere in src, this is only the folder that isn't copied. "partials"
data: string
The data folder, its files are in every page by their name: projects.json -> {{ projects }}. Left out of the build. "data"
notFound: string
The 404 page, in the pages folder. Kept in the build's root, out of the sitemap. "404.html"
sass: Record<string, string>
Sass to compile, source to output: { "scss/index.scss": "css/index.css" }. Needs sass installed
rerender: string[]
Folders and files the pages read with fs, whose changes render every page again in dev. They can be outside src, "../README.md", and are watched too, Sass in them builds the CSS first. What the pages import, include or use from the data folder is followed on its own
minify: boolean
Minify the HTML of production builds: comments and the whitespace that doesn't show are taken out, and esbuild minifies the inline scripts and styles. The tags and their attributes stay as written
cname: boolean
A CNAME file with the domain, for GitHub Pages
tags: Record<string, TagFunction>
Your own tags: { "x-name": ({ attrs }) => "<b>html</b>" }
transforms: Transform[]
Run on every page's HTML once its placeholders are filled
after: Hook[]
Run once the pages are written
esbuild: BuildOptions
esbuild options for every bundle, over mothwing's
url: string
"https://example.com"
root: string
The config's folder
src: string
The source folder, absolute
out: string
The build folder, absolute
pages: string
The pages folder, absolute
exclude: string[]
Folders left out of the build, with pages, partials and data
js: Required<JsConfig>
The JS to bundle
seo: Required<SeoConfig>
For <x-seo>
csp: false | Record<string, string>
The Content-Security-Policy's directives
sitemap: false | Required<SitemapConfig>
sitemap.xml
robots: false | Required<RobotsConfig>
robots.txt
activeLinks: false | ActiveLink
aria-current on links to the page they're on and to its section
Ttype SitemapConfig

How sitemap.xml is split and dated.

sections?: string[]
Sections that get a sitemap of their own behind a sitemap index, the rest share sitemap-pages.xml. Without any, sitemap.xml has every page itself
dates?: boolean
Date each page by the last git commit to its files. true

Callback types

cbHook(build: { site: Site; routes: Route[]; dev: boolean; }): any

Run once the pages are written, dev is true on dev renders.

cbTagFunction(
  tag: { attrs: Record<string, string | true>; route: Route; site: Site; }
): string | string[]

Returns the HTML a tag turns into. A list is one line each, indented like the tag.

cbTransform(html: string, route: Route, site: Site): string

Run on every page's HTML once its placeholders are filled, returns the HTML to write.