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

hono-aep-webc-factory

v0.2.0

Published

Build-once compiler for no-build sites: React/Vue/Svelte/Lit sources — or components you didn't write — become self-registering custom elements. The build lives here so the site never needs one.

Readme

hono-aep-webc-factory

The build lives here so your site never needs one. Point it at a React, Vue, Svelte, or Lit source — or at a component you didn't write — and get back a single self-registering .gen.js custom element that any plain HTML page can use with one <script type="module">.

This package exists to widen what a no-build site can reach, not to introduce a way of writing things. It owns no tags, ships no components, and has no runtime presence on your site.

bun add -d hono-aep-webc-factory
bunx webc-factory build

Config

webc.config.json next to your sources:

{
  "outDir": "../docs/js/components",
  "external": ["#stores", "nanostores"],
  "entries": [
    { "src": "lit/counter.js" },
    { "src": "react/counter.tsx" },
    { "src": "svelte/counter.svelte" },
    { "src": "vue/counter.vue", "tag": "my-counter-vue" }
  ]
}
  • engine is inferred from the extension (.svelte, .vue, .tsx/.jsx → react, .js/.ts → lit); set "engine" to override.
  • name defaults to the source basename → <name>.gen.js.
  • tag is required for Vue, because that's the engine where we generate the registration. Svelte declares its own tag via <svelte:options customElement={{ tag, shadow: "none" }} />; Lit and React sources call customElements.define themselves.

external is the one setting that matters

Anything holding shared state must be listed there. Externals are left as bare specifiers and resolved at runtime by the page's import map, so every artifact and the host page share one module instance. Bundle your store instead and you get one silent copy per artifact — components stop seeing each other's updates and nothing errors.

Wrap a component you didn't write

The reason this package is worth installing:

bun add react-select
bunx webc-factory wrap react react-select --tag x-select --build

You get <x-select> usable from plain HTML. Attributes become props (kebab → camel, JSON-parsed when parseable); rich values go through the element's props property:

<x-select options='[{"value":"a","label":"A"}]'></x-select>
<script type="module">
  document.querySelector("x-select").props = { onChange: (v) => console.log(v) };
</script>

Headless / compound libraries

wrap inspects the export before generating. Libraries like Base UI, Radix and Ark export a namespace of parts (Root, Track, Thumb…) rather than one component — mounting that namespace throws. When one is detected you get a compose scaffold instead, and you write the markup:

bunx webc-factory wrap react @base-ui-components/react/slider \
  --tag x-slider --export Slider
# ℹ exports a namespace of parts (Root, Control, Track, Thumb, …) — headless.
#   Emitting a compose scaffold; the markup is yours to write.

That split is the point: the library owns behavior, state and accessibility; the arrangement and the CSS stay yours. Force it either way with --compose.

Honest limits: the component is bundled, so it must be installed here; components that import their own CSS need that CSS included on your page; and prop-shapes beyond primitives need the props property rather than attributes. Vue wrapping rides on defineCustomElement, so it is close to free; React wrapping mounts a root per element.

What it handles for you

The footguns this package exists to have already stepped on:

  • process.env.NODE_ENV="production" — otherwise you ship React's dev build (383KB instead of 182KB, in a real measurement).
  • __VUE_OPTIONS_API__ / __VUE_PROD_DEVTOOLS__ / __VUE_PROD_HYDRATION_MISMATCH_DETAILS__ — without these a Vue artifact throws ReferenceError at runtime, not at build time.
  • Svelte compiles in runes mode, so legacy Svelte-4 store syntax is a compile error rather than a silent fallback.
  • Vue SFC templates are compiled to render functions — no runtime compiler in the artifact — with isCustomElement set so your other web components pass through untouched.
  • A provenance banner on line 1 of every artifact.
  • Externals are matched by an EXACT filter. A catch-all resolve hook claims every resolution in the graph, and packages that resolve through an exports map then vanish from the bundle silently — the build "succeeds" and the artifact throws ReferenceError: <minified name> is not defined at runtime. Fixed in 0.1.1; found by wrapping Base UI.

Weight is the trade

Each artifact carries its own runtime — N React artifacts means N copies of React. Measured on the reference site: lit 15KB · svelte 51KB · vue 69KB · react 182KB (minified). If you want several React components, load one shared runtime lazily instead with hono-aep-react-jit. The build prints sizes on every run so the trade stays visible.