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

@phillipsharring/graspr-build

v0.3.4

Published

Build mechanics for Graspr sites: HTML compiler, page baker, Vite dev plugin

Readme

@phillipsharring/graspr-build

Graspr

Build mechanics for Graspr sites: HTML compiler, static page baker, and Vite dev plugin.

This package contains everything needed to build a Graspr site, separate from the runtime concerns (HTMX, Handlebars, auth) that live in @phillipsharring/graspr-framework. Static sites can depend on graspr-build alone; full apps depend on both.

Install

npm install -D @phillipsharring/graspr-build vite @tailwindcss/vite tailwindcss

Project shape

my-site/
├── content/
│   ├── layouts/        # base.html, etc  - shared shells
│   ├── components/     # custom-tag templates: lnk.html, callout.html, ...
│   └── pages/          # one HTML file per route
├── public/             # static assets, copied as-is
├── src/
│   ├── app.js          # vite entry  - at minimum, imports CSS
│   └── styles/         # CSS (tailwind v4 @theme block, etc)
├── site.config.js      # siteName, siteUrl, copyright, ...
└── vite.config.js

vite.config.js

import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';
import { grasprBuild } from '@phillipsharring/graspr-build/vite';
import siteConfig from './site.config.js';

export default defineConfig({
    root: 'src',
    publicDir: '../public',
    plugins: [tailwindcss(), grasprBuild({ siteConfig })],
    build: {
        outDir: '../dist',
        manifest: true,
        emptyOutDir: true,
        rollupOptions: { input: { app: './src/app.js' } },
    },
});

package.json

{
    "scripts": {
        "dev": "vite",
        "build": "vite build && graspr-build-pages",
        "preview": "vite preview"
    }
}

Page format

<layout name="base" title="About" />
<page-head>
<meta name="description" content="..." />
</page-head>

<h1>About</h1>
<callout type="info">Page content here.</callout>
  • <layout name="..." title="..." /> self-closing tag at the top picks the layout from content/layouts/
  • <page-head>...</page-head> block injects extra <head> content
  • Everything after is the page body, slotted into the layout's [[app]] placeholder

Component format

content/components/callout.html:

<aside class="rounded border border-blue-300 bg-blue-50 p-4">
    [[#if title]]<h3 class="font-bold">[[title]]</h3>[[/if]]
    [[slot]]
</aside>
  • [[prop]] - HTML-escaped prop
  • [[{prop}]] - raw prop (for attribute values, HTML snippets)
  • [[slot]] - child content
  • [[#if flag]] ... [[else]] ... [[/if]] - boolean conditional on a prop's truthiness

Custom tags can be either HTML custom-element style (<my-callout> - must contain a hyphen) or single-word tags (<callout> - works as long as a matching callout.html exists in content/components/).

Programmatic API

import { renderPage, buildPages } from '@phillipsharring/graspr-build';

// Bake all pages under content/pages/ to dist/
await buildPages({ root: process.cwd(), siteConfig });

// Render a single page (used by buildPages and the dev plugin)
const html = await renderPage({
    layoutsDir: 'content/layouts',
    componentsDir: 'content/components', // string OR string[]
    pagePath: 'content/pages/index.html',
    siteConfig,
    jsSrc: '/assets/app-XXXX.js',
    cssHref: '/assets/app-XXXX.css',
});

componentsDir: string or array

renderPage accepts componentsDir as either a single directory or an array of directories. When resolving a custom tag like <callout>, the compiler tries each directory in order and uses the first match. This enables a future module system to contribute partials from src/modules/*/partials/ alongside the project-level content/components/ without changing the API.

License

MIT