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

@alwatr/weaver

v10.4.0

Published

A bun-native build tool and dev server that composes @alwatr/loom (TSX→HTML), Bun's bundler (client JS), and Tailwind CSS — with in-memory serve and native live-reload.

Readme

@alwatr/weaver

weaver is a bun-native build tool and development server for @alwatr/loom sites. It is the composition layer loom intentionally leaves out of scope: loom renders TSX to HTML, and weaver wraps that with client-JS bundling, Tailwind CSS, static assets, minification, an in-memory dev server, and native browser live-reload — all on Bun.

Why a rewrite (vs. the original @alwatr/weaver)

| | @alwatr/weaver (old) | @alwatr/weaver | | ----------- | ------------------------------------------------------------------ | ----------------------------------------------- | | HTML engine | Eleventy / Nunjucks (.njk) | @alwatr/loom (.tsx), in-process | | HTML build | separate child process (to dodge 11ty's watch-mode cache bugs) | a render() function you supply | | Config | auto-discovered weaver.config.ts (file magic) | an explicit object you construct | | Dev watch | bespoke incremental watcher | bun --watch (pages) + in-process JS/CSS watch | | Live-reload | — | Bun-native WebSocket + build id |

The old tool offloaded HTML generation to a child process only because Eleventy's incremental cache went stale in watch mode. weaver sidesteps that: start the dev server under Bun's native watcher and a page change restarts the process — a fresh process re-imports the entire render graph (pages, partials, loom), so HTML is always current with no temp files, no re-bundling, and no stale module cache. Client JS and CSS are Bun.build inputs that live outside that import graph, so weaver watches them itself (see below).

Explicit configuration — no magic

There is no config-file discovery and no dynamic config naming. Each app declares its build in its own src/weaver.ts. For loom sites use the @alwatr/weaver facade: hand it the page registry and it renders with loom's buildSite, then runs the CLI.

// app/<name>/src/weaver.ts
import {weave} from '@alwatr/weaver';

import {pages} from './site.js';

await weave({
  pages,
  outDir: 'deploy/dist',
  publicDir: 'public',
  scripts: ['src/main.ts'],
  styles: ['src/main.css'],
});
// app/<name>/src/site.ts — a pure page registry, nothing else
import type {Page} from '@alwatr/loom';

import {aboutPage} from './page/about.jsx';
// …

export const pages: Page[] = [aboutPage /* … */];

Because freshness comes from the process restart (not from re-bundling), passing the live pages array is always current in watch mode.

Render-agnostic core

The core (@alwatr/weaver) never imports loom. It takes a render() function returning {permalink, html}[]; the /loom subpath is the one place weaver couples to loom. Non-loom callers use the core directly:

import {Weaver} from '@alwatr/weaver';

await new Weaver({
  render: () => myRenderer(), // () => RenderResult[] | Promise<RenderResult[]>
  outDir: 'dist',
  scripts: ['src/main.ts'],
  styles: ['src/main.css'],
}).run(Bun.argv.slice(2));

CLI

bun src/weaver.ts build                  # one-shot build to outDir (honors NODE_ENV)
NODE_ENV=production bun src/weaver.ts build
bun --watch src/weaver.ts serve          # dev server :3000, live-reload, restart-on-change
bun --watch src/weaver.ts serve -p 8080  # dev server :8080
bun src/weaver.ts --help

| Option | Meaning | | ---------------------------- | ----------------------------------------------------------------------------- | | build (default) | Build the site to disk. | | -s, --serve | In-memory dev server + live-reload. Run under bun --watch for the dev loop. | | --prod / --dev | Force production / development output. | | -p, --port <n> | Dev server port (default PORT env or 3000). | | --host <h> | Dev server hostname (default 0.0.0.0). | | --out <dir> | Override the output directory. | | --no-reload | Disable live-reload injection. | | -v, --verbose | Verbose logging. | | -h, --help / --version | Help / version. |

How the dev loop works

Watch is split by what each tool can actually see:

  1. Pages / render graph (.tsx, loom). bun --watch src/weaver.ts serve lets Bun watch the server's import graph (site.ts → pages → loom) and restart the process on a change — re-rendering over a fresh module graph. serve itself just builds once and serves.
  2. Client JS + CSS. These are Bun.build inputs, not imports of the server process, so bun --watch can't see them. weaver watches the watch roots (default ['src']) in-process and rebuilds only the affected bundle on change.
  3. Serves from memory. Generated HTML/JS/CSS/fonts live in memory; nothing is written to outDir. Static files are streamed straight from publicDir on demand.
  4. Live-reload. Each process run has a unique build id (restart → new id → reload); an in-process JS/CSS rebuild publishes a reload message. The injected client acts on either, and ignores a plain network blip (same id).

Scope

weaver owns bundling, CSS, static copy, minification, serving, and watching its bundler inputs (JS/CSS). loom owns TSX→HTML, and bun --watch owns watching the render graph. The core is decoupled from loom — it consumes a render() function's plain {permalink, html} output via structural typing; the @alwatr/weaver adapter is the single, opt-in coupling point.