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

@mithril-inspector/esbuild

v0.3.2

Published

The esbuild integration for Mithril Inspector — a thin adapter over the shared transform, runtime and server packages (§4, §12.4).

Readme

@mithril-inspector/esbuild

The esbuild integration for Mithril Inspector. A thin adapter over the shared transform (@mithril-inspector/transform) and runtime (@mithril-inspector/runtime) — same option shape and instrumentation behaviour as @mithril-inspector/vite/@mithril-inspector/rollup, reused via @mithril-inspector/adapter-kit rather than reimplemented (ADR-004).

Mithril Inspector: picking a component, drilling into its rendered DOM via the Elements tab, watching its attrs/state History timeline, and the redraw-flash visualization

Usage

// build.mjs / esbuild config
import * as esbuild from "esbuild"
import { mithrilInspector } from "@mithril-inspector/esbuild"

await esbuild.build({
  entryPoints: ["src/main.ts"],
  bundle: true,
  outfile: "dist/main.js",
  sourcemap: true,
  plugins: [mithrilInspector()],
})

mithrilInspector(options?) returns a single esbuild Plugin, built from build.onResolve/build.onLoad/build.onEnd — esbuild has no transform-per-file hook like Rollup/Vite, so instrumentation happens in onLoad instead, reading and transforming each matched file directly.

What it does — and doesn't

  • AST transformation — an onLoad hook filtered to .js/.jsx/.ts/.tsx (and their .mjs/.cjs/.mts/.cts variants) reads the file, calls the same transformMithrilModule every adapter uses, and returns the instrumented source with the right loader inferred from the extension. @mithril-inspector/adapter-kit's shouldAttemptTransform short-circuits before ever touching disk for node_modules and the inspector's own packages.
  • Runtime import resolution — an onResolve hook redirects virtual:mithril-inspector/runtime/.../overlay into a dedicated mithril-inspector-virtual namespace; a matching onLoad hook serves their generated source, which esbuild then bundles directly into whichever output chunk imports them (esbuild has no \0-prefix virtual-module convention — a plugin namespace is the idiomatic equivalent).
  • Watch modeesbuild.context(...).watch() re-runs onLoad for changed files automatically; the shared transform's own content-hash-keyed cache means a changed file simply produces a fresh cache entry.
  • Source maps — esbuild's onLoad has no dedicated map field (unlike Rollup/Vite's transform hook), so the transform's map is appended as a //# sourceMappingURL=data:application/json;... comment, which esbuild's own source-map handling picks up and chains into the final output map.
  • Helper development server — the opt-in devServer option (below), reusing @mithril-inspector/server's createInspectorMiddleware.

It does not inject anything into an HTML page — esbuild has no transformIndexHtml equivalent — see "Mounting the overlay" below.

Dev-only / minified-build guard

Active only when enabled (default: NODE_ENV !== "production") and the build is not minified, unless includeInProduction is set:

mithrilInspector({
  includeInProduction: true, // force it into a minified/production build too
})

esbuild has no command === "build" distinction a plugin can read the way Vite does, so initialOptions.minify is used as the production signal in addition to NODE_ENV — a plain esbuild --minify run excludes inspector code by default even without NODE_ENV=production set.

Helper development server

esbuild's own context().serve() has no middleware hook to mount the open-in-editor endpoint on, and it can't share an origin with a separately started startInspectorServer (its no-CORS posture requires same-origin). The devServer option solves both: it starts a small combined static-file + open-in-editor server, lazily on the first completed build (wired through onEnd), and stops it when the build context is disposed (onDispose):

mithrilInspector({
  editor: "code",
  devServer: { servedir: "public", port: 5178 }, // port optional, defaults to an ephemeral free port
})

Point your <script src="..."> tags (and the browser) at that server instead of esbuild's own serve() — it serves whatever is on disk under servedir (so it works fine alongside esbuild.context(...).watch(), which just keeps writing rebuilt output there) plus the /__mithril-inspector/open-in-editor endpoint at the same origin.

The same server is also exported standalone, for a companion script that doesn't want the plugin managing its lifecycle:

import { createEsbuildDevServer } from "@mithril-inspector/esbuild"

const handle = await createEsbuildDevServer({
  servedir: "public",
  inspector: { root: projectRoot, editor: "code" },
})
console.log(`Mithril Inspector: ${handle.url}`)
// ... later: await handle.close()

It binds to 127.0.0.1 only, matching startInspectorServer's security posture. The other two patterns @mithril-inspector/rollup documents — mounting createInspectorMiddleware in an existing dev server, or a startInspectorServer companion behind your own reverse proxy — work here too, unchanged.

Pass editor explicitly, as above — leaving it unset falls back to MITHRIL_INSPECTOR_EDITOR/LAUNCH_EDITOR/VISUAL/EDITOR from the process's own environment before defaulting to "code", and it can't be a terminal editor (vi, vim, nvim, emacs, nano) either way — see @mithril-inspector/vite's README for why.

Mounting the overlay

Since esbuild has no HTML injection hook, pick one of two patterns:

  1. Guarded import in application code (simplest — matches @mithril-inspector/rollup's pattern):

    if (process.env.NODE_ENV !== "production") {
      import("virtual:mithril-inspector/overlay")
    }
  2. A dedicated entry point plus a manual <script> tag, for zero application-code edits: add a second entryPoints file that only contains import "virtual:mithril-inspector/overlay", build it to its own output (e.g. dist/mithril-inspector-overlay.js), and add <script type="module" src="/mithril-inspector-overlay.js"></script> to your HTML manually — omit that entry point (or the <script> tag) from production builds to keep them inspector-free.

Options

Same shape as @mithril-inspector/vite plus the esbuild-specific devServer option above — see that package's README for the full option reference (include/exclude, root/projectRoots, editor, pathMappings, mode, ui, picker, componentTree, source, mithrilImports/hyperscriptIdentifiers, debug, redact).