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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lukeed/bongo

v0.0.12

Published

WIP

Readme

bongo

A static site generator built on Rolldown and LightningCSS.

Published as @lukeed/bongo, but projects should install and reference it through the bongo alias:

bun add bongo@npm:@lukeed/bongo

All examples below use bongo.

Overview

bongo processes content files through a loader pipeline, applies layouts and partials with a tempura-based template engine, and bundles client assets with Rolldown for JavaScript and LightningCSS for CSS.

Usage

bongo build
bongo dev

CLI options:

bongo build -C site --minify
bongo dev -C site --host --port 3000

How It Works

When you run bongo build:

  1. Config is loaded from bongo.config.{ts,mjs,cjs,js} by walking upward from cwd. Bun and Deno import TypeScript config files natively. Node bundles the config through Rolldown to a temp .mjs and dynamic-imports it.
  2. Layouts and partials are collected from layouts/ and partials/. Handlebars files (.hbs) are compiled with tempura. JavaScript and TypeScript partials are loaded as modules.
  3. Content files are passed through registered loaders, then rendered through the matching layout chain.
  4. Rendered HTML is scanned for <bongo:style>, <bongo:script>, and <bongo:client> elements. Assets are registered and replaced with placeholder tokens.
  5. Asset groups are flushed. JavaScript goes through Rolldown; CSS goes through LightningCSS. Output filenames include content hashes.
  6. Placeholders are substituted with external asset URLs or inline asset contents.
  7. Pages are written to public/.

Config

import { markdown } from '@bongo/markdown';
import { shiki } from '@bongo/markdown/highlight';

export default {
	$site: {
		title: 'My Site',
		url: 'https://example.com/',
		lang: 'en',
	},

	$utils: {
		excerpt(text: string, len: number) {
			return text.slice(0, len);
		},
	},

	plugins: [
		markdown({
			highlighter: shiki({
				theme: 'github-dark',
				langs: ['js', 'ts', 'css', 'bash'],
			}),
		}),
	],
};

Config fields:

  • $site — site metadata; available in templates as {{$site.title}}. The url field also drives publicPath.
  • $assets — loader map keyed by file extension.
  • $utils — helper functions available in templates.
  • $partials — programmatic partial functions.
  • plugins — array of partial config objects, merged in order.

Loaders

Loaders transform content files by file extension. Register them with $assets:

export const $assets = {
	md(filename, content, utils) {
		return {
			filename: filename + '.html',
			content: renderedHtml,
			metadata: { title: '...' },
		};
	},
};

Note: You can use @bongo/markdown for a ready-made solution.

Loaders are chained by extension. A .md file produces .md.html, the .html extension stops the chain and enters the template pipeline.

Loader utilities:

  • utils.decode(uint8array) — convert a buffer to a string.
  • utils.render(partialName, args) — invoke a registered partial.
  • utils.frontmatter(content) — parse YAML-like frontmatter delimited by ---.

publicPath

The publicPath prefix applied to external asset URLs is derived from $site.url:

  • Root-served site (https://example.com) -> publicPath = /
  • Subpath-served site (https://example.com/blog/) -> publicPath = /blog/

No manual configuration is required.

Templates

Templates use tempura syntax. Asset entrypoints are declared with <bongo:style>, <bongo:script>, and <bongo:client> custom elements.

{{! layouts/html.hbs }}
<!doctype html>
<html lang="{{$site.lang}}">
<head>
  <meta charset="utf-8"/>
  <title>{{ $page.title }}</title>

  {{! External CSS: emits <link rel="stylesheet" href="/style.hash.css"/> }}
  <bongo:style src="app.css"></bongo:style>

  {{! Inline CSS: splices built file contents into a <style> block }}
  <bongo:style src="print.css" inline></bongo:style>
</head>
<body>
  {{{ $page.content }}}

  {{! External JS: emits <script type="module" src="/main.hash.js"></script> }}
  <bongo:script src="main.ts"></bongo:script>

  {{! Async external JS }}
  <bongo:script src="analytics.ts" async></bongo:script>

  {{! Inline JS: file contents spliced into the <script> element }}
  <bongo:script src="boot.ts" inline></bongo:script>
</body>
</html>

<bongo:style> attributes:

  • src="..." — path relative to assets/.
  • inline — splice built CSS directly into a <style> block. Omit for an external stylesheet link.
  • global — disable CSS modules auto-detection on *.module.css sources.

<bongo:script> attributes:

  • src="..." — path relative to assets/.
  • inline — splice built JavaScript directly into a <script> block.
  • async — add the async attribute to an external script.
  • defer — add the defer attribute for CJS output. ESM modules are deferred by the browser.
  • format="esm"|"cjs" — output module format. Defaults to esm.
  • target="..." — browserslist target string.

<bongo:client> bundles bongo's browser runtime and emits a module script that calls client(...), plus the hidden aria-live region used to announce page-title changes:

<bongo:client />
<bongo:client transitions />
<bongo:client transitions="slide" />

Multiple <bongo:client> tags on a page share one bundled runtime.

View Transitions

Add transition:name and transition:animate to HTML elements in a layout or partial. bongo rewrites them at build time.

<header transition:name="site-header">...</header>
<nav transition:name="nav" transition:animate="slide">...</nav>
  • transition:name="NAME" — removed from the element; view-transition-name: NAME is merged into the element's style attribute.
  • transition:animate="KEYFRAME" — requires a named @keyframes KEYFRAME in your CSS. bongo injects a <style data-bongo-transitions> block in <head> that wires the keyframe to matching view-transition pseudo-elements.

If transition:animate is present without transition:name, an automatic name is generated.

Partials

{{#partial src="header" }}
{{#partial src="card" title="Hello" }}

Template Variables

  • $site — global site config.
  • $page — current page. Includes path, url, title, summary, params, section, tags, and content.
  • $pages — every page in the build, sorted by path.
  • $section — pages scoped to the current page's section directory.
  • $tags — array of { tag, pages } entries.
  • $assets — configured loader map.
  • $utils — configured helper functions.

Content files ending in .hbs are tempura-compiled with the same context as layouts: $site, $page, $pages, $section, $tags, $assets, $utils, plus params as an alias for $page.params.

The current page's own $page.content is empty while its body is compiling. It is the value produced by that compile.

CSS

All CSS is handled by LightningCSS: @import bundling, autoprefixing, nesting, and optional minification.

.module.css files automatically enable CSS modules. Non-module files can opt in explicitly when registering the entry.

Dev Server

bongo dev starts the HTTP server and runs the initial build behind it. File changes in assets/, layouts/, partials/, or content/ trigger a rebuild.

Rebuilds push one of these SSE messages to the browser:

| Message | Meaning | |---|---| | { type: 'reload' } | Successful rebuild; browser reloads the page. | | { type: 'error', errors } | Build failed; full-viewport error overlay mounts. | | { type: 'error-clear' } | Next successful build; overlay unmounts, then the browser reloads. |

The hmr message type exists in the type union for forward compatibility. Today, file changes trigger a full reload.

Client Runtime

bongo/client turns a statically generated site into a client-navigated SPA.

import { client, navigate, on, off } from 'bongo/client';

The simplest setup is the <bongo:client /> element:

{{! layouts/html.hbs }}
<bongo:client transitions />

That expands at build time to:

<div id="bongo:live" role="status" aria-live="polite" style="...visually-hidden..."></div>
<script type="module">
  import { client } from "/client.D59fAaym.js";
  client({ transitions: true });
</script>

The runtime updates #bongo:live with the new page title on navigation. If the element is missing, announcements are skipped silently.

If you also need custom event handlers or boot code, keep a separate <bongo:script>:

<bongo:client transitions />
<bongo:script src="main.ts"></bongo:script>
import { on } from 'bongo/client';

on('bongo:navigated', ({ to }) => {
	console.log('navigated to', to.pathname);
});

Or call client() yourself:

import { client } from 'bongo/client';

client({ transitions: true });

ClientOptions

| Option | Type | Default | Description | |---|---|---|---| | transitions | boolean \| 'fade' \| 'initial' \| 'slide' \| string | false | View-transition preset. true / 'fade' injects a cross-fade keyframe. 'slide' injects a slide keyframe. 'initial' wraps the swap without injecting CSS. Custom string wraps only; user CSS provides the rules. Any falsy value disables transitions. Opt a link out with transition:disable. | | prefetch | boolean \| 'hover' \| 'tap' | false | Prefetch same-origin links into the in-memory HTML cache. true / 'hover' fires after hover settles, or immediately on touch. 'tap' fires on mousedown / touchstart. Opt a link out with prefetch:disable. |

Events

Subscribe with on(event, handler). It returns an unsubscribe function. Remove a specific handler with off(event, handler).

on('bongo:navigated', ({ to }) => {
	console.log('navigated to', to.pathname);
});

| Event | Detail shape | Fires when | |---|---|---| | bongo:ready | { url: URL } | Once, immediately after client() is called. | | bongo:navigate | { to: URL; from: URL } | Navigation intercepted, before the fetch. | | bongo:replace | { to: URL; from: URL; doc: Document } | HTML fetched and parsed; swap has not happened yet. | | bongo:replaced | { to: URL; from: URL } | DOM swapped. | | bongo:navigated | { to: URL; from: URL } | Navigation settled. | | bongo:error | { to: URL; error: Error } | Navigation fetch or swap threw. |

Programmatic Navigation

navigate('/about');
navigate('/old-url', { replace: true });

navigate() returns Promise<void> and resolves when navigation and any transition complete.

Opt Out

Force a full-page load:

<a href="/legacy" transition:disable>Legacy dashboard</a>

Disable prefetch without forcing a full-page load:

<a href="/logout" prefetch:disable>Log out</a>

Scroll Restoration

client() sets history.scrollRestoration = 'manual'.

  • Back / forward restores the saved {x, y} position for that URL.
  • Push / replace scrolls to the hash element if present, otherwise (0, 0).

Browser Support

The Navigation API is available in Chrome and Edge, and is behind a flag or unsupported in Firefox and Safari at time of writing. If the API is absent, client() emits bongo:ready and returns without installing navigation interception.

TypeScript

bongo ships ambient JSX types for its custom elements and attributes. Extend the bundled config:

{
	"extends": "bongo/tsconfig",
	"compilerOptions": {
		// your own settings
	}
}

The augmentation adds:

  • <bongo:client>, <bongo:script>, and <bongo:style> intrinsic elements.
  • transition:name and transition:animate on every HTML element.
  • transition:disable and prefetch:disable on anchors.

Or reference the types directly:

/// <reference types="bongo/jsx" />