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

pawikan

v0.2.1

Published

A TypeScript-first frontend framework that compiles .paw to TypeScript to JavaScript. Svelte/Solid-style, no virtual DOM, with SSR. Pairs with Bao; backend agnostic.

Readme

Pawikan

A TypeScript-first frontend framework that compiles .paw to TypeScript to JavaScript. Svelte/Solid-style compile-time reactivity, no virtual DOM, with SSR. Built to pair with Bao (first-class), but backend agnostic.

Pawikan shares the .paw syntax with Pating (the server-rendered Python templating engine). Pating renders .paw on the server in Python; Pawikan compiles the same syntax to JS for SPA/SSR/desktop.

This is a private, work-in-progress repository. It has no remote.

Pipeline

.paw -> lexer -> parser -> AST -> TypeScript generator -> esbuild/SWC -> JavaScript

See docs/SPEC.md for the design and locked decisions.

Develop

npm install
npm run build      # tsc -> dist/
npm test           # vitest unit suite (133 tests, fast, no browser)
npm run test:e2e   # Playwright headless-Chromium browser test (see below)

Browser e2e (Playwright)

The unit suite runs under Node/happy-dom. A separate, heavier Playwright suite (e2e/, npm run test:e2e) drives a real headless Chromium to prove the Precognition live-validation flow end to end in a browser: it compiles a .paw form with p-precognition, serves it with the real pawikan/runtime, stands up a /validate stub (422 with messages for a bad value, 204 for a good one, honoring the Precognition: true header), then types into the email field and asserts the .field-error appears after the debounce + fetch and clears on a valid value. First run downloads Chromium with npx playwright install chromium. It is intentionally kept out of npm test so the unit run stays fast.

Status

First working build of the full pipeline .paw -> lexer -> parser -> AST -> TypeScript generator -> esbuild -> JavaScript. Svelte/Solid-style compile-time reactivity, no virtual DOM. Verified with npm run build, npm run typecheck (clean), and npm test (133 tests passing). The minified client runtime is 5.64 KB (budget: < 20 KB).

Production-ready (built and tested)

  • Compiler core — lexer, expression sub-parser, parser, and AST. Mirrors the Pating grammar for portability: {expr} interpolation, <if>/<elif>/<else>, <for x in items> with loop helpers and <else>, <set>, <include>, <extends>/<block>, <macro>/<call>, comments, whitespace control, and the client directives (p-data/state:, p-text/p-html/p-show/p-if, :bind/p-bind, @event/p-on with modifiers, p-model, p-drag, p-canvas). Plus the section-style component form (state:/load:/watch:/template:). Line/column-accurate diagnostics throughout.
  • Client/TypeScript generator — emits a reactive mount() module with fine-grained bindings (text, attrs, show, model, events, <if>/<for> blocks). Only emits interactive code when the template needs it.
  • Client runtime (pawikan/runtime) — signals/computed/effect + DOM binding helpers, no virtual DOM. Tiny (6.85 KB minified).
  • Drag & canvas (Tier 1/2) — p-drag makes an element draggable via pointer events, writing position to style.transform (GPU-composited, no layout); with an expression it is controlled (p-drag="pos" reads/writes a reactive {x, y}, like p-model for position) and .x/.y lock an axis. p-canvas binds a <canvas> to a reactive draw expression (p-canvas="drawScene(ctx, bars())") that repaints, coalesced to one frame via requestAnimationFrame, whenever a signal it reads changes. Both are direct-DOM bindings — still no virtual DOM. Backed by the draggable() / bindCanvas() runtime helpers.
  • SSR generator + layer — AST to an HTML-string render(ctx) function; interpolation with autoescaping, filters, <if>/<for>, macros, attributes.
  • Component slots<slot/> (default) and <slot name="x"/> with fallback content; a <call name(args)>...</call> site fills them with bare children (default slot) and <template slot="x">...</template> (named slots). Compiled in both generators (SSR slot functions; client __props.__slots).
  • Built-in UI components (pawikan/components) — Tier 1: field-errors, pagination, flash (dismissible, reactive on the client), csrf, and method, authored in .paw and matching the Pating contract (same names, props, and data shapes). renderComponent(name, ctx) server-renders them.
  • Authorization components (Tier 2) — <auth>, <guest>, and <can ability="update" of="post"> wrap children and render them only when the condition holds. Context/props provide current_user (truthy when authenticated) and a callable can(ability, resource?) => boolean, matching the Pating/Bao contract. They desugar to <if> at parse time, so they work in both the SSR and reactive client output with zero added runtime weight.
  • Asset stacks (Tier 2) — <stack name="scripts"/> is a placeholder; any <push name="scripts">...</push> (anywhere, including nested components/macros/loops) appends to it, and the content surfaces at the stack in push order. <push ... once> dedupes a block to a single emission per render. SSR collects pushes into a per-render registry and resolves the placeholders at the end of render(), so a push can target a stack declared earlier or later. On the reactive client they are intentionally no-ops (see known limits) since stacks are a server/document-head concern.
  • Localization (Tier 2) — t(key, params?) is available in template scope (SSR via ctx.t/ctx.translations, client via __props.t/__props.translations). A missing key returns the key; {param} placeholders interpolate from params. Use it inline ({ t('welcome', { name }) }, with object shorthand) or as a component (<t key="welcome" name="{user}"/>). A caller-supplied t function takes precedence over the built-in table lookup. Matches the Bao + Pating contract.
  • Notifications (Tier 2) — <notifications> (in pawikan/components) renders an inbox from notifications: [{ id, level, title, message, read, created_at }]. Unread items get the is-unread class (read items is-read) so unread is distinct, the header shows an unread count (derived, or passed as unread_count), and an empty list renders an is-empty placeholder. SSR + reactive client (the read flag is reactive).
  • Reactive Precognition live validation (Tier 2) — useForm({ url }) (and the p-precognition="/url" form directive via bindPrecognition(root)) debounce changed fields, POST them with the Precognition: true header, and bind the returned { field: [messages] } into a reactive errors signal that the .field-error markup reads. 422 shows messages; 204 clears them. This is now verified end to end in a real headless browser (Chromium) via the Playwright suite (npm run test:e2e), not just under happy-dom: the browser test types into a compiled .paw form and asserts the message appears and clears across the real debounce + fetch + reactive DOM update.
  • compile() APIsource -> { code, css, map, ast, ts, meta } via esbuild.
  • Router manifest + matcher — file-based routing: index, static, [param], [...catchAll], _layout association; URL matching with param extraction.

Functional first cut (works, with known limits)

  • CLI (paw build web|ssr|static, paw routes) — compiles/writes real output. paw dev shells out to Vite if installed (thin launcher, not a custom dev server).
  • Vite plugin (pawikan/vite) — transforms .paw on import; injects extracted CSS via a dev style tag (no production CSS extraction yet).
  • SSR hydrationrenderToString({ hydrate: true }) emits a data-paw-h marker + a bootstrap script that mounts the client component. This is full client remount, not true partial/resumable hydration.

Gaps / TODO (not done or partial)

  • Cross-file layout composition. <extends>/<block> parse into the AST and single-template blocks render, but the SSR layer does not yet load a parent layout file and merge child blocks into it. <include> likewise parses but is not resolved against the filesystem at render time.
  • SPA routing runtime is a first cut: history push/pop, click interception, and param extraction work, but nested-layout composition at runtime and route data-loading are left to the caller. No code-splitting.
  • paw build static does not inject dynamic route params, so [slug] pages render with empty params. Static export of dynamic routes needs a params source.
  • Macros / <set> / <include> in client components are not compiled (they work in SSR). Client components focus on the reactive subset.
  • Asset stacks are SSR-only. <stack>/<push> collect and emit during server render; in a client (mount) component they are no-ops. Document-head asset injection is a server concern, so this is by design rather than a TODO.
  • p-transition is emitted as a marker attribute only; no transition engine.
  • Selective/resumable hydration, islands, and SSR streaming are not implemented.
  • CSS is extracted from <style> blocks but is not scoped per-component.
  • List reconciliation is correctness-first (full re-render on change), not a keyed minimal-diff reconciler.