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

@grovemotorco/livery-core

v1.0.0

Published

Deterministic SVG renderer for code snippet images

Readme

@grovemotorco/livery-core

The renderer behind Livery: turns a config object into an SVG image of syntax-highlighted source code, plus the layout metrics that produced it.

npm install @grovemotorco/livery-core

Why SVG only

Tools in this space usually preview with DOM + CSS and export with a headless browser, and the two drift — fonts substitute, sub-pixel rounding differs, backdrop-filter silently disappears. Here SVG is the only render target, and it is the same string whether you inline it in a page, rasterize it in a Worker, or rasterize it on a laptop.

Because code is monospace, the renderer computes every glyph position itself rather than trusting each renderer's text shaping:

charWidth = fontSize × font.advance          // 0.6 for JetBrains Mono
x         = codeLeft + column × charWidth
baseline  = lineTop + (lineHeight − (asc + desc) × fontSize) / 2 + asc × fontSize

The metrics come out of the vendored TTFs, not from guesses.

Rendering an SVG

import { resolveConfig, renderSnippet } from "@grovemotorco/livery-core";

const config = resolveConfig({
  code: "const answer = 42;\n",
  language: "ts",
  theme: "grove-dark",
});

const { svg, width, height, layout, themeBg } = await renderSnippet(config);

resolveConfig validates and fills in defaults — it is a Zod schema, so unknown or out-of-range values throw rather than rendering something surprising. renderSnippet returns a RenderResult: the svg string, its width and height, the full layout, and the theme's background and foreground colours (handy for matching surrounding chrome).

Pass { fontFaceCss } when the SVG has to stand alone — for a canvas export, or any context that has not already loaded the fonts. Pass { idPrefix } when several SVGs share a page, so their generated element ids do not collide.

There is also renderSnippetSync(config, highlighted, options) if you already hold a Highlighted result and want to stay off the event loop.

Rasterizing to PNG

Rasterizing is opt-in, because it costs a ~2.5 MB WASM binary. You supply the binary and the font bytes; the package never does I/O itself, which is what lets it run unchanged in a Worker, in Node, and in the browser.

import { readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import {
  FontRegistry,
  initRasterizer,
  rasterizeToPng,
  renderSnippet,
  resolveConfig,
} from "@grovemotorco/livery-core";

const require = createRequire(import.meta.url);
await initRasterizer(await readFile(require.resolve("@resvg/resvg-wasm/index_bg.wasm")));

// Fonts ship with this package and are reachable as subpath exports.
const fonts = new FontRegistry(async (path) => {
  try {
    return new Uint8Array(
      await readFile(require.resolve(`@grovemotorco/livery-core/fonts/${path}`)),
    );
  } catch {
    return null;
  }
});

const config = resolveConfig({ code: "const answer = 42;\n", language: "ts" });
const { svg, width } = await renderSnippet(config);

const png = await rasterizeToPng(svg, {
  width: width * 2, // scale
  fontBuffers: await fonts.buffersFor(config.font.family),
  fontFamily: config.font.family,
});

initRasterizer is safe to call repeatedly — only the first call does work, and a failure is not cached, so a transient fetch error can be retried.

Backgrounds

resvg performs no I/O, so any background image has to be embedded before rendering or it comes out blank:

import { inlineBackgroundImage, renderSnippet } from "@grovemotorco/livery-core";

const embedded = await inlineBackgroundImage(config, { loadAsset });
const { svg } = await renderSnippet(embedded);

Remote URLs are restricted to https, 5 MB and a 5 s timeout. Bundled backdrops resolve through the loadAsset loader you provide, the same shape as fonts.

What ships

dist/index.mjs    the renderer (ESM), with dist/index.d.mts alongside
fonts/            six OFL monospace families, as TTF
backdrops/        the bundled wallpapers

Fonts and backdrops are reachable as subpath exports — @grovemotorco/livery-core/fonts/jetbrains-mono/regular.ttf — so a bundler can fingerprint them like any other asset.

TTF and not WOFF2 because resvg's font parser cannot decompress WOFF2. Using one file format for the browser and the rasterizer removes a whole class of "the export doesn't match the preview" bugs.

Shiki grammars and themes are listed explicitly and imported on demand, so a bundler emits chunks only for the languages you actually reach — not for all ~350 grammars.

Limits worth knowing

  • No backdrop-filter. SVG has no equivalent and neither does resvg; window translucency is a plain alpha fill over the backdrop.
  • No conic gradients. SVG has none. The mesh background type stacks radial gradients to cover the same ground.

Requirements

Node.js 20 or newer, or any modern browser or edge runtime. ESM only.

Related

Licence

MIT. Bundled fonts are OFL 1.1 and ship their own LICENSE.txt beside the TTFs, and the Grove Motor Co wallpapers are not covered by the MIT grant — see LICENSE.